最近在分析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 */
- else
- lua_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] then
- local loader = findloader(name) //这一步演示在代码中以抽象函数findloader来表示
- if loader == nil then
- error("unable to load module" .. name)
- end
- package.loaded[name] = true
- local res = loader(name)
- if res ~= nil then
- package.loaded[name] = res
- end
- end
- return 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 */
- else
- lua_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 */
- else
- lua_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;
- }