<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      最近發現線上有個服務器某些邏輯耗時比較久,問了下同事,他告訴我是因為lua的pairs函數很慢導致的。

      “啊!不至于吧,這數據量才多少”我一臉詫異,記憶中Lua不至于慢到這種程度,遍歷個幾十萬的table速度還是很快的,而我們線上的這個table數據量才幾萬。

      他把線上的數據導了出來,做了一個測試,發現僅僅遍歷一個5萬多table,Lua確實花了將近3秒多的時間。

      整個程序非常簡單,大概就是

      local tbl = {}
      -- 里面保存了5萬多個key,什么都不做,僅遍歷
      for _ in pairs(tbl) do end
      

      就這樣,它也能花將近3秒的時間,實在讓人大跌眼鏡。

      考慮到線上的程序是從Lua 5.1升級到Lua 5.3的,而我們生成的id都是64位整數,我的直覺是5.3加入的64位整數出了bug。然而我隨機了6萬個數字做key,發現遍歷還是挺快的,大約3毫秒左右。這說明Lua本身是沒有問題的,可能是我們線上程序的key觸發了某個bug。

      于是我又下載了Lua 5.1和Lua 5.4進行測試,發現這兩個版本是沒問題的,耗時都在毫秒級別。

      下載Lua 5.3.6的源碼,修改src目錄里的Makefile,使用-O0 -g3進行編譯。再用valgrind --tool=callgrind ./lua test.lua跑一下性能測試,發現在程序在luaH_next -->> findindex -->>luaV_equalobj這里耗時很久。

      從5.1到5.4版本,Lua table的基礎設定都沒有變化。整個table分為兩部分,一部分是數組,一部分是hash。運行-g3編譯的lua,通過gdb斷點查看,5.3和5.4版本在當前測試的程序中,都沒有使用數組。應該是hash的問題。那就剩下兩種可能,一種是findindex邏輯有問題,還有一種就是hash邏輯有問題。

      通過對比兩個版本的findindex函數,雖然有細小的差異,但基本邏輯就是判斷是否在數組中,如果不在,則進行hash,根據hash值從hash結構里取對象。

      // ltable.c
      
      static Node *mainposition (const Table *t, int ktt, const Value *kvl) {
        switch (withvariant(ktt)) {
          case LUA_VNUMINT:
            return hashint(t, ivalueraw(*kvl));
          case LUA_VNUMFLT:
            return hashmod(t, l_hashfloat(fltvalueraw(*kvl)));
          case LUA_VSHRSTR:
            return hashstr(t, tsvalueraw(*kvl));
          case LUA_VLNGSTR:
            return hashpow2(t, luaS_hashlongstr(tsvalueraw(*kvl)));
          case LUA_VFALSE:
            return hashboolean(t, 0);
          case LUA_VTRUE:
            return hashboolean(t, 1);
          case LUA_VLIGHTUSERDATA:
            return hashpointer(t, pvalueraw(*kvl));
          case LUA_VLCF:
            return hashpointer(t, fvalueraw(*kvl));
          default:
            return hashpointer(t, gcvalueraw(*kvl));
        }
      }
      
      static const TValue *getgeneric (Table *t, const TValue *key, int deadok) {
        Node *n = mainpositionTV(t, key);
        for (;;) {  /* check whether 'key' is somewhere in the chain */
          if (equalkey(key, n, deadok))
            return gval(n);  /* that's it */
          else {
            int nx = gnext(n);
            if (nx == 0)
              return &absentkey;  /* not found */
            n += nx;
          }
        }
      }
      
      static unsigned int findindex (lua_State *L, Table *t, TValue *key,
                                     unsigned int asize) {
        unsigned int i;
        if (ttisnil(key)) return 0;  /* first iteration */
        i = ttisinteger(key) ? arrayindex(ivalue(key)) : 0;
        if (i - 1u < asize)  /* is 'key' inside array part? */
          return i;  /* yes; that's the index */
        else {
          const TValue *n = getgeneric(t, key, 1);
          if (unlikely(isabstkey(n)))
            luaG_runerror(L, "invalid key to 'next'");  /* key not found */
          i = cast_int(nodefromval(n) - gnode(t, 0));  /* key index in hash table */
          /* hash elements are numbered after array ones */
          return (i + 1) + asize;
        }
      }
      

      那剩下一種可能就是hash有問題了。lua有幾個hash函數:hashstr、hashboolean、hashint、hashpointer等等,分別對應不同的類型作key。由于測試中使用的都是int64的整數作為key,那對比了一下5.3和5.4版本的hashint函數,發現他們真的不一樣。

      // lua 5.3.6
      #define lmod(s,size) \
      	(check_exp((size&(size-1))==0, (cast_int((s) & ((size)-1)))))
      #define hashpow2(t,n)		(gnode(t, lmod((n), sizenode(t))))
      
      #define hashint(t,i)		hashpow2(t, i)
      
      // lua 5.4.4
      
      #define sizenode(t)	(twoto((t)->lsizenode))
      
      #define hashmod(t,n)	(gnode(t, ((n) % ((sizenode(t)-1)|1))))
      
      static Node *hashint (const Table *t, lua_Integer i) {
        lua_Unsigned ui = l_castS2U(i);
        if (ui <= (unsigned int)INT_MAX)
          return hashmod(t, cast_int(ui));
        else
          return hashmod(t, ui);
      }
      

      sizenode(t)是取當前table的節點數量,當插入一個元素到table時,無論是插入到數組部分,還是到hash部分,如果是超過了預留的節點,那這個節點就會相應地增加,然后進行rehash,所有元素進行重排。因此遍歷table的時候,這個節點數量是不變的。從gdb調試的堆棧查到,Lua 5.3運行這個測試程序時這個值為16。那它的這個hash邏輯就變成了

      // 假如現在有一個key為100,當前sizenode(t)為16
      
      hashint(t,i);
      // 宏展開為
      (gnode(t, lmod(100, 16)));
      // 宏展開為
      (gnode(t, 100 & 15));
      // 宏展開為
      t->node[100 & 15];
      

      所以,Lua 5.3的hashint函數可以總結為: key & sizenode(t)。大多數語言的hash函數(java、c++之類),基本就兩個要點:一是效率高,因為插入、查找都要頻繁調用hash,因此這個函數執行效率一定要高;二是沖突少,如果沖突太多,hash就會往數組那邊退化導致效率下降。Lua 5.3的這個hash函數,效率是足夠高了,可沖突就多得不行了。由于需要考慮內存占用的問題,sizenode(t)的值肯定不會太大(比如測試的例子中,值為16)。key與sizenode(t)做一個與操作基本就是取低N位。假如有幾個數,他們的低位是一樣的,但高位不一樣,那不就沖突了嗎?

      為了驗證這個問題,我特意重新設計了測試程序

      local random_key = {}
      for i = 1, 60000 do
              local k = math.random(1000000, 10000000)
              random_key[k] = true
      end
      
      local count = 0
      local beg = os.clock()
      for _ in pairs(random_key) do count = count + 1 end
      print("random key", os.clock() - beg, count)
      
      
      local tbl = {}
      for i = 1, 60000 do
              local k = (i << 32) | 10086
              tbl[k] = true
      end
      
      local beg = os.clock()
      for _ in pairs(tbl) do end
      print("test done", os.clock() - beg, _VERSION)
      

      結果跑下來,這些數字作key的話,就真的很慢。可以看到,隨機數字的話,大約為3ms,這些特意生成的數字,Lua 5.3跑出了14s。

      debian:~/local_code/lua-5.3.6/src$ lua test.lua 
      random key      0.003292        59805
      test done       3.368816        Lua 5.4
      debian:~/local_code/lua-5.3.6/src$ ./lua test.lua 
      random key      0.003773        59811
      test done       14.956801       Lua 5.3
      

      采用高低位合并成一個64位整數來生成id,是很多業務中常用的邏輯,然則這里就踩坑了。
      后來,通過把Lua 5.4.4的hash函數移植到Lua 5.3,重新編譯后再測試就正常了。不過我建議還是直接升級到Lua 5.4.4,不用改源碼。

      細心的讀者可能會發現,上面的測試結果中,Lua 5.4雖然比Lua 5.3快,但也花了3s多,這明顯是不正常的。是的,這確實是有問題。這是因為我這系統中的Lua是很久之前安裝的,雖然是5.4的版本,但我不太清楚具體是哪個版本,我翻了下本地的一個源碼包,可能是5.4.2,這個版本的hash函數還是和Lua 5.3是一樣的。我重新下了一個5.4.4的版本編譯后執行,結果才是正常的

      debian:~/local_code/lua-5.4.4/src$ ./lua test.lua 
      random key      0.003401        59794
      test done       0.002218        Lua 5.4
      

      這里又有一個問題,那就是Lua 5.4.2的hash函數即使和Lua 5.3一樣,為什么它還是要比5.3要快很多。關注過Lua版本的大概是知道原因的。Lua 5.1是一個經典的版本,發布時間長,使用的人也多,優化得很好。到了5.3版本,加入了int64的支持,做了比較大的改動,性能下降比較多。到了5.4,又做了很多優化,連lua的虛擬機都重構了,所以即使同樣的算法,執行效率也比5.3高一些。根據網上的一些測試結果,比5.1都要好。

      這里我不明白的是為什么Lua 5.3會選擇這樣一個hash函數,因為從5.1和5.4.4的hash函數是一樣的,唯獨5.3采用這么一個性能比較差的算法。不過由于5.3版本是老版本了,我也不想再去研究這個。

      posted on 2022-12-18 21:29  coding my life  閱讀(785)  評論(0)    收藏  舉報
      主站蜘蛛池模板: 婷婷丁香五月亚洲中文字幕| 久青草视频在线观看免费| 人妻少妇偷人作爱av| 在线播放国产精品三级网| 中国女人高潮hd| 国产日韩精品中文字幕| 精品无套挺进少妇内谢| 亚洲乳大丰满中文字幕| 久久国产国内精品国语对白| 精品一区二区三区四区五区 | 国产精选一区二区三区| 人妻无码久久精品| 欧美性猛交xxxx乱大交丰满| 成人国产精品中文字幕| 国内精品久久久久久久97牛牛| 国产一区二区三区黄色大片| 99在线精品国自产拍中文字幕| 国产中文字幕一区二区| 国产精品最新免费视频| 性欧美vr高清极品| 亚洲精品国产摄像头| 国产一区二区三区内射高清| 国产玖玖玖玖精品电影| 性欧美VIDEOFREE高清大喷水| 夜夜偷天天爽夜夜爱| 长腿校花无力呻吟娇喘| 国产在线不卡精品网站| 国内精品自在拍精选| 无码免费大香伊蕉在人线国产| 影音先锋男人站| 久久热这里只有精品66| 在线播放国产精品亚洲 | 午夜福利日本一区二区无码| 人妻换着玩又刺激又爽| 极品美女aⅴ在线观看| 中国熟妇毛多多裸交视频| 麻豆麻豆麻豆麻豆麻豆麻豆| 成av免费大片黄在线观看| 国产无遮挡免费视频免费| 亚洲精品一区二区动漫| 一区二区三区精品偷拍|