在 OpenResty 中正确地测量 Lua 代码的执行时间
本教程演示了在OpenResty中对用户Lua代码进行基准测试的正确方法和错误方法。
1 | cd ~ |
首先,要保证我们的CPU始终处于全速运转状态。
1 | echo performance | sudo tee /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor |
它通常默认取值powersave
而我们需要设置成 performance
。
对一些Lua代码进行计时的最简单方法是使用time
命令和resty
命令。
1 | time resty -e 'ngx.re.find("hello, world.", [[\w+\.]], "jo")' |
但这种方法有一个问题。resty
命令本身有一个启动和退出的开销。
1 | time resty -e '' |
我们可以看到在这台机器上的开销大约有11毫秒。
相反,我们应该使用OpenResty提供的ngx.now
Lua API函数。
1 | restydoc -s ngx.now |
让我们把我们的Lua代码放到一个名为./bench.lua
的文件中,以便更好地阅读。
我们进行以下编辑:
1.首先,我们要确保nginx里面的缓存时间是最新的。 2.然后我们记录的开始时间有毫秒的精度。 3.然后把我们前面提到的正则匹配调用。 4.然后我们再更新缓存时间。 5.最后,通过做时间减法输出经过的时间。 6.让我们保存文件。
1 | ngx.update_time() |
然后运行resty
这个shell命令。
1 | resty bench.lua |
它记录的延迟时间大约是1毫秒。但我们很快就会发现它很不准确。
正确的方法是在bench.lua
文件中做如下编辑:
1.把调用放到一个名为target
的Lua函数中。
2.然后先调用这个函数100次作为热身。现在这个target
函数应该在这个循环执行完后进行JIT编译。
3.然后在定时码区域里面,我们反复调用1000万次。
4.最后我们计算平均时间。
1 | local function target() |
我们现在再次运行这个脚本。
1 | resty bench.lua |
我们可以看到,每次调用仅仅是30纳秒左右。比之前的结果快了很多很多倍!
实际上,我们可以进一步确保在我们给代码计时之前,没有死掉的GC对象还没有被回收,只要在第一个ngx.update_time()
调用之前插入以下一行代码。只需在第一个ngx.update_time()
调用之前插入以下一行代码。
1 | collectgarbage() |
在这里,我们强制执行一个完整的GC周期,然后再记录开始时间。
但是,它对我们这里的例子没有什么帮助。这是因为我们的定时代码无论如何也不会创建很多GC对象。
我们可以通过避免不必要的Lua表查找操作,使target
函数更快。
1 | local re_find = ngx.re.find |
但这里的差别可能比较小。
这就是我今天要讲的内容。希望你觉得有趣。
如果你喜欢这个视频,请订阅我们的 YouTube 频道 或 B 站频道。谢谢!
关于本文和关联视频
本文和相关联的视频都是完全由我们的 OpenResty Demo 系统从一个极简单的剧本文件自动生成的。
关于作者
章亦春是开源项目 OpenResty® 的创始人,同时也是 OpenResty Inc. 公司的创始人和 CEO。他贡献了许多 Nginx 的第三方模块,相当多 Nginx 和 LuaJIT 核心补丁,并且设计了 OpenResty XRay 等产品。
关注我们
如果您觉得本文有价值,非常欢迎关注我们 OpenResty Inc. 公司的博客网站 。也欢迎扫码关注我们的微信公众号:
翻译
我们提供了英文版原文和中译版(本文) 。我们也欢迎读者提供其他语言的翻译版本,只要是全文翻译不带省略,我们都将会考虑采用,非常感谢!