C調用LUA各種方式的性能對比

測試環境

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倍左右。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章