• 【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函数重写一下:

    1. -- 保存原始的 require 函数
    2. local original_require = require
    3. -- 新的 require 函数,重写路径处理
    4. function require(modname)
    5. -- 将路径中的斜杠替换为点
    6. local modified_modname = modname:gsub("/", ".")
    7. -- 调用原始的 require 函数加载模块
    8. return original_require(modified_modname)
    9. end

    就能避免这个重复加载了的问题。

    真机上跑了一下,运行内存竟然少了近几十M(项目里数值表占用太多吧= =), 加载卡顿也少了一些。


    放一下lua源码里的 require实现:
    https://www.lua.org/source/5.4/loadlib.c.html

    1. static int ll_require (lua_State *L) {
    2. const char *name = luaL_checkstring(L, 1);
    3. lua_settop(L, 1); /* LOADED table will be at index 2 */
    4. lua_getfield(L, LUA_REGISTRYINDEX, LUA_LOADED_TABLE);
    5. lua_getfield(L, 2, name); /* LOADED[name] */
    6. if (lua_toboolean(L, -1)) /* is it there? */
    7. return 1; /* package is already loaded */
    8. /* else must load package */
    9. lua_pop(L, 1); /* remove 'getfield' result */
    10. findloader(L, name);
    11. lua_rotate(L, -2, 1); /* function <-> loader data */
    12. lua_pushvalue(L, 1); /* name is 1st argument to module loader */
    13. lua_pushvalue(L, -3); /* loader data is 2nd argument */
    14. /* stack: ...; loader data; loader function; mod. name; loader data */
    15. lua_call(L, 2, 1); /* run loader to load module */
    16. /* stack: ...; loader data; result from loader */
    17. if (!lua_isnil(L, -1)) /* non-nil return? */
    18. lua_setfield(L, 2, name); /* LOADED[name] = returned value */
    19. else
    20. lua_pop(L, 1); /* pop nil */
    21. if (lua_getfield(L, 2, name) == LUA_TNIL) { /* module set no value? */
    22. lua_pushboolean(L, 1); /* use true as result */
    23. lua_copy(L, -1, -2); /* replace loader result */
    24. lua_setfield(L, 2, name); /* LOADED[name] = true */
    25. }
    26. lua_rotate(L, -2, 1); /* loader data <-> module result */
    27. return 2; /* return module result and loader data */
    28. }

    用lua翻译就是:

    1. --require 函数的实现
    2. function require(name)
    3. if not package.loaded[name] then
    4. local loader = findloader(name) //这一步演示在代码中以抽象函数findloader来表示
    5. if loader == nil then
    6. error("unable to load module" .. name)
    7. end
    8. package.loaded[name] = true
    9. local res = loader(name)
    10. if res ~= nil then
    11. package.loaded[name] = res
    12. end
    13. end
    14. return package.loaded[name]
    15. end


    from Lua-require_lua require原理-CSDN博客


    ToLua\macnojit\lua\loadlib.c里的实现     luaL_gsub(L, name, "/", ".");做了替换

    1. static int ll_require (lua_State *L) {
    2. const char *name = luaL_checkstring(L, 1);
    3. int i;
    4. lua_settop(L, 1); /* _LOADED table will be at index 2 */
    5. const char* key = luaL_gsub(L, name, "/", ".");
    6. lua_getfield(L, LUA_REGISTRYINDEX, "_LOADED");
    7. lua_getfield(L, 3, key);
    8. if (lua_toboolean(L, -1)) { /* is it there? */
    9. if (lua_touserdata(L, -1) == sentinel) /* check loops */
    10. luaL_error(L, "loop or previous error loading module " LUA_QS, name);
    11. return 1; /* package is already loaded */
    12. }
    13. /* else must load it; iterate over available loaders */
    14. lua_getfield(L, LUA_ENVIRONINDEX, "loaders");
    15. if (!lua_istable(L, -1))
    16. luaL_error(L, LUA_QL("package.loaders") " must be a table");
    17. lua_pushliteral(L, ""); /* error message accumulator */
    18. for (i=1; ; i++) {
    19. lua_rawgeti(L, -2, i); /* get a loader */
    20. if (lua_isnil(L, -1))
    21. luaL_error(L, "module " LUA_QS " not found:%s",
    22. name, lua_tostring(L, -2));
    23. lua_pushstring(L, name);
    24. lua_call(L, 1, 1); /* call it */
    25. if (lua_isfunction(L, -1)) /* did it find module? */
    26. break; /* module loaded successfully */
    27. else if (lua_isstring(L, -1)) /* loader returned error message? */
    28. lua_concat(L, 2); /* accumulate it */
    29. else
    30. lua_pop(L, 1);
    31. }
    32. lua_pushlightuserdata(L, sentinel);
    33. lua_setfield(L, 3, key); /* _LOADED[name] = sentinel */
    34. lua_pushstring(L, name); /* pass name as argument to module */
    35. lua_call(L, 1, 1); /* run loaded module */
    36. if (!lua_isnil(L, -1)) /* non-nil return? */
    37. lua_setfield(L, 3, key); /* _LOADED[name] = returned value */
    38. lua_getfield(L, 3, key);
    39. if (lua_touserdata(L, -1) == sentinel) { /* module did not set a value? */
    40. lua_pushboolean(L, 1); /* use true as result */
    41. lua_pushvalue(L, -1); /* extra copy to be returned */
    42. lua_setfield(L, 3, key); /* _LOADED[name] = true */
    43. }
    44. return 1;
    45. }


    https://github.com/LuaJIT/LuaJIT/blob/v2.1/src/lib_package.c  里没有实现替换,这边项目里本来改过的,后来升级一次不小心又改掉这个修改了:

    1. #define KEY_SENTINEL (U64x(80000000,00000000)|'s')
    2. static int lj_cf_package_require(lua_State *L)
    3. {
    4. const char *name = luaL_checkstring(L, 1);
    5. int i;
    6. lua_settop(L, 1); /* _LOADED table will be at index 2 */
    7. lua_getfield(L, LUA_REGISTRYINDEX, "_LOADED");
    8. lua_getfield(L, 2, name);
    9. if (lua_toboolean(L, -1)) { /* is it there? */
    10. if ((L->top-1)->u64 == KEY_SENTINEL) /* check loops */
    11. luaL_error(L, "loop or previous error loading module " LUA_QS, name);
    12. return 1; /* package is already loaded */
    13. }
    14. /* else must load it; iterate over available loaders */
    15. lua_getfield(L, LUA_ENVIRONINDEX, "loaders");
    16. if (!lua_istable(L, -1))
    17. luaL_error(L, LUA_QL("package.loaders") " must be a table");
    18. lua_pushliteral(L, ""); /* error message accumulator */
    19. for (i = 1; ; i++) {
    20. lua_rawgeti(L, -2, i); /* get a loader */
    21. if (lua_isnil(L, -1))
    22. luaL_error(L, "module " LUA_QS " not found:%s",
    23. name, lua_tostring(L, -2));
    24. lua_pushstring(L, name);
    25. lua_call(L, 1, 1); /* call it */
    26. if (lua_isfunction(L, -1)) /* did it find module? */
    27. break; /* module loaded successfully */
    28. else if (lua_isstring(L, -1)) /* loader returned error message? */
    29. lua_concat(L, 2); /* accumulate it */
    30. else
    31. lua_pop(L, 1);
    32. }
    33. (L->top++)->u64 = KEY_SENTINEL;
    34. lua_setfield(L, 2, name); /* _LOADED[name] = sentinel */
    35. lua_pushstring(L, name); /* pass name as argument to module */
    36. lua_call(L, 1, 1); /* run loaded module */
    37. if (!lua_isnil(L, -1)) /* non-nil return? */
    38. lua_setfield(L, 2, name); /* _LOADED[name] = returned value */
    39. lua_getfield(L, 2, name);
    40. if ((L->top-1)->u64 == KEY_SENTINEL) { /* module did not set a value? */
    41. lua_pushboolean(L, 1); /* use true as result */
    42. lua_pushvalue(L, -1); /* extra copy to be returned */
    43. lua_setfield(L, 2, name); /* _LOADED[name] = true */
    44. }
    45. lj_lib_checkfpu(L);
    46. return 1;
    47. }


     

  • 相关阅读:
    Pandas知识点-详解行列级批处理函数apply
    vue中的依赖升级
    Kotlin高仿微信-第7篇-主页-动态权限申请
    stm32f103c8t6最小系统引脚及功能原理图
    Java 面试题:如何保证集合是线程安全的? ConcurrentHashMap 如何实现高效地线程安全?
    SAP 直接外部数据库技术配置手册-Oracle
    第十九次CCF计算机软件能力认证
    windows上的静态链接和动态链接的区别与作用(笔记)
    手把手教你开发微信小程序自定义底部导航栏
    跑步需要哪些运动装备?跑步爱好者者的装备推荐
  • 原文地址:https://blog.csdn.net/zhenmu/article/details/136451603