NodeMCU/Lua 性能问题

我正在为 ws2812 模块添加一些代码,以便能够具有某种可重用的缓冲区,其中我们可以存储 led 值。

当前版本在这里

我的问题有两个。

首先,我希望具有某些“面向对象”的接口。所以我这样做了:

local buffer = ws2812.newBuffer(300);
for j = 0,299 do
   buffer:set(j, 255, 255, 255)
end
buffer:write(pin);

这里的问题是 buffer:set 在每个循环周期内都会被解析,这很耗费资源(这个循环需要 ~20.2 毫秒):

8       [2]     FORPREP         1 6     ; to 15
9       [3]     SELF            5 0 -7  ; “set”
10      [3]     MOVE            7 4
11      [3]     LOADK           8 -8    ; 255
12      [3]     LOADK           9 -8    ; 255
13      [3]     LOADK           10 -8   ; 255
14      [3]     CALL            5 6 1
15      [2]     FORLOOP         1 -7    ; to 9

我找到了一个解决这个问题的变通方法,但看起来不是很“好看”:

local buffer = ws2812.newBuffer(300);
local set = getmetatable(buffer).set;
for j = 0,299 do
   set(buffer, j, 255, 255, 255)
end
buffer:write(pin);

它可以正常工作(循环需要 4.3 毫秒,比原来快了 4 倍以上),但更像是一种 hack。 :/ 是否有更好的方式来“缓存” buffer:set 解析?

第二个问题,在我的 C 代码中,我使用:

ws2812_buffer * buffer = (ws2812_buffer*)luaL_checkudata(L, 1, “ws2812.buffer”);

这将返回我的缓冲区 ptr 并检查它是否真的是 ws2812.buffer。但这个调用太慢了,在我的 ESP8266 上,大约需要 50 毫微秒。如果对于我的 300 次 buffer:set 调用都这样做,那就是大约 15 毫秒!

是否有更好的方法来获取一些用户数据并检查其类型,或者我应该在我的结构开头加入一些“标志”来进行自己的检查(与 50us 相比是几乎“免费”的...)?

点赞
用户3586583
用户3586583

为了让它看起来不太像 hack,你可以尝试使用

local set = buffer.set

这本质上是相同的代码,但是没有使用 getmetatable,因为元表是通过 __index 元方法隐式使用的。

在我们的项目中,我们制作了我们自己的 luaL_checkudata 实现。一个选择,就像你提出的那样,是使用一个持有类型的包装对象。由于所有的 userdata 都被假定为包装在包装器中,我们可以使用它来获取和确认 userdata 的类型。但是没有进行基准测试,测试元表取代了这个方法。

我认为测试元表比包装更慢,因为 luaL_checkudata 做了很多工作来获取和测试元表,而通过包装我们直接访问了类型。然而,基准测试会告诉我们确定的结果。

2015-12-26 15:20:23