月度存档: 8月 2016

c/c++ 与 lua之前互调的性能测试

完整测试代码如下:

测试目标主要是看看c与lua之前互调的性能如何。

测试结论:c调用lua与lua调用c基本只有一个数量级的性能损失。

从c中调用lua,使用缓存ref的方式,可以提升30%的性能。

#include <lua.h>
#include <lauxlib.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/time.h>
#include "skynet.h"

// 取当前毫秒值
static int
ltime(lua_State *L) {
	struct timeval tv;
	gettimeofday(&tv,NULL);
	lua_pushinteger(L,tv.tv_sec*1000+tv.tv_usec/1000);
	return 1;
}

// 供lua调用,内部无业务逻辑
static int
ltest1(lua_State *L) {
        return 0;
}

// 供lua调用, 内部int相加并返回
static int
ltest2(lua_State *L) {
	int a1=luaL_checkinteger(L,1);
	int a2=luaL_checkinteger(L,2);
	lua_pushinteger(L,a1+a2);
	return 1;
}
// 内部调用lua中方法
static int
ltest3(lua_State *L) {
	lua_getglobal(L,"global_test_func1");
	lua_call(L,0,0);
	return 0;
}
// 内部调用lua中方法,并取得lua中返回值
static int
ltest4(lua_State *L) {
	lua_getglobal(L,"global_test_func2");
	lua_pushinteger(L,1);
	lua_pushinteger(L,2);
	lua_call(L,2,1);
	int sum = (int)lua_tointeger(L, -1); 
	lua_pop(L, 1); 
	lua_pushinteger(L,sum);
	return 1;
}
// 内部调用lua方法10000次
static int
ltest5(lua_State *L) {
	int sum=0;	

	for(int m=0;m<10000000;m++){
        	lua_getglobal(L,"global_test_func2");
        	lua_pushinteger(L,1);
        	lua_pushinteger(L,2);
        	lua_call(L,2,1);
        	sum=(int)lua_tointeger(L, -1);
		lua_pop(L, 1); 
	}

        lua_pushinteger(L,sum);
        return 1;
}
// 和test5一起对比
static int
ltest6(lua_State *L) {
        for(int m=0;m<10000000;m++){
                lua_getglobal(L,"global_test_func2");
                lua_pushinteger(L,1);
                lua_pushinteger(L,2);
                lua_call(L,2,1);
                lua_pop(L, 1);
        }
        lua_pushinteger(L,3);
        return 1;
}
// 和test5一起做对比
static int
ltest7(lua_State *L) {
	int func2_handler;
	lua_getglobal(L,"global_test_func2");
	func2_handler=luaL_ref(L,LUA_REGISTRYINDEX);

	for(int m=0;m<10000000;m++){
		lua_rawgeti(L,LUA_REGISTRYINDEX,func2_handler);
		lua_pushinteger(L,1);
		lua_pushinteger(L,2);
		lua_call(L,2,1);
		lua_pop(L,1);
	}
	
	//luaL_unref(L,LUA_REGISTRYINDEX);
	lua_pushinteger(L,3);
	return 1;
}

// 和test5一起做对比一下
int add(int a,int b){
	return a+b;
}
static int
ltest8(lua_State *L) {
	for(int m=0;m<10000000;m++){
		add(1,2);
	}
	return 0;
}


// full userdata

// light userdata

int
luaopen_perf(lua_State *L) {
        luaL_checkversion(L);
        luaL_Reg l[] = {
		{ "time",  ltime  },
                { "test1", ltest1 }, // 从lua中直接调用c,内部无逻辑
                { "test2", ltest2 },
		{ "test3", ltest3 },
		{ "test4", ltest4 },
		{ "test5", ltest5 },
		{ "test6", ltest6 },
		{ "test7", ltest7 },
		{ "test8", ltest8 },
                { NULL, NULL}
        };
        luaL_newlib(L,l);

        return 1;
}

lua代码:

require "common"

local perf = require "perf"


function global_test_func1()
--	print("global_test_func1")
end
function global_test_func2(a,b)
	return a+b
end
local t1
local t2

t1=perf.time()
for i=1,10000000 do
	perf.test1()
end
t2=perf.time()
print("test1:"..(t2-t1))

t1=perf.time()
for i=1,10000000 do
	perf.test2(1,2)
end
t2=perf.time()
print("test2:"..(t2-t1))

t1=perf.time()
for i=1,10000000 do
	perf.test3()
end
t2=perf.time()
print("test3:"..(t2-t1))

t1=perf.time()
for i=1,10000000 do
	perf.test4()
end
t2=perf.time()
print("test4:"..(t2-t1))

t1=perf.time()
perf.test5()
t2=perf.time()
print("test5:"..(t2-t1))

t1=perf.time()
perf.test6()
t2=perf.time()
print("test6:"..(t2-t1))

t1=perf.time()
perf.test7()
t2=perf.time()
print("test7:"..(t2-t1))

t1=perf.time()
perf.test8()
t2=perf.time()
print("test8:"..(t2-t1))

运行结果:

