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

最近在分析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函数重写一下:

Lua 复制代码
-- 保存原始的 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

cpp 复制代码
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翻译就是:

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, "/", ".");做了替换

cpp 复制代码
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 里没有实现替换,这边项目里本来改过的,后来升级一次不小心又改掉这个修改了:

cpp 复制代码
#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;
}
相关推荐
全栈师16 分钟前
C#中分组循环的做法
开发语言·c#
FAREWELL0007519 分钟前
C#进阶学习(十六)C#中的迭代器
开发语言·学习·c#·迭代器模式·迭代器
吗喽对你问好32 分钟前
Java位运算符大全
java·开发语言·位运算
chenglin01641 分钟前
.NET中,const和readonly区别
开发语言·.net
DXM05211 小时前
牟乃夏《ArcGIS Engine地理信息系统开发教程》学习笔记3-地图基本操作与实战案例
开发语言·笔记·学习·arcgis·c#·ae·arcgis engine
Vaclee1 小时前
JavaScript-基础语法
开发语言·javascript·ecmascript
CodeWithMe2 小时前
【C++】线程池
开发语言·c++
专注API从业者2 小时前
《Go 语言高并发爬虫开发:淘宝商品 API 实时采集与 ETL 数据处理管道》
开发语言·后端·爬虫·golang
欧先生^_^3 小时前
Scala语法基础
开发语言·后端·scala
青铜爱码士3 小时前
redis+lua+固定窗口实现分布式限流
redis·分布式·lua