最近在分析untiy客户端lua的性能时发现, 真机上有的lua重复加载了2次策划数值表。
打印了一下当时package.loaded (按理require了之后这里会存一下,后面require就不会重复加载了),
发现了存在 data/xxxx data.xxxx 两个路径的。
那是项目里 require "data/xxxx" require "data.xxxx" 两种方式混用造成的。
在Unity编辑器里跑没问题,package.loaded里打印出来都是点.连接的。 但真机上却重复了。
再仔细分析源码问题,发现真机为了编译lua用了luajit (lib_package.c 有个版本 require实现没有 /替换成 .), unity上用的是tolua编出来的dll(macnojit 的 loadlib.c 里 require实现做了/换成.)
简单解决方案,在第一个加载.lua文件开头把require函数重写一下:
-- 保存原始的 require 函数
local original_require = require
-- 新的 require 函数,重写路径处理
function require(modname) -- 将路径中的斜杠替换为点 local modified_modname = modname:gsub("/", ".")-- 调用原始的 require 函数加载模块 return original_require(modified_modname)
end
就能避免这个重复加载了的问题。
真机上跑了一下,运行内存竟然少了近几十M(项目里数值表占用太多吧= =), 加载卡顿也少了一些。
放一下lua源码里的 require实现:
https://www.lua.org/source/5.4/loadlib.c.html
static int ll_require (lua_State *L) {const char *name = luaL_checkstring(L, 1);lua_settop(L, 1); /* LOADED table will be at index 2 */lua_getfield(L, LUA_REGISTRYINDEX, LUA_LOADED_TABLE);lua_getfield(L, 2, name); /* LOADED[name] */if (lua_toboolean(L, -1)) /* is it there? */return 1; /* package is already loaded *//* else must load package */lua_pop(L, 1); /* remove 'getfield' result */findloader(L, name);lua_rotate(L, -2, 1); /* function <-> loader data */lua_pushvalue(L, 1); /* name is 1st argument to module loader */lua_pushvalue(L, -3); /* loader data is 2nd argument *//* stack: ...; loader data; loader function; mod. name; loader data */lua_call(L, 2, 1); /* run loader to load module *//* stack: ...; loader data; result from loader */if (!lua_isnil(L, -1)) /* non-nil return? */lua_setfield(L, 2, name); /* LOADED[name] = returned value */elselua_pop(L, 1); /* pop nil */if (lua_getfield(L, 2, name) == LUA_TNIL) { /* module set no value? */lua_pushboolean(L, 1); /* use true as result */lua_copy(L, -1, -2); /* replace loader result */lua_setfield(L, 2, name); /* LOADED[name] = true */}lua_rotate(L, -2, 1); /* loader data <-> module result */return 2; /* return module result and loader data */
}
用lua翻译就是:
--require 函数的实现
function require(name)if not package.loaded[name] thenlocal loader = findloader(name) //这一步演示在代码中以抽象函数findloader来表示if loader == nil thenerror("unable to load module" .. name)endpackage.loaded[name] = truelocal res = loader(name)if res ~= nil thenpackage.loaded[name] = resendendreturn package.loaded[name]
end
from Lua-require_lua require原理-CSDN博客
ToLua\macnojit\lua\loadlib.c里的实现 luaL_gsub(L, name, "/", ".");做了替换
static int ll_require (lua_State *L) {const char *name = luaL_checkstring(L, 1);int i;lua_settop(L, 1); /* _LOADED table will be at index 2 */const char* key = luaL_gsub(L, name, "/", ".");lua_getfield(L, LUA_REGISTRYINDEX, "_LOADED");lua_getfield(L, 3, key);if (lua_toboolean(L, -1)) { /* is it there? */if (lua_touserdata(L, -1) == sentinel) /* check loops */luaL_error(L, "loop or previous error loading module " LUA_QS, name);return 1; /* package is already loaded */}/* else must load it; iterate over available loaders */lua_getfield(L, LUA_ENVIRONINDEX, "loaders");if (!lua_istable(L, -1))luaL_error(L, LUA_QL("package.loaders") " must be a table");lua_pushliteral(L, ""); /* error message accumulator */for (i=1; ; i++) {lua_rawgeti(L, -2, i); /* get a loader */if (lua_isnil(L, -1))luaL_error(L, "module " LUA_QS " not found:%s",name, lua_tostring(L, -2));lua_pushstring(L, name);lua_call(L, 1, 1); /* call it */if (lua_isfunction(L, -1)) /* did it find module? */break; /* module loaded successfully */else if (lua_isstring(L, -1)) /* loader returned error message? */lua_concat(L, 2); /* accumulate it */elselua_pop(L, 1);}lua_pushlightuserdata(L, sentinel);lua_setfield(L, 3, key); /* _LOADED[name] = sentinel */lua_pushstring(L, name); /* pass name as argument to module */lua_call(L, 1, 1); /* run loaded module */if (!lua_isnil(L, -1)) /* non-nil return? */lua_setfield(L, 3, key); /* _LOADED[name] = returned value */lua_getfield(L, 3, key);if (lua_touserdata(L, -1) == sentinel) { /* module did not set a value? */lua_pushboolean(L, 1); /* use true as result */lua_pushvalue(L, -1); /* extra copy to be returned */lua_setfield(L, 3, key); /* _LOADED[name] = true */}return 1;
}
https://github.com/LuaJIT/LuaJIT/blob/v2.1/src/lib_package.c 里没有实现替换,这边项目里本来改过的,后来升级一次不小心又改掉这个修改了:
#define KEY_SENTINEL (U64x(80000000,00000000)|'s')static int lj_cf_package_require(lua_State *L)
{const char *name = luaL_checkstring(L, 1);int i;lua_settop(L, 1); /* _LOADED table will be at index 2 */lua_getfield(L, LUA_REGISTRYINDEX, "_LOADED");lua_getfield(L, 2, name);if (lua_toboolean(L, -1)) { /* is it there? */if ((L->top-1)->u64 == KEY_SENTINEL) /* check loops */luaL_error(L, "loop or previous error loading module " LUA_QS, name);return 1; /* package is already loaded */}/* else must load it; iterate over available loaders */lua_getfield(L, LUA_ENVIRONINDEX, "loaders");if (!lua_istable(L, -1))luaL_error(L, LUA_QL("package.loaders") " must be a table");lua_pushliteral(L, ""); /* error message accumulator */for (i = 1; ; i++) {lua_rawgeti(L, -2, i); /* get a loader */if (lua_isnil(L, -1))luaL_error(L, "module " LUA_QS " not found:%s",name, lua_tostring(L, -2));lua_pushstring(L, name);lua_call(L, 1, 1); /* call it */if (lua_isfunction(L, -1)) /* did it find module? */break; /* module loaded successfully */else if (lua_isstring(L, -1)) /* loader returned error message? */lua_concat(L, 2); /* accumulate it */elselua_pop(L, 1);}(L->top++)->u64 = KEY_SENTINEL;lua_setfield(L, 2, name); /* _LOADED[name] = sentinel */lua_pushstring(L, name); /* pass name as argument to module */lua_call(L, 1, 1); /* run loaded module */if (!lua_isnil(L, -1)) /* non-nil return? */lua_setfield(L, 2, name); /* _LOADED[name] = returned value */lua_getfield(L, 2, name);if ((L->top-1)->u64 == KEY_SENTINEL) { /* module did not set a value? */lua_pushboolean(L, 1); /* use true as result */lua_pushvalue(L, -1); /* extra copy to be returned */lua_setfield(L, 2, name); /* _LOADED[name] = true */}lj_lib_checkfpu(L);return 1;
}