为 cocos2d-x 选择新的 Luabinding (1)

最近正在做 Cocos Creator 的 Lua 支持。因为 Creator 使用了一个精简的 cocos2d-x 引擎,所以我也基于这个引擎来做 Cocos Creator 的 Lua 支持

cocos2d-x 目前使用一套基于 tolua++ 的 Luabinding 层。而 tolua++ 这货已经停止维护快十年了,所以换掉 tolua++ 势在必行。另外我一直认为 tolua++ 存在性能问题,替换为更高效的 luabinding 可以充分发挥 Lua 的性能优势。

这篇文章的重点就是选择新的 Luabinding。


性能测试

既然以提高性能为目的,必须用数据来说话。所以我做了一个性能测试工程,放在 github 的 cocos2dx_benchmark 仓库里。

在 iPhone 6 上,这个 Lua 测试例可以在保证 55fps+ 帧率的基础上跑 4500 个星星。

为了看到 Lua 和 C++ 的性能差距到底有多大,我又在 quick2d-engine 仓库中做了一个新的测试例。

C++ 版的测试结果是可以跑到 12000 个星星。可以看出 cocos2d-x Lua 的综合性能只有 C++ 的 37.5%


选择新 Luabinding

找了好些 Luabinding 库,根据这个性能测试选择了 Sol2 。但经过一天多的试验,这个库缺少一个及其重要的特性 concatenate the metatables together。而且作者刚刚发布 2.5 版后就说他不玩了。。因为太累 -_-#

好吧,我换成第二快的 kaguya

这个库采用大量 C++11 的新特性,采用模板的形式来导出 C++ 接口,代码如下:

auto cc = _lua["cc"] = _lua.newTable();

cc["Ref"].setClass(kaguya::ClassMetatable<Ref>()
    .addMemberFunction("retain",&Ref::retain)
    .addMemberFunction("release",&Ref::release)
    .addMemberFunction("getReferenceCount",&Ref::getReferenceCount));

cc["Scheduler"].setClass(kaguya::ClassMetatable<Scheduler,Ref>());

cc["Director"].setClass(kaguya::ClassMetatable<Director,Ref>()
    .addStaticFunction("getInstance",&Director::getInstance)
    .addMemberFunction("endDirector",&Director::end)
    .addMemberFunction("getScheduler",&Director::getScheduler));

cc["Node"].setClass(kaguya::ClassMetatable<Node,Ref>()
    .addStaticFunction("create",&Node::create)
    .addMemberFunction("addChild",static_cast<void(Node::*)(Node*)>(&Node::addChild))
    .addMemberFunction("addChild",static_cast<void(Node::*)(Node*,int)>(&Node::addChild))
    .addMemberFunction("addChild",int,const std::string&)>(&Node::addChild))
    .addMemberFunction("removeChild",&Node::removeChild)
    .addMemberFunction("setPosition",static_cast<void(Node::*)(const Vec2&)>(&Node::setPosition))
    .addMemberFunction("setPosition",static_cast<void(Node::*)(float,float)>(&Node::setPosition))
    .addMemberFunction("setColor",&Node::setColor)
    .addMemberFunction("setOpacity",&Node::setOpacity)
    .addMemberFunction("schedule",static_cast<void(Node::*)(const std::function<void(float)>&,float,const std::string &)>(&Node::schedule)));

代码看上去是相当整洁的。

但经过性能测试,却发现在超过 3000 个星星后,性能会出现暴跌,而不是线性降低。

又反复测试了一下,估计问题应该出在 Lua call C++ member function 这里。所以又写了一段代码

static int lgetNode(lua_State *L)
{
    // node
    kaguya::ObjectPointerWrapper<Sprite*> *wrap = static_cast<kaguya::ObjectPointerWrapper<Sprite*>*>(lua_touserdata(L,-1));
    Node *node = static_cast<Node*>(wrap->get());
    lua_pushlightuserdata(L,node);
    return 1;
}

static int lsetPosition(lua_State *L)
{
    // node,x,y
    Node *node = static_cast<Node*>(lua_touserdata(L,-3));
    node->setPosition(lua_tonumber(L,-2),lua_tonumber(L,-1));
    return 0;
}

static int lsetOpacity(lua_State *L)
{
    // node,opacity
    Node *node = static_cast<Node*>(lua_touserdata(L,-2));
    node->setOpacity(lua_tointeger(L,-1));
    return 0;
}

lua_State *L = _lua.state();

lua_pushcfunction(L,&lgetNode);
lua_setglobal(L,"lgetNode");

lua_pushcfunction(L,&lsetPosition);
lua_setglobal(L,"lsetPosition");

lua_pushcfunction(L,&lsetOpacity);
lua_setglobal(L,"lsetOpacity");

在 Lua 里,就不再使用对象方法调用了,而是改成这三个全局函数。测试结果暴涨到 8200 个星星,接近 C++ 70% 的性能了。


总结

cocos2d-x 现在使用的 Luabinding 只能达到 C++ 37.5% 的性能。在更换新 Luabinding 并做相应调整后,可以获得高达 C++ 70% 的性能表现。而这损耗的 30%,其中还有大量的数值运算。要知道拼计算,脚本是肯定比不过 C++ 的。

虽然新的 Luabinding 方案还有很多工作要做,但我有信心最终搞一个比现在快一倍的 Luabinding 出来。

相关文章

操作步骤 1、创建cocos2d-x工程 2、新建 Scene1.cpp Scene1.h Scene1.h代码 #ifndef __SCENE1_H__#defi...
开发环境:OS(WINDOWS 8.1 X64 企业版) cocos2d-x 2.2.1 vs2010 想给vs安装上cocos的模版,执行Install...
把创建项目做成一个批处理,当创建项目时可以省时省力很多。 操作步骤 1、在 E:cocos2d-x-2.2.1toolspr...
https://www.cnblogs.com/JiaoQing/p/3906780.html 四个响应函数 1 EventListenerPhysicsContact* evC...
转载于 http://www.cnblogs.com/kenkofox/p/3926797.html 熟悉js的dom事件或者flash事件的,基本都能立...
ScrollView(滚动容器)加载大量item时会导致游戏界面的卡顿,严重时整个界面会出现卡死的情况。最近项...