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
17test1_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
17test1_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
71if 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
29test2_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