【lua】tolua(Luajit) require 重复加载了的问题(路径混用斜杠/和点.造成的问题)

文章讲述了在Unity客户端使用lua时,由于require路径问题导致的资源重复加载,进而影响性能。通过分析lua源码和luajit/tolua的require实现,提出了一种解决方案,即重写require函数以统一路径处理,从而避免了资源的无谓加载,显著提高了内存使用和运行效率。
摘要由CSDN通过智能技术生成

最近在分析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;
}


 

  • 9
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值