test1:470
test2:777
test3:1619
test4:1941
test5:1490
test6:1437
test7:675
test8:40

jni错误 UnsatisfiedLinkError

很久没写过jni了。这次项目中用到,准备使用vs2015生成dll,供java调用。我用premake5生成了vs2015项目,但生成出来的dll,无法在java中调用,报以下错误:

Exception in thread "main" java.lang.UnsatisfiedLinkError: RecastLib.hello()V
    at RecastLib.hello(Native Method)
    at RecastLib.main(RecastLib.java:22)

使用vs2015中的工具 dumpbin 查看dll中的方法:

          1    0 00001200 ?Java_RecastLib_hello@@YAXPEAUJNIEnv_@@PEAV_jobject@@@
Z = ?Java_RecastLib_hello@@YAXPEAUJNIEnv_@@PEAV_jobject@@@Z (void __cdecl Java_R
ecastLib_hello(struct JNIEnv_ *,class _jobject *))
          2    1 00001220 ?_Java_RecastLib_hello@@YAXPEAUJNIEnv_@@PEAV_jobject@@
@Z = ?_Java_RecastLib_hello@@YAXPEAUJNIEnv_@@PEAV_jobject@@@Z (void __cdecl _Jav
a_RecastLib_hello(struct JNIEnv_ *,class _jobject *))

发现 vs2015 编译的方法名全部被添加上了@后缀。

正确的应该是:

    ordinal hint RVA      name

          1    0 00011091 Java_RecastLib_hello = @ILT+140(Java_RecastLib_hello)

这是因为 vc++中的 Name Mangling:

	JNIEXPORT void JNICALL Java_RecastLib_hello
		(JNIEnv *, jobject);

如果要导出的cpp中使用了c++特性,即使指定为 extern “C” 也受Name Mangling的影响。

这里可以定义一个  java.def 文件也解决。定义如下:

LIBRARY recastDll
EXPORTS
  Java_RecastLib_hello

然后在vs2015的链接器中定义:

这样导出的函数即不会受name mangling 的影响。

除此之外,还可以使用  RegisterNatives 的方式将函数入口注入java vm(两种试并无性能差异)。

如下:

static JNINativeMethod s_methods[] = {
	{ "jniHello", "()V", (void*)Java_RecastLib_hello },
	{ "jniLoad",  "(Ljava/lang/String;)I", (void*)Java_RecastLib_load },
	{ "jniFind",  "(FFFFFF)Ljava/util/List;", (void*)Java_RecastLib_find }
};
JNIEXPORT jint JNICALL
JNI_OnLoad(JavaVM *vm, void *reserved)
{
	JNIEnv* env = NULL;
	if (vm->GetEnv((void**)&env, JNI_VERSION_1_8) != JNI_OK)
	{
		return JNI_ERR;
	}
	jclass cls = env->FindClass("LRecastLib;");
	if (cls == NULL)
	{
		return JNI_ERR;
	}
	int len = sizeof(s_methods) / sizeof(s_methods[0]);
	if (env->RegisterNatives(cls, s_methods, len) < 0)
	{
		return JNI_ERR;
	}
	return JNI_VERSION_1_8;
}

一个hashmap算法

阅读云风开源的AOI实现时,看到里面有一个hashmap算法实现。

https://github.com/cloudwu/aoi

这里记录下分享下其实现

map内部存储是使用数组实现即  map_slot,初始分配16个。容量不足时,以翻倍容量增加,并将对象重新进行分配至新的内存空间。

这里画图举例时以初始容量为8进行原理说明:

1、初始状态,所有slot处于未分配

2、插入A,使用hash算法计算出应该分配在哪个slot上(注:可以考虑添加扰动函数避免比较差的hash算法,这里针对aoi的情况,直接使用了对size取模)

假设对A取模后将其放入第三个slot

3、继续插入B和C,其中B和A取模后 不在相同的slot中。

然后是取模后分配到相同的slot的情况:

实际上两个对象不会存在相同的slot中。

这里如果出现分配的slot中已经存在对象了,比如此时,C分配的第三个slot已经存在A了。

则从数组最尾部向前查找第一个为空的slot,将C放入,并将A.next指向C所在的slot

 

4、继续插入一个和A相同slot的对象时,继续从尾部向前查找一个空的slot,并将A.next指向新分配的slot,新分配的slot.next指向原A.next ( 即C)

5、如果遇到分配的slot被其它slot的对象占用如何处理。

比如,我这里想分配E,取模后应该分配在第七个格子,但第七个格子已经被D占用。

这里采用的方式,是将D取出( 因为D取模不应该分配在第七个格子,只是因为第三个格子被占用了,所以才从尾部查找的一个空闲的格子放D),取出D后将E放后,如下图:

 

这里D被取出应该如何处理,再执行一个第1步,将D放hashmap即可。

 


Warning: Use of undefined constant XML - assumed 'XML' (this will throw an Error in a future version of PHP) in /opt/wordpress/wp-content/plugins/wp-syntaxhighlighter/wp-syntaxhighlighter.php on line 1048