logo资料库

cocos引擎的Lua效率分析器.docx

第1页 / 共5页
第2页 / 共5页
第3页 / 共5页
第4页 / 共5页
第5页 / 共5页
资料共5页,全文预览结束
基于Cocos引擎的Lua函数效率分析器
基本思路:
实现功能:
代码实现:
样例报表:
基于 Cocos 引擎的 Lua 函数效率分析器 基本思路: Lua 的 lua_hook 功能能在每个函数被调用时和返回时触发一个特定回调,通过计算调用和返 回的时间差即可知道一个函数的耗时 Lua 的 lua_getinfo 函数能取得当前 Lua 的完整调用栈信息 在 ios 和 android 平台可以使用 std::chrono::high_resolution_clock 获得高精度时间,windows 平 台 上 该 接 口 精 度 不 足 , 需 要 更 换 为 QueryPerformanceCounter 和 QueryPerformanceFrequency 两个接口 通过在 hook 函数开头和返回时分别计时来尽量排除计时器本身开销带来的误差 实现功能: 能统计 Lua 虚拟机中每个函数的独占开销占比、总开销占比、调用次数、平均调用时间,最 长调用时间和时间加权的平均调用时间 在调用结束后,生成一个 csv 格式的表格文件,方便进行分析 代码实现: C++部分代码: // lua profiler static inline QWORD profileTimer() { #ifdef _MSC_VER LARGE_INTEGER time; QueryPerformanceCounter(&time); return time.QuadPart; #else return std::chrono::high_resolution_clock::now().time_since_epoch().count(); #endif } static inline QWORD profileTicker() { #ifdef _MSC_VER LARGE_INTEGER time; QueryPerformanceFrequency(&time); return time.QuadPart;
#else return std::chrono::high_resolution_clock::duration(std::chrono::seconds(1)).count(); #endif } struct ProfileStack { }; std::string name; QWORD QWORD QWORD startTime; startOffset; startCost; struct ProfileStatistics { }; std::string name; QWORD QWORD QWORD QWORD selfCost = 0; totalCost = 0; maxCost = 0; callCount = 0; double sqrSum = 0; static std::vector profileStack; static QWORD profileOffset = 0; static QWORD profileCost = 0; static std::unordered_map profileStatistics; static void profileHook(lua_State* L, lua_Debug* d) { auto p1 = profileTimer(); if (d->event) // return { if (profileStack.empty()) return; auto& ci = profileStack.back(); auto offset = profileOffset - ci.startOffset; auto cost = p1 - ci.startTime - offset; auto selfCost = cost - (profileCost - ci.startCost); profileCost += selfCost; auto& s = profileStatistics[ci.name]; s.selfCost += selfCost; s.totalCost += cost; s.callCount += 1; s.sqrSum += (double)cost * (double)cost;
s.maxCost = std::max(s.maxCost, cost); profileStack.pop_back(); auto p2 = profileTimer(); profileOffset += p2 - p1; } else // call { profileStack.push_back(ProfileStack()); auto& s = profileStack.back(); s.startCost = profileCost; { } lua_getinfo(L, "Sn", d); std::ostringstream oss; oss << d->short_src; if (d->linedefined > 0) oss << "(" << d->linedefined << ")"; if (d->name) oss << ":[" << d->namewhat << "]" << d->name; s.name = oss.str(); auto p2 = profileTimer(); auto offset = p2 - p1; profileOffset += offset; s.startTime = p2; s.startOffset = profileOffset; } } static void dumpProfile() { std::vector list; for (auto& i : profileStatistics) { } list.push_back(i.second); list.back().name = i.first; auto fileUtils = cocos2d::FileUtils::getInstance(); std::ofstream fout(fileUtils->getSuitableFOpen(fileUtils->getWritablePath() + "profile.csv")); auto ticker = profileTicker() / 1000.0; fout << "function name,total cost,self cost,call count,avg cost,max cost,avg^2 cost" << std::endl; for (auto& i : list) { fout << i.name << ",";
fout << i.totalCost * 100.0 / profileCost << "%,"; fout << i.selfCost * 100.0 / profileCost << "%,"; fout << i.callCount << ","; fout << (double)i.totalCost / i.callCount / ticker << ","; fout << i.maxCost / ticker << ","; fout << i.sqrSum / i.totalCost / ticker << std::endl; } } static void beginProfile() { } sealp::callLuaFunction("showNotify", "beginProfile"); profileOffset = 0; profileCost = 0; profileStack.clear(); profileStatistics.clear(); auto L = cocos2d::LuaEngine::getInstance()->getLuaStack()->getLuaState(); lua_sethook(L, profileHook, LUA_MASKCALL | LUA_MASKRET, 0); void SealUtilToLua::beginProfile() { } auto L = cocos2d::LuaEngine::getInstance()->getLuaStack()->getLuaState(); lua_Debug ar; if (lua_getstack(L, 0, &ar)) { } auto scheduler = cocos2d::Director::getInstance()->getScheduler(); scheduler->performFunctionInCocosThread([]() { ::beginProfile(); }); else ::beginProfile(); static void endProfile() { } auto L = cocos2d::LuaEngine::getInstance()->getLuaStack()->getLuaState(); lua_sethook(L, profileHook, 0, 0); dumpProfile(); sealp::callLuaFunction("showNotify", "endProfile"); void SealUtilToLua::endProfile() { auto L = cocos2d::LuaEngine::getInstance()->getLuaStack()->getLuaState(); lua_Debug ar;
if (lua_getstack(L, 0, &ar)) { } auto scheduler = cocos2d::Director::getInstance()->getScheduler(); scheduler->performFunctionInCocosThread([]() { ::endProfile(); }); else ::endProfile(); } 将 SealUtilToLua::beginProfile()与 SealUtilToLua::beginProfile()两个接口导出到 Lua,在 Lua 中通过控制台指令调用即可 样例报表: 见 profile.csv,配合 excel 的筛选和排序功能能非常方便的定位开销过高的函数,通过数值间 的关联性还能分析出函数是否被重复调用等其他问题
分享到:
收藏