Skip to content

Commit c12ede2

Browse files
Aleksey LeshchukJuanVqz
andauthored
Randomized string concat (#216)
* randomized string concatenation comparison * bmbm approach + collateral actions extracted and measured * Update code/string/concatenation_randomized.rb Co-authored-by: Juan Vásquez <[email protected]> * Update code/string/concatenation_randomized.rb Co-authored-by: Juan Vásquez <[email protected]> * Update code/string/concatenation_randomized.rb Co-authored-by: Juan Vásquez <[email protected]> * Update code/string/concatenation_randomized.rb Co-authored-by: Juan Vásquez <[email protected]> * Update code/string/concatenation_randomized.rb Co-authored-by: Juan Vásquez <[email protected]> * Update code/string/concatenation_randomized.rb * Use benckmark/ips --------- Co-authored-by: Juan Vásquez <[email protected]> Co-authored-by: Juan Vasquez <[email protected]>
1 parent 7c87d13 commit c12ede2

File tree

1 file changed

+154
-0
lines changed

1 file changed

+154
-0
lines changed
Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
require 'benchmark/ips'
2+
3+
module RandStr
4+
RND_STRINGS_AMOUNT = 1000
5+
@rand_strs = {
6+
lt100: [],
7+
lt10: [],
8+
lt1000: [],
9+
eq10: [],
10+
eq100: [],
11+
}
12+
13+
def self.generate_rand_strs
14+
chars = ('A'..'z').to_a * 20
15+
@rand_strs[:lt10] = Array.new(RND_STRINGS_AMOUNT) { chars.sample(rand(10)).join }
16+
@rand_strs[:lt100] = Array.new(RND_STRINGS_AMOUNT) { chars.sample(rand(100)).join }
17+
@rand_strs[:lt1000] = Array.new(RND_STRINGS_AMOUNT) { chars.sample(rand(1000)).join }
18+
@rand_strs[:eq10] = Array.new(RND_STRINGS_AMOUNT) { chars.sample(10).join }
19+
@rand_strs[:eq100] = Array.new(RND_STRINGS_AMOUNT) { chars.sample(100).join }
20+
end
21+
22+
self.generate_rand_strs
23+
24+
def self.rand_str(named_range)
25+
@rand_strs[named_range][rand(RND_STRINGS_AMOUNT)]
26+
end
27+
28+
def self.method_missing(symbol)
29+
return super unless @rand_strs.keys.include?(symbol)
30+
31+
define_singleton_method(symbol) { rand_str(symbol) }
32+
return rand_str(symbol)
33+
end
34+
35+
end
36+
37+
38+
# 2 + 1 = 3 object
39+
def fastest_plus(foo, bar)
40+
foo + bar
41+
end
42+
43+
# 2 + 1 = 3 object
44+
def slow_concat(foo, bar)
45+
foo.concat bar
46+
end
47+
48+
# 2 + 1 = 3 object
49+
def slow_append(foo, bar)
50+
foo << bar
51+
end
52+
53+
54+
def fast_interpolation(foo, bar)
55+
"#{foo}#{bar}"
56+
end
57+
58+
# bench_100_to_100
59+
# Rehearsal -----------------------------------------------------------
60+
# String#+ 1.263725 0.027868 1.291593 ( 1.292498)
61+
# "#{foo}#{bar}" 1.139442 0.022956 1.162398 ( 1.163574)
62+
# String#concat 2.017746 0.014836 2.032582 ( 2.034682)
63+
# String#append 1.320778 0.000000 1.320778 ( 1.321896)
64+
# Collateral actions only 0.713309 0.000000 0.713309 ( 0.714402)
65+
# -------------------------------------------------- total: 6.520660sec
66+
#
67+
# user system total real nomalized ratio
68+
# Collateral actions only 0.703668 0.000000 0.703668 ( 0.705658)
69+
# String#+ 1.014123 0.000000 1.014123 ( 1.015003) 0.30934
70+
# "#{foo}#{bar}" 1.101751 0.000585 1.102336 ( 1.103558) 0.3979 x 1.3 slower
71+
# String#concat 1.382647 0.000000 1.382647 ( 1.385333) 0.679675 x 2.2 slower
72+
# String#append 1.319974 0.000000 1.319974 ( 1.324772) 0.619114 x 2 slower
73+
74+
def bench_100_to_100
75+
Benchmark.ips do |x|
76+
# 1M for rehearsal + 1M for bm
77+
sarr1 = Array.new(2_000_000) { RandStr.eq100.dup }
78+
sarr2 = Array.new(2_000_000) { RandStr.eq100.dup }
79+
80+
i, j = 0, 0
81+
# if we want compare apples with apples, we need to measure and exclude "collateral" operations:
82+
# integer += 1, access to an array of randomized strings 100 symbols length,
83+
# then two methods invocation from RandStr module eq100 / lt100.
84+
#
85+
# and only then we can compare string concat methods properly
86+
x.report("Collateral actions only") { k=0; 1_000_000.times { k+=1; RandStr.eq100; sarr2[k]; RandStr.lt100; } }
87+
88+
x.report("String#+") { k=0; 1_000_000.times { k+=1; sarr1[k]; fastest_plus(RandStr.eq100, RandStr.lt100) } }
89+
x.report('"#{foo}#{bar}"') { k=0; 1_000_000.times { k+=1; sarr2[k]; fast_interpolation(RandStr.eq100, RandStr.lt100) } }
90+
x.report("String#concat") { 1_000_000.times { RandStr.eq100; slow_concat(sarr1[i], RandStr.lt100); i+=1; } }
91+
x.report("String#append") { 1_000_000.times { RandStr.eq100; slow_append(sarr2[j], RandStr.lt100); j+=1; } }
92+
end
93+
end
94+
95+
# bench_100_to_1000
96+
# Rehearsal -----------------------------------------------------------
97+
# Collateral actions only 0.674168 0.000016 0.674184 ( 0.675031)
98+
# String#+ 2.148756 0.032954 2.181710 ( 2.187042)
99+
# "#{foo}#{bar}" 1.570816 0.004948 1.575764 ( 1.579080)
100+
# String#concat 2.223220 0.160917 2.384137 ( 2.387601)
101+
# String#append 2.005056 0.202962 2.208018 ( 2.211476)
102+
# -------------------------------------------------- total: 9.023813sec
103+
#
104+
# user system total real nomalized ratio
105+
# Collateral actions only 0.666190 0.000000 0.666190 ( 0.666398)
106+
# String#+ 1.077629 0.036944 1.114573 ( 1.115465) 0.449067
107+
# "#{foo}#{bar}" 1.230489 0.001029 1.231518 ( 1.232423) 0.566025 x 1.25 slower
108+
# String#concat 1.881313 0.149949 2.031262 ( 2.033965) 1.367567 x 3.05 slower
109+
# String#append 1.913785 0.177921 2.091706 ( 2.094298) 1.4279 x 3.18 slower
110+
111+
def bench_100_to_1000
112+
Benchmark.ips do |x|
113+
sarr1 = Array.new(2_000_000) { RandStr.eq100.dup }
114+
sarr2 = Array.new(2_000_000) { RandStr.eq100.dup }
115+
116+
i, j = 0, 0
117+
x.report("Collateral actions only") { k=0; 1_000_000.times { k+=1; RandStr.eq100; sarr2[k]; RandStr.lt1000; } }
118+
119+
x.report("String#+") { k=0; 1_000_000.times { k+=1; sarr1[k]; fastest_plus(RandStr.eq100, RandStr.lt1000) } }
120+
x.report('"#{foo}#{bar}"') { k=0; 1_000_000.times { k+=1; sarr2[k]; fast_interpolation(RandStr.eq100, RandStr.lt1000) } }
121+
x.report("String#concat") { 1_000_000.times { RandStr.eq100; slow_concat(sarr1[i], RandStr.lt1000); i+=1; } }
122+
x.report("String#append") { 1_000_000.times { RandStr.eq100; slow_append(sarr2[j], RandStr.lt1000); j+=1; } }
123+
end
124+
end
125+
126+
# bench_10_to_100
127+
# Rehearsal -----------------------------------------------------------
128+
# Collateral actions only 0.681273 0.000000 0.681273 ( 0.681611)
129+
# String#+ 1.188326 0.000701 1.189027 ( 1.196455)
130+
# "#{foo}#{bar}" 1.182554 0.003851 1.186405 ( 1.191678)
131+
# String#concat 1.707191 0.006764 1.713955 ( 1.720055)
132+
# String#append 1.177368 0.000831 1.178199 ( 1.184116)
133+
# -------------------------------------------------- total: 5.948859sec
134+
#
135+
# user system total real nomalized ratio
136+
# Collateral actions only 0.682486 0.000000 0.682486 ( 0.682818)
137+
# String#+ 0.914002 0.000000 0.914002 ( 0.917294) 0.234476
138+
# "#{foo}#{bar}" 1.096633 0.000966 1.097599 ( 1.100782) 0.417964 x 1.78 slower
139+
# String#concat 1.373582 0.000910 1.374492 ( 1.375239) 0.692421 x 2.95 slower
140+
# String#append 1.300632 0.000000 1.300632 ( 1.300807) 0.617989 x 2.63 slower
141+
142+
def bench_10_to_100
143+
Benchmark.ips do |x|
144+
sarr1 = Array.new(2_000_000) { RandStr.eq100.dup }
145+
sarr2 = Array.new(2_000_000) { RandStr.eq100.dup }
146+
147+
i, j = 0, 0
148+
x.report("Collateral actions only") { k=0; 1_000_000.times { k+=1; RandStr.eq10; sarr2[k]; RandStr.lt100; } }
149+
x.report("String#+") { k=0; 1_000_000.times { k+=1; sarr1[k]; fastest_plus(RandStr.eq10, RandStr.lt100) } }
150+
x.report('"#{foo}#{bar}"') { k=0; 1_000_000.times { k+=1; sarr2[k]; fast_interpolation(RandStr.eq10, RandStr.lt100) } }
151+
x.report("String#concat") { 1_000_000.times { RandStr.eq10; slow_concat(sarr1[i], RandStr.lt100); i+=1; } }
152+
x.report("String#append") { 1_000_000.times { RandStr.eq10; slow_append(sarr2[j], RandStr.lt100); j+=1; } }
153+
end
154+
end

0 commit comments

Comments
 (0)