测试环境
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倍左右。