測試環境
i5-3470 @ 3.20G 3.70G,Windows,lua5.3,程序運行在單核單線程上。
Lua腳本
function lua_callback()
end
function reg()
set_callback(lua_callback)
end
lua_callback是主要測試對象,是個空函數,reg是Lua向C註冊回調。
C測試代碼
#include "stdio.h"
#include <string>
#include <windows.h>
extern "C" {
#include <lua.h>
#include <lualib.h>
#include <lauxlib.h>
}
lua_State *L_ = NULL;
int lua_callback_ = LUA_REFNIL;
LONGLONG get_tickcount() {
LARGE_INTEGER freq,counter;
QueryPerformanceFrequency(&freq);
QueryPerformanceCounter(&counter);
return (1000000L * counter.QuadPart / freq.QuadPart)/1000L;
}
std::string load_test_lua() {
std::string lua_buff;
FILE *fp = fopen("test.lua","rb");
if (fp == NULL) {
return "";
}
fseek(fp, 0, SEEK_END);
int size = ftell(fp);
fseek(fp, 0, SEEK_SET);
lua_buff.resize(size);
fread((void*)lua_buff.data(), size, 1, fp);
fclose(fp);
return lua_buff;
}
int set_callback(lua_State *L) {
int ret = -1;
do {
int callback = (int)luaL_ref(L, LUA_REGISTRYINDEX); //將棧頂回調放入lua註冊表獲並取引用
if (callback == LUA_REFNIL) {
break;
}
lua_callback_ = callback; //保存回調函數的引用
ret = 0;
} while (0);
return ret;
}
void register_function(lua_State *L) {
lua_register(L, "set_callback", set_callback); //註冊函數,lua->C
}
int reg(lua_State *L) {
lua_State *co = lua_newthread(L);
lua_getglobal(co, "reg");
int rs = lua_resume(co, L, 0);
return rs;
}
int call1(lua_State *L) {
lua_State *co = lua_newthread(L);
lua_getglobal(co, "lua_callback");
int rs = lua_resume(co, L, 0);
lua_pop(L,1); //co留在主線程的棧上,必須pop,否則棧會溢出
return rs;
}
int call2(lua_State *L) {
lua_State *co = lua_newthread(L);
lua_rawgeti(co, LUA_REGISTRYINDEX, lua_callback_);
int rs = lua_resume(co, L, 0);
lua_pop(L,1); //co留在主線程的棧上,必須pop,否則棧會溢出
return rs;
}
int call3(lua_State *L) {
lua_rawgeti(L, LUA_REGISTRYINDEX, lua_callback_);
int rs = lua_pcall(L, 0, 0, 0);
return rs;
}
int call4(lua_State *L) {
lua_getglobal(L, "lua_callback");
int rs = lua_pcall(L, 0, 0, 0);
return rs;
}
LONGLONG test_call(int type, int count, lua_State *L) {
LONGLONG s = get_tickcount();
switch (type) {
case 1: {
for (int i = 0; i < count;i++) {
call1(L);
}
break;
}
case 2: {
for (int i = 0; i < count;i++) {
call2(L);
}
break;
}
case 3: {
for (int i = 0; i < count;i++) {
call3(L);
}
break;
}
case 4: {
for (int i = 0; i < count;i++) {
call4(L);
}
break;
}
}
LONGLONG e = get_tickcount();
return e - s;
}
int main()
{
L_ = luaL_newstate();
luaL_openlibs(L_);
register_function(L_);
std::string lua_buffer = load_test_lua();
int rs = (luaL_loadbuffer(L_, lua_buffer.c_str(), lua_buffer.length(), "test") || lua_pcall(L_, 0, LUA_MULTRET, 0));
if (rs != LUA_OK) {
return 0;
}
reg(L_);
int count = 1000000;
LONGLONG s1 = test_call(1, count, L_);
LONGLONG s2 = test_call(2, count, L_);
LONGLONG s3 = test_call(3, count, L_);
LONGLONG s4 = test_call(4, count, L_);
printf("coroutine+no register callback=%I64dms\n",s1);
printf("coroutine+register callback=%I64dms\n",s2);
printf("no coroutine+register callback=%I64dms\n",s3);
printf("no coroutine+no register callback=%I64dms\n",s4);
getchar();
return 0;
}
測試結果
測試1000000次
coroutine+no register callback=21745ms //啓動協程,直接通過lua函數名調用,每個調用需要0.02ms左右
coroutine+register callback=21519ms //啓動協程,通過lua註冊表中的回調調用
no coroutine+register callback=567ms //不啓動協程,在主線程通過lua註冊表中的回調調用,每個調用需要0.6us左右
no coroutine+no register callback=674ms //不啓動協程,在主線程通過lua函數名直接調用
結論
可以看出通過lua函數名調用和通過註冊表註冊lua回調的性能差距不大,每個調用大概差距在0.1us左右。
但是通過協程調用與不使用協程調用性能差距達到35倍左右。