lua三种字符串拼接性能分析

说明

lua中提供了3中字符串拼接函数,测试在日常使用情况下,各种拼接字符串的性能对比。

测试环境

操作系统:Debian GNU/Linux 8
CPU:Intel(R) Xeon(R) CPU E5-2640 v2 @ 2.00GHz
内存:64G
lua环境:LuaJIT-2.1.0-beta3 (测试的时候关闭jit)

测试

普通日常使用性能分析

日常拼接字符串的时候一般都是多个已存在的变量,夹杂一些字符串常量进行拼接,例如:

1  
package.cpath = pg.script_dir .. "/bot/lib/?.so;" .. id .. package.cpath  

—|—

  • 测试代码:

    1   2   3   4   5   6   7   8   9   10   11   12   13   14   15   16   17   18   19   20   21   22   23   24   25   26   27   28   29   30   31   32   33   34   35   36   37   38   39   40   41   42   43   44   45   46   47   48   49   50   51   52   53   54   55   56   57   58   59   60   61   62   63  
    
if jit then  
    jit.off()  
end  
  
-- 随机生成字符串备用  
local randomStringSzie = 200000  
local strTable = {}  
function ()  
    for i=1, randomStringSzie do  
        strTable[#strTable + 1] = tostring(math.random(1, randomStringSzie*10))  
    end  
end  
  
-- 辅助函数,用来打印时间  
function showtime(f)  
    local start = os.clock()  
    f()  
    print(os.clock() - start)  
end  
  
local testCount = 50000  
  
-- string.format  
function test1_1_0()  
    io.write("test1_1_0: use string.format: ")  
    for i=1, testCount do  
        local str = string.format("%s-string1-%s-string2-%s", strTable[math.random(1, randomStringSzie)],   
                    strTable[math.random(1, randomStringSzie)],  
                    strTable[math.random(1, randomStringSzie)])  
    end  
end  
  
-- ..  
function test1_2_0()  
    io.write("test1_2_0: use ..:            ")  
    for i=1, testCount do  
        local str = strTable[math.random(1, randomStringSzie)] .. "-string1-" ..  
                    strTable[math.random(1, randomStringSzie)] .. "-string2-" ..  
                    strTable[math.random(1, randomStringSzie)]  
    end  
end  
  
-- table.concat  
function test1_3_0()  
    io.write("test1_3_0: use table.concat:  ")  
    for i=1, testCount do  
        local str = table.concat({strTable[math.random(1, randomStringSzie)], "-string1-",  
                                strTable[math.random(1, randomStringSzie)], "-string2-",  
                                strTable[math.random(1, randomStringSzie)]})  
    end  
end  
  
makeStrTable()  
  
collectgarbage("collect")       -- 防止gc影响,先清除一遍  
showtime(test1_1_0)  
  
collectgarbage("collect")        -- 防止gc影响,先清除一遍  
showtime(test1_2_0)  
  
collectgarbage("collect")        -- 防止gc影响,先清除一遍  
showtime(test1_3_0)  

—|—

  • 为了避免字符串拼接函数执行先后顺序的影响,三个函数轮流被注释,单独执行5次的测试结果

    1   2   3   4   5   6   7   8   9   10   11   12   13   14   15   16   17  
    
    test1_1_0: use string.format: 0.029643  
test1_1_0: use string.format: 0.029626  
test1_1_0: use string.format: 0.029654  
test1_1_0: use string.format: 0.029586  
test1_1_0: use string.format: 0.029663  
  
test1_2_0: use ..:            0.025584  
test1_2_0: use ..:            0.025809  
test1_2_0: use ..:            0.025492  
test1_2_0: use ..:            0.025415  
test1_2_0: use ..:            0.025523  
  
test1_3_0: use table.concat:  0.035074  
test1_3_0: use table.concat:  0.035227  
test1_3_0: use table.concat:  0.035077  
test1_3_0: use table.concat:  0.034965  
test1_3_0: use table.concat:  0.035284  

—|—

  • 考虑到这边测试的拼接的都是本身就是字符串的变量,日常中会有很多将number拼接的情况,修改测试函数,使用纯number拼接测试一次:

    1   2   3   4   5   6   7   8   9   10   11   12   13   14   15   16   17   18   19   20   21   22   23   24   25   26   27   28   29   30  
    
    -- string.format  
function test1_1_0()  
    io.write("test1_1_0: use string.format: ")  
    for i=1, testCount do  
        local str = string.format("%d-string1-%d-string2-%d",  
                        math.random(1, randomStringSzie),  
                        math.random(1, randomStringSzie),  
                        math.random(1, randomStringSzie))  
    end  
end  
  
-- ..  
function test1_2_0()  
    io.write("test1_2_0: use ..:            ")  
    for i=1, testCount do  
        local str = math.random(1, randomStringSzie) .. "-string1-" ..  
                    math.random(1, randomStringSzie) .. "-string2-" ..  
                    math.random(1, randomStringSzie)  
    end  
end  
  
-- table.concat  
function test1_3_0()  
    io.write("test1_3_0: use table.concat:  ")  
    for i=1, testCount do  
        local str = table.concat({math.random(1, randomStringSzie), "-string1-",  
                                  math.random(1, randomStringSzie), "-string2-",  
                                  math.random(1, randomStringSzie)})  
    end  
end  

—|—

  • 同样的方式测试5次

    1   2   3   4   5   6   7   8   9   10   11   12   13   14   15   16   17  
    
    test1_1_0: use string.format: 0.028289  
test1_1_0: use string.format: 0.02815  
test1_1_0: use string.format: 0.028763  
test1_1_0: use string.format: 0.028143  
test1_1_0: use string.format: 0.028161  
  
test1_2_0: use ..:            0.031198  
test1_2_0: use ..:            0.030967  
test1_2_0: use ..:            0.031142  
test1_2_0: use ..:            0.031129  
test1_2_0: use ..:            0.03098  
  
test1_3_0: use table.concat:  0.038446  
test1_3_0: use table.concat:  0.037694  
test1_3_0: use table.concat:  0.037729  
test1_3_0: use table.concat:  0.03787  
test1_3_0: use table.concat:  0.037877  

—|—

  • 测试结论

    • 日常使用情况下都不应该使用table.concat函数来进行拼接字符串,因为每次拼接都会有创建table的性能消耗,并且也会带来gc压力
    • 如果使用number比较多的情况下的拼接的字符串,使用string.format性能比..好,反之,如果number较少使用..性能更好;测试发现,日常情况下(总拼接参数个数小于8个),如果number数量:string数量<=1的情况下,使用..性能会好点,其他情况下使用string.format

特殊情况——已存在table拼接

将一个已存在的table中的字符串拼接也可以用这三种字符串进行拼接,那table的大小会不会对三种方式性能有影响?

  • 测试代码

    1   2   3   4   5   6   7   8   9   10   11   12   13   14   15   16   17   18   19   20   21   22   23   24   25   26   27   28   29   30   31   32   33   34   35   36   37   38   39   40   41   42   43   44   45   46   47   48   49   50   51   52   53   54   55   56   57   58   59   60   61   62   63   64   65   66   67   68   69   70   71  
    
    if jit then  
    jit.off()  
end  
  
math.randomseed(os.time())  
  
local strTable = {}  
function ()  
    for i=1,200000 do  
        strTable[#strTable + 1] = tostring(math.random(1,10000000))  
    end  
end  
  
local maxCount = 50000  
  
function test2_1(arr, len)  
    io.write("test2_1: ")  
  
    local start = os.clock()  
      
    for cnt=1, maxCount do  
        local str = ""  
        for i=1, len do  
            str = string.format("%s%s", str, arr[i])  
        end  
    end  
  
    print( os.clock() - start)  
end  
  
function test2_2(arr, len)  
    io.write("test2_2: ")  
  
    local start = os.clock()  
      
    for cnt=1, maxCount do  
        local str = ""  
        for i=1, len do  
            str = str..arr[i]  
        end  
    end  
  
    print( os.clock() - start)  
end  
  
function test2_3(arr, len)  
    io.write("test2_3: ")  
  
    local start = os.clock()  
      
    for cnt=1, maxCount do  
        local str = table.concat(arr)  
    end  
  
    print( os.clock() - start)  
end  
  
makeStrTable()  
  
for i = 2, 10 do  
    local concatTable = {}  
    for j=1, i do  
        concatTable[#concatTable + 1] = strTable[math.random(1,200000)]  
    end  
    collectgarbage("collect")  
    test2_1(concatTable, i)  
    collectgarbage("collect")  
    test2_2(concatTable, i)  
    collectgarbage("collect")  
    test2_3(concatTable, i)  
end  

—|—

  • 同样为了相互直接的影响,分别注释并测试的测试结果

    1   2   3   4   5   6   7   8   9   10   11   12   13   14   15   16   17   18   19   20   21   22   23   24   25   26   27   28   29  
    
    test2_1: 0.012331  
test2_1: 0.018489  
test2_1: 0.02484  
test2_1: 0.030889  
test2_1: 0.03711  
test2_1: 0.044915  
test2_1: 0.050445  
test2_1: 0.057397  
test2_1: 0.064486  
  
test2_2: 0.007221  
test2_2: 0.011114  
test2_2: 0.013879  
test2_2: 0.017953  
test2_2: 0.021438  
test2_2: 0.024831  
test2_2: 0.028542  
test2_2: 0.033702  
test2_2: 0.037664  
  
test2_3: 0.00487  
test2_3: 0.005351  
test2_3: 0.005765  
test2_3: 0.006716  
test2_3: 0.007291  
test2_3: 0.00796  
test2_3: 0.0082739999999999  
test2_3: 0.009553  
test2_3: 0.010339  

—|—

  • 测试结论

    • 在已经存在的table去调用字符串拼接,不论table里面元素多少,都是table.concat快,因为没有了table创建的开销

特殊情况——创建一个table并拼接

一般情况下,都是从某个table选取一些进行拼接,这时候就不能直接用table.concat对已存在的table进行拼接,需要重新构造一个新的table,或者直接使用string.format, .. 进行拼接,那这种情况下,使用哪种性能最好?新构造的table的大小对各种情况有没有性能影响?

  • 测试代码

    1   2   3   4   5   6   7   8   9   10   11   12   13
    

糖果

糖果
LUA教程

Lapis框架的常用处理方法

Lapis框架的常用处理方法 Continue reading

MoonScript实现选择排序

Published on February 26, 2017

MoonScript与Redis客户端

Published on January 19, 2017