NodeMCU/Lua性能问题



我正在向ws2812模块添加一些代码,以便能够拥有某种可重复使用的缓冲区,在那里我们可以存储led值。

当前版本已存在。

我有两个问题。

首先,我想要一些"OO风格"的接口。所以我做了:

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

这里的问题是,buffer:set在每个环路转弯处都得到解决,这是昂贵的(该环路需要约20.2ms):

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.3ms,速度快4倍多),但更像是一次黑客攻击。:/有没有更好的方法来"缓存"缓冲区:设置分辨率?

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

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

这会返回我的缓冲区ptr,并检查它是否真的是ws2812.buffer。但这个电话太慢了:在我的ESP8266上,大约有50个。如果每次通话都完成(例如,对于我的300次buffer:set),大约需要15毫秒!

有没有更好的方法来获取一些用户数据并检查其类型,或者我应该在结构的开头添加一些"金丝雀"来进行自己的检查(与50us相比,这几乎是"免费的"…)?

为了让它看起来不那么像黑客,你可以尝试使用

local set = buffer.set

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

在我们的项目中,我们自己实现了luaL_checkudata。正如您类似地建议的那样,一种选择是使用一个包含该类型的包装器对象。由于假设所有用户数据都被包装在包装器中,我们可以使用它来获取和确认用户数据的类型。但并没有进行基准测试,而是使用了测试元表。

我想说测试元表比封装慢,因为luaL_checkudata做了很多工作来获取和测试元表,并且通过封装,我们可以直接访问类型。然而,基准测试肯定会说明问题。