diff options
Diffstat (limited to 'benchmark')
35 files changed, 796 insertions, 10 deletions
diff --git a/benchmark/README.md b/benchmark/README.md index c5c29d0daf..9f9192685e 100644 --- a/benchmark/README.md +++ b/benchmark/README.md @@ -40,7 +40,7 @@ Usage: benchmark-driver [options] RUBY|YAML... --filter REGEXP Filter out benchmarks with given regexp --run-duration SECONDS Warmup estimates loop_count to run for this duration (default: 3) --timeout SECONDS Timeout ruby command execution with timeout(1) - -v, --verbose Verbose mode. Multiple -v options increase visilibity (max: 2) + -v, --verbose Verbose mode. Multiple -v options increase visibility (max: 2) ``` ## make benchmark diff --git a/benchmark/app_fib.rb b/benchmark/app_fib.rb index 34a7b2e725..e61bc8aa32 100644 --- a/benchmark/app_fib.rb +++ b/benchmark/app_fib.rb @@ -1,4 +1,4 @@ -def fib n +def fib(n) if n < 3 1 else diff --git a/benchmark/class_superclass.yml b/benchmark/class_superclass.yml new file mode 100644 index 0000000000..847ff811f1 --- /dev/null +++ b/benchmark/class_superclass.yml @@ -0,0 +1,23 @@ +prelude: | + class SimpleClass; end + class OneModuleClass + 1.times { include Module.new } + end + class MediumClass + 10.times { include Module.new } + end + class LargeClass + 100.times { include Module.new } + end +benchmark: + object_class_superclass: | + Object.superclass + simple_class_superclass: | + SimpleClass.superclass + one_module_class: | + OneModuleClass.superclass + medium_class_superclass: | + MediumClass.superclass + large_class_superclass: | + LargeClass.superclass +loop_count: 20000000 diff --git a/benchmark/dir_pwd.yml b/benchmark/dir_pwd.yml new file mode 100644 index 0000000000..c435d3ac5e --- /dev/null +++ b/benchmark/dir_pwd.yml @@ -0,0 +1,2 @@ +benchmark: + pwd: Dir.pwd diff --git a/benchmark/file_basename.yml b/benchmark/file_basename.yml new file mode 100644 index 0000000000..fbd78785aa --- /dev/null +++ b/benchmark/file_basename.yml @@ -0,0 +1,6 @@ +prelude: | + # frozen_string_literal: true +benchmark: + long: File.basename("/Users/george/src/github.com/ruby/ruby/benchmark/file_dirname.yml") + long_name: File.basename("Users_george_src_github.com_ruby_ruby_benchmark_file_dirname.yml") + withext: File.basename("/Users/george/src/github.com/ruby/ruby/benchmark/file_dirname.yml", ".yml") diff --git a/benchmark/file_dirname.yml b/benchmark/file_dirname.yml new file mode 100644 index 0000000000..43a81c9371 --- /dev/null +++ b/benchmark/file_dirname.yml @@ -0,0 +1,6 @@ +prelude: | + # frozen_string_literal: true +benchmark: + long: File.dirname("/Users/george/src/github.com/ruby/ruby/benchmark/file_dirname.yml") + short: File.dirname("foo/bar") + n_4: File.dirname("/Users/george/src/github.com/ruby/ruby/benchmark/file_dirname.yml", 4) diff --git a/benchmark/file_expand_path.yml b/benchmark/file_expand_path.yml new file mode 100644 index 0000000000..9e503ab003 --- /dev/null +++ b/benchmark/file_expand_path.yml @@ -0,0 +1,4 @@ +prelude: | + # frozen_string_literal: true +benchmark: + expand_path: File.expand_path("../../foo.txt", __FILE__) diff --git a/benchmark/file_extname.yml b/benchmark/file_extname.yml new file mode 100644 index 0000000000..fb16e55840 --- /dev/null +++ b/benchmark/file_extname.yml @@ -0,0 +1,6 @@ +prelude: | + # frozen_string_literal: true +benchmark: + long: File.extname("/Users/george/src/github.com/ruby/ruby/benchmark/file_dirname.yml") + long_name: File.extname("Users_george_src_github.com_ruby_ruby_benchmark_file_dirname.yml") + short: File.extname("foo/bar") diff --git a/benchmark/file_join.yml b/benchmark/file_join.yml new file mode 100644 index 0000000000..845257cf1e --- /dev/null +++ b/benchmark/file_join.yml @@ -0,0 +1,7 @@ +prelude: | + # frozen_string_literal: true +benchmark: + two_strings: File.join(__FILE__, "path") + many_strings: File.join(__FILE__, "path", "a", "b", "c", "d") + array: File.join([__FILE__, "path", "a", "b", "c", "d"]) + mixed: File.join(__FILE__, "path", "a", "b", ["c", "d"]) diff --git a/benchmark/float_predicate.yml b/benchmark/float_predicate.yml new file mode 100644 index 0000000000..b946937666 --- /dev/null +++ b/benchmark/float_predicate.yml @@ -0,0 +1,12 @@ +prelude: | + floats = [1.0, -1.0, 0.0, Float::NAN, Float::INFINITY, -Float::INFINITY] + +benchmark: + float_nan?: floats.each { |f| f.nan? } + float_finite?: floats.each { |f| f.finite? } + float_infinite?: floats.each { |f| f.infinite? } + float_zero?: floats.each { |f| f.zero? } + float_positive?: floats.each { |f| f.positive? } + float_negative?: floats.each { |f| f.negative? } + +loop_count: 1000000 diff --git a/benchmark/int_to_s.yml b/benchmark/int_to_s.yml new file mode 100644 index 0000000000..000dae9612 --- /dev/null +++ b/benchmark/int_to_s.yml @@ -0,0 +1,25 @@ +prelude: | + # frozen_string_literal: true + N1 = 5 + N2 = 42 + N3 = 400 + N5 = 12345 + N10 = 1_234_567_890 + N19 = 4_611_686_018_427_387_903 + NEG = -1_234_567_890 + BIG20 = 10 ** 19 + 12_345_678_901_234_567 + BIG40 = 10 ** 39 + 123_456_789_012_345 + BIG100 = 10 ** 99 + 42 +benchmark: + fix_1digit: "N1.to_s" + fix_2digit: "N2.to_s" + fix_3digit: "N3.to_s" + fix_5digit: "N5.to_s" + fix_10digit: "N10.to_s" + fix_19digit: "N19.to_s" + fix_negative: "NEG.to_s" + big_20digit: "BIG20.to_s" + big_40digit: "BIG40.to_s" + big_100digit: "BIG100.to_s" + interp_id: '"id=#{N10}"' + interp_mixed: '"a=#{N2},b=#{N5},c=#{N10}"' diff --git a/benchmark/integer_predicate.yml b/benchmark/integer_predicate.yml new file mode 100644 index 0000000000..7c05ff2587 --- /dev/null +++ b/benchmark/integer_predicate.yml @@ -0,0 +1,9 @@ +prelude: | + nums = (0..9).to_a + +benchmark: + integer_zero?: nums.each { |n| n.zero? } + integer_even?: nums.each { |n| n.even? } + integer_odd?: nums.each { |n| n.odd? } + +loop_count: 1000000 diff --git a/benchmark/io_close.yml b/benchmark/io_close.yml new file mode 100644 index 0000000000..a552872884 --- /dev/null +++ b/benchmark/io_close.yml @@ -0,0 +1,13 @@ +prelude: | + ios = 1000.times.map do + 100.times.map{IO.pipe} + end +benchmark: + # Close IO + io_close: | + # Process each batch of ios per iteration of the benchmark. + ios.pop.each do |r, w| + r.close + w.close + end +loop_count: 100 diff --git a/benchmark/io_close_contended.yml b/benchmark/io_close_contended.yml new file mode 100644 index 0000000000..1d9e4e0d0f --- /dev/null +++ b/benchmark/io_close_contended.yml @@ -0,0 +1,21 @@ +prelude: | + ios = 100.times.map do + 10.times.map do + pipe = IO.pipe.tap do |r, w| + Thread.new do + r.read + rescue IOError + # Ignore + end + end + end + end +benchmark: + # Close IO + io_close_contended: | + # Process each batch of ios per iteration of the benchmark. + ios.pop.each do |r, w| + r.close + w.close + end +loop_count: 10 diff --git a/benchmark/lib/benchmark_driver/runner/ractor.rb b/benchmark/lib/benchmark_driver/runner/ractor.rb index c730b8e4a5..fd9c2dd4db 100644 --- a/benchmark/lib/benchmark_driver/runner/ractor.rb +++ b/benchmark/lib/benchmark_driver/runner/ractor.rb @@ -87,7 +87,7 @@ __bmdv_ractors << Ractor.new(__bmdv_loop_after - __bmdv_loop_before) { |__bmdv_l <% end %> # Wait for all Ractors before executing code to write results -__bmdv_ractors.map!(&:take) +__bmdv_ractors.map!(&:value) <% results.each do |result| %> File.write(<%= result.dump %>, __bmdv_ractors.shift) diff --git a/benchmark/module_eqq.yml b/benchmark/module_eqq.yml index a561fb86dc..2f9c490d92 100644 --- a/benchmark/module_eqq.yml +++ b/benchmark/module_eqq.yml @@ -1,4 +1,5 @@ prelude: | + module SomeModule; end class SimpleClass; end class MediumClass 10.times { include Module.new } @@ -24,4 +25,8 @@ benchmark: SimpleClass === LargeObj simple_class_eqq_huge_obj: | SimpleClass === HugeObj -loop_count: 20000000 + simple_class_eqq_module: | + SimpleClass === HugeObj + module_eqq_module: | + SomeModule === HugeObj +loop_count: 10000000 diff --git a/benchmark/nilclass.yml b/benchmark/nilclass.yml index da66e71068..66234c4cdf 100644 --- a/benchmark/nilclass.yml +++ b/benchmark/nilclass.yml @@ -1,10 +1,16 @@ prelude: | def a = nil benchmark: + rationalize: + nil.rationalize + to_c: | + nil.to_c to_i: | nil.to_i to_f: | nil.to_f + to_r: | + nil.to_r splat: | a(*nil) loop_count: 100000 diff --git a/benchmark/object_allocate.yml b/benchmark/object_allocate.yml index bdbd4536db..c6269923f0 100644 --- a/benchmark/object_allocate.yml +++ b/benchmark/object_allocate.yml @@ -45,4 +45,5 @@ benchmark: allocate_kwarg_params: "KWArg.new(a: 1, b: 2, c: 3, d: 4)" allocate_mixed_params: "Mixed.new(1, 2, c: 3, d: 4)" allocate_no_params: "Object.new" + allocate_allocate: "Object.allocate" loop_count: 100000 diff --git a/benchmark/object_class.yml b/benchmark/object_class.yml new file mode 100644 index 0000000000..1e5409d1e2 --- /dev/null +++ b/benchmark/object_class.yml @@ -0,0 +1,40 @@ +prelude: | + def get_class(obj) + i = 10_000 + while i > 0 + i -= 1 + # 100 times per loop + obj.class; obj.class; obj.class; obj.class; obj.class; obj.class; obj.class; obj.class; obj.class; obj.class; + obj.class; obj.class; obj.class; obj.class; obj.class; obj.class; obj.class; obj.class; obj.class; obj.class; + obj.class; obj.class; obj.class; obj.class; obj.class; obj.class; obj.class; obj.class; obj.class; obj.class; + obj.class; obj.class; obj.class; obj.class; obj.class; obj.class; obj.class; obj.class; obj.class; obj.class; + obj.class; obj.class; obj.class; obj.class; obj.class; obj.class; obj.class; obj.class; obj.class; obj.class; + obj.class; obj.class; obj.class; obj.class; obj.class; obj.class; obj.class; obj.class; obj.class; obj.class; + obj.class; obj.class; obj.class; obj.class; obj.class; obj.class; obj.class; obj.class; obj.class; obj.class; + obj.class; obj.class; obj.class; obj.class; obj.class; obj.class; obj.class; obj.class; obj.class; obj.class; + obj.class; obj.class; obj.class; obj.class; obj.class; obj.class; obj.class; obj.class; obj.class; obj.class; + obj.class; obj.class; obj.class; obj.class; obj.class; obj.class; obj.class; obj.class; obj.class; obj.class; + obj.class; obj.class; obj.class; obj.class; obj.class; obj.class; obj.class; obj.class; obj.class; obj.class; + end + end + + class Obj + end + obj = Obj.new + + singleton = Obj.new + def singleton.bar + end + + extended = Obj.new + 2.times do + extended.extend Module.new + end + + immediate = 1.4 +benchmark: + obj: get_class(obj) + extended: get_class(extended) + singleton: get_class(singleton) + immediate: get_class(immediate) +loop_count: 1000 diff --git a/benchmark/object_id.yml b/benchmark/object_id.yml new file mode 100644 index 0000000000..2bd52b923f --- /dev/null +++ b/benchmark/object_id.yml @@ -0,0 +1,4 @@ +benchmark: + baseline: "Object.new" + object_id: "Object.new.object_id" +# loop_count: 100000 diff --git a/benchmark/pathname.yml b/benchmark/pathname.yml new file mode 100644 index 0000000000..bcf3011eab --- /dev/null +++ b/benchmark/pathname.yml @@ -0,0 +1,15 @@ +prelude: | + abs = Pathname("/a") + rel = Pathname("a") + p1 = Pathname.new('foo/././././bar') + p2 = Pathname.new('foo/bar/./../..') + p3 = Pathname.new('foo/bar/zot') +benchmark: + p1+p2: p1+p2 + abs.root?: abs.root? + rel.root?: rel.root? + abs.absolute?: abs.absolute? + rel.absolute?: rel.absolute? + p1.cleanpath: p1.cleanpath + p2.cleanpath: p2.cleanpath + relative_path_from: p3.relative_path_from('foo/bar/qux/quax') diff --git a/benchmark/ractor_string_fstring.yml b/benchmark/ractor_string_fstring.yml new file mode 100644 index 0000000000..14b92d8fd8 --- /dev/null +++ b/benchmark/ractor_string_fstring.yml @@ -0,0 +1,18 @@ +type: lib/benchmark_driver/runner/ractor +benchmark: + ractor_fstring_random: | + i = 0 + str = "same".dup + while i < 2000000 + -(i.to_s.freeze) + i += 1 + end + ractor_fstring_same: | + i = 0 + str = "same".dup + while i < 2000000 + -str + i += 1 + end +loop_count: 1 +ractor: 4 diff --git a/benchmark/set.yml b/benchmark/set.yml new file mode 100644 index 0000000000..061509cb1f --- /dev/null +++ b/benchmark/set.yml @@ -0,0 +1,261 @@ +prelude: | + # First 1000 digits of pi + pi = <<~END.gsub(/\D/, '') + 31415926535897932384626433832795028841971693993751058209749445923078164062862089 + 98628034825342117067982148086513282306647093844609550582231725359408128481117450 + 28410270193852110555964462294895493038196442881097566593344612847564823378678316 + 52712019091456485669234603486104543266482133936072602491412737245870066063155881 + 74881520920962829254091715364367892590360011330530548820466521384146951941511609 + 43305727036575959195309218611738193261179310511854807446237996274956735188575272 + 48912279381830119491298336733624406566430860213949463952247371907021798609437027 + 70539217176293176752384674818467669405132000568127145263560827785771342757789609 + 17363717872146844090122495343014654958537105079227968925892354201995611212902196 + 08640344181598136297747713099605187072113499999983729780499510597317328160963185 + 95024459455346908302642522308253344685035261931188171010003137838752886587533208 + 38142061717766914730359825349042875546873115956286388235378759375195778185778053 + 21712268066130019278766111959092164201989380952572010654505906988788448549 + END + array1 = 10.times.flat_map do |i| + pi[i...].chars.each_slice(10).map(&:join) + end + array2 = array1.map(&:reverse) + array1.map!(&:to_i) + array2.map!(&:to_i) + a1 = array1[...10] + a2 = array1[...100] + a3 = array1 + oa1 = array2[...10] + oa2 = array2[...100] + oa3 = array2 + s0 = Set.new + s0 = Set.new + s1 = Set.new(a1) + s2 = Set.new(a2) + s3 = Set.new(a3) + o0 = Set.new + o1 = Set.new(array2[...10]) + o2 = Set.new(array2[...100]) + o3 = Set.new(array2) + d0 = s0.dup + d1 = s1.dup + d2 = s2.dup + d3 = s3.dup + ss1 = s1 - a1[-1..-1] + ss2 = s2 - a2[-1..-1] + ss3 = s3 - a3[-1..-1] + os1 = o1 - oa1[-1..-1] + os2 = o2 - oa2[-1..-1] + os3 = o3 - oa3[-1..-1] + member = a1.first + cbi = s0.dup.compare_by_identity + ns = Set[s3, o3, d3] + set_subclass = Class.new(Set) + +benchmark: + new_0: Set.new + new_10: Set.new(a1) + new_100: Set.new(a2) + new_1000: Set.new(a3) + aref_0: Set[] + aref_10: Set[*a1] + aref_100: Set[*a2] + aref_1000: Set[*a3] + amp_0: s0 & o0 + amp_10: s1 & o1 + amp_100: s2 & o2 + amp_1000: s3 & o3 + amp_same_0: s0 & d0 + amp_same_10: s1 & d1 + amp_same_100: s2 & d2 + amp_same_1000: s3 & d3 + minus_0: s0 - o0 + minus_10: s1 - o1 + minus_100: s2 - o2 + minus_1000: s3 - o3 + minus_same_0: s0 - d0 + minus_same_10: s1 - d1 + minus_same_100: s2 - d2 + minus_same_1000: s3 - d3 + spaceship_0: s0 <=> o0 + spaceship_diff_10: s1 <=> o1 + spaceship_diff_100: s2 <=> o2 + spaceship_diff_1000: s2 <=> o3 + spaceship_sub_10: s1 <=> ss1 + spaceship_sub_100: s2 <=> ss2 + spaceship_sub_1000: s2 <=> ss3 + spaceship_sup_10: ss1 <=> s1 + spaceship_sup_100: ss2 <=> s2 + spaceship_sup_1000: ss2 <=> s3 + eq_0: s0 == o0 + eq_10: s1 == o1 + eq_100: s2 == o2 + eq_1000: s3 == o3 + eq_same_0: s0 == d0 + eq_same_10: s1 == d1 + eq_same_100: s2 == d2 + eq_same_1000: s3 == d3 + xor_0: s0 ^ o0 + xor_10: s1 ^ o1 + xor_100: s2 ^ o2 + xor_1000: s3 ^ o3 + xor_same_0: s0 ^ d0 + xor_same_10: s1 ^ d1 + xor_same_100: s2 ^ d2 + xor_same_1000: s3 ^ d3 + pipe_0: s0 | o0 + pipe_10: s1 | o1 + pipe_100: s2 | o2 + pipe_1000: s3 | o3 + pipe_same_0: s0 | d0 + pipe_same_10: s1 | d1 + pipe_same_100: s2 | d2 + pipe_same_1000: s3 | d3 + add: a3.each { s0.add(it) } + add_exist: a3.each { s3.add(it) } + addq: a3.each { s0.add?(it) } + addq_exist: a3.each { s3.add?(it) } + classify_0: s0.classify { it } + classify_10: s1.classify { it & 2 } + classify_100: s2.classify { it & 8 } + classify_1000: s3.classify { it & 32 } + clear: s0.clear + collect_0: s0.collect! { it } + collect_10: s1.collect! { it } + collect_100: s2.collect! { it } + collect_1000: s3.collect! { it } + compare_by_identity_0: s0.dup.compare_by_identity + compare_by_identity_10: s1.dup.compare_by_identity + compare_by_identity_100: s2.dup.compare_by_identity + compare_by_identity_1000: s3.dup.compare_by_identity + compare_by_identityq_false: s0.compare_by_identity? + compare_by_identityq_true: cbi.compare_by_identity? + clone_0: s0.clone + clone_10: s1.clone + clone_100: s2.clone + clone_1000: s3.clone + delete: a3.each { s3.delete(it) } + delete_not_exist: a3.each { o3.delete(it) } + deleteq: a3.each { s3.delete?(it) } + deleteq_not_exist: a3.each { o3.delete?(it) } + delete_if_0: s0.delete_if { it } + delete_if_10: s1.delete_if { it & 2 == 0 } + delete_if_100: s2.delete_if { it & 2 == 0 } + delete_if_1000: s3.delete_if { it & 2 == 0 } + disjoint_0: s0.disjoint? o0 + disjoint_10: s1.disjoint? o1 + disjoint_100: s2.disjoint? o2 + disjoint_1000: s3.disjoint? o3 + disjoint_same_0: s0.disjoint? d0 + disjoint_same_10: s1.disjoint? d1 + disjoint_same_100: s2.disjoint? d2 + disjoint_same_1000: s3.disjoint? d3 + divide_1arity_0: s0.divide { true } + divide_1arity_10: s1.divide { it & 2 } + divide_1arity_100: s2.divide { it & 8 } + divide_1arity_1000: s3.divide { it & 32 } + divide_2arity_0: s0.divide { true } + divide_2arity_10: s1.divide { (_1 & 2) == (_2 & 2) } + divide_2arity_100: s2.divide { (_1 & 8) == (_2 & 8) } + divide_2arity_1000: s3.divide { (_1 & 32) == (_2 & 32) } + dup_0: s0.dup + dup_10: s1.dup + dup_100: s2.dup + dup_1000: s3.dup + each_0: s0.each { it } + each_10: s1.each { it } + each_100: s2.each { it } + each_1000: s3.each { it } + empty_true: s0.empty? + empty_false: s3.empty? + flatten: ns.flatten + flattenb: ns.flatten! + include_true_0: s0.include? member + include_true_10: s1.include? member + include_true_100: s2.include? member + include_true_1000: s3.include? member + include_false_0: s0.include?(-1) + include_false_10: s1.include?(-1) + include_false_100: s2.include?(-1) + include_false_1000: s3.include?(-1) + intersect_0: s0.intersect? o0 + intersect_10: s1.intersect? o1 + intersect_100: s2.intersect? o2 + intersect_1000: s3.intersect? o3 + intersect_same_0: s0.intersect? d0 + intersect_same_10: s1.intersect? d1 + intersect_same_100: s2.intersect? d2 + intersect_same_1000: s3.intersect? d3 + join_0: s0.join + join_10: s1.join + join_100: s2.join + join_1000: s3.join + join_arg_0: s0.join "" + join_arg_10: s1.join "" + join_arg_100: s2.join "" + join_arg_1000: s3.join "" + keep_if_0: s0.keep_if { it } + keep_if_10: s1.keep_if { it & 2 == 0 } + keep_if_100: s2.keep_if { it & 2 == 0 } + keep_if_1000: s3.keep_if { it & 2 == 0 } + merge_set: s0.dup.merge(s3, o3) + merge_enum: s0.dup.merge(array1, array2) + proper_subset_0: s0.proper_subset? s0 + proper_subset_10: s1.proper_subset? ss1 + proper_subset_100: s2.proper_subset? ss2 + proper_subset_1000: s3.proper_subset? ss3 + proper_subset_false_10: s1.proper_subset? os1 + proper_subset_false_100: s2.proper_subset? os2 + proper_subset_false_1000: s3.proper_subset? os3 + proper_superset_0: s0.proper_superset? s0 + proper_superset_10: ss1.proper_superset? s1 + proper_superset_100: ss2.proper_superset? s2 + proper_superset_1000: ss3.proper_superset? s3 + proper_superset_false_10: os1.proper_superset? s1 + proper_superset_false_100: os2.proper_superset? s2 + proper_superset_false_1000: os3.proper_superset? s3 + reject_0: s0.reject! { it } + reject_10: s1.reject! { it & 2 == 0 } + reject_100: s2.reject! { it & 2 == 0 } + reject_1000: s3.reject! { it & 2 == 0 } + replace_0: s = Set.new; array1.each { s.replace(s0) } + replace_10: s = Set.new; array1.each { s.replace(s1) } + replace_100: s = Set.new; array1.each { s.replace(s2) } + replace_1000: s = Set.new; array1.each { s.replace(s3) } + reset_0: s0.reset + reset_10: s1.reset + reset_100: s2.reset + reset_1000: s3.reset + select_0: s0.select! { it } + select_10: s1.select! { it & 2 == 0 } + select_100: s2.select! { it & 2 == 0 } + select_1000: s3.select! { it & 2 == 0 } + size_0: s0.size + size_10: s1.size + size_100: s2.size + size_1000: s3.size + subtract_set: s3.dup.subtract(os3) + subtract_enum: s3.dup.subtract(oa3) + subtract_same_set: s3.dup.subtract(s3) + subtract_same_enum: s3.dup.subtract(a3) + subset_0: s0.subset? s0 + subset_10: s1.subset? ss1 + subset_100: s2.subset? ss2 + subset_1000: s3.subset? ss3 + subset_false_10: s1.subset? os1 + subset_false_100: s2.subset? os2 + subset_false_1000: s3.subset? os3 + superset_0: s0.superset? s0 + superset_10: ss1.superset? s1 + superset_100: ss2.superset? s2 + superset_1000: ss3.superset? s3 + superset_false_10: os1.superset? s1 + superset_false_100: os2.superset? s2 + superset_false_1000: os3.superset? s3 + to_a_0: s0.to_a + to_a_10: s1.to_a + to_a_100: s2.to_a + to_a_1000: s3.to_a + to_set_0: s0.to_set + to_set_10: s1.to_set + to_set_100: s2.to_set + to_set_1000: s3.to_set diff --git a/benchmark/string_casecmp.yml b/benchmark/string_casecmp.yml index 2354040a04..88a3555c8a 100644 --- a/benchmark/string_casecmp.yml +++ b/benchmark/string_casecmp.yml @@ -20,7 +20,9 @@ benchmark: casecmp-10: lstr10.casecmp(ustr10) casecmp-100: lstr100.casecmp(ustr100) casecmp-1000: lstr1000.casecmp(ustr1000) + casecmp-1000vs10: lstr1000.casecmp(ustr10) casecmp-nonascii1: lnonascii1.casecmp(unonascii1) casecmp-nonascii10: lnonascii10.casecmp(unonascii10) casecmp-nonascii100: lnonascii100.casecmp(unonascii100) casecmp-nonascii1000: lnonascii1000.casecmp(unonascii1000) + casecmp-nonascii1000vs10: lnonascii1000.casecmp(unonascii10) diff --git a/benchmark/string_codepoints.yml b/benchmark/string_codepoints.yml new file mode 100644 index 0000000000..6a07db7ce1 --- /dev/null +++ b/benchmark/string_codepoints.yml @@ -0,0 +1,9 @@ +prelude: | + mixed_ascii64 = ("a" * 63 + "\u{100}") * 2048 + mixed_ascii256 = ("a" * 255 + "\u{100}") * 512 + utf8_2byte = "\u{100}" * 65536 + +benchmark: + codepoints_mixed_ascii64: mixed_ascii64.codepoints + codepoints_mixed_ascii256: mixed_ascii256.codepoints + codepoints_utf8_2byte: utf8_2byte.codepoints diff --git a/benchmark/string_concat.yml b/benchmark/string_concat.yml index f11f95ee9a..c07fd21013 100644 --- a/benchmark/string_concat.yml +++ b/benchmark/string_concat.yml @@ -1,8 +1,8 @@ prelude: | CHUNK = "a" * 64 UCHUNK = "é" * 32 - SHORT = "a" * (GC::INTERNAL_CONSTANTS[:BASE_SLOT_SIZE] / 2) - LONG = "a" * (GC::INTERNAL_CONSTANTS[:BASE_SLOT_SIZE] * 2) + SHORT = "a" * (GC.stat_heap(0, :slot_size) / 2) + LONG = "a" * (GC.stat_heap(0, :slot_size) * 2) GC.disable # GC causes a lot of variance benchmark: binary_concat_7bit: | diff --git a/benchmark/string_fstring.yml b/benchmark/string_fstring.yml new file mode 100644 index 0000000000..cafef1f3fe --- /dev/null +++ b/benchmark/string_fstring.yml @@ -0,0 +1,16 @@ +benchmark: + fstring_random: | + i = 0 + str = "same".dup + while i < 5_000_000 + -(i.to_s.freeze) + i += 1 + end + fstring_same: | + i = 0 + str = "same".dup + while i < 10_000_000 + -str + i += 1 + end +loop_count: 1 diff --git a/benchmark/string_gsub.yml b/benchmark/string_gsub.yml index 0f964337dd..c26e1a6498 100644 --- a/benchmark/string_gsub.yml +++ b/benchmark/string_gsub.yml @@ -20,8 +20,19 @@ prelude: | } ESCAPE_PATTERN = Regexp.union(ESCAPED_CHARS.keys) + NO_MATCH_SHARED_STRING = ("a" * 100_000).freeze benchmark: + gsub_no_match_shared: | + str = NO_MATCH_SHARED_STRING.dup + str.gsub!("z", "x") + str + + sub_no_match_shared: | + str = NO_MATCH_SHARED_STRING.dup + str.sub!("z", "x") + str + escape: | str = STR.dup str.gsub!(ESCAPE_PATTERN, ESCAPED_CHARS) diff --git a/benchmark/string_memsearch.yml b/benchmark/string_memsearch.yml new file mode 100644 index 0000000000..cde363289a --- /dev/null +++ b/benchmark/string_memsearch.yml @@ -0,0 +1,75 @@ +prelude: | + # Haystacks of various sizes + small_hay = "a" * 256 + medium_hay = "a" * 4096 + large_hay = "a" * 65536 + + # Short needles (2-8 bytes) that exercise rb_memsearch_ss + needle_2 = "xy" + needle_4 = "xyzw" + needle_8 = "xyzwabcd" + + # Needle whose first byte is absent from the haystack (memchr fast-path) + # vs needle whose first byte is common (rolling hash comparison) + first_byte_absent = "x" + "a" * 3 + first_byte_common = "a" + "x" * 3 + + # Haystack with match at the end + hay_match_end = "a" * 4095 + "xy" + + # Haystack with match at the start + hay_match_start = "xy" + "a" * 4094 + + # Mixed content haystack (more realistic) + mixed_hay = (("abcdefghij" * 100) + "z") * 10 + +benchmark: + # === First byte absent from haystack (biggest win for rolling hash) === + index_first_byte_absent_small: | + small_hay.index(first_byte_absent) + index_first_byte_absent_medium: | + medium_hay.index(first_byte_absent) + index_first_byte_absent_large: | + large_hay.index(first_byte_absent) + + # === First byte common in haystack (stresses comparison loop) === + index_first_byte_common_small: | + small_hay.index(first_byte_common) + index_first_byte_common_medium: | + medium_hay.index(first_byte_common) + index_first_byte_common_large: | + large_hay.index(first_byte_common) + + # === Needle length variations (all absent) === + index_needle_2_absent: | + medium_hay.index(needle_2) + index_needle_4_absent: | + medium_hay.index(needle_4) + index_needle_8_absent: | + medium_hay.index(needle_8) + + # === Match at end of haystack === + index_match_at_end: | + hay_match_end.index(needle_2) + + # === Match at start of haystack === + index_match_at_start: | + hay_match_start.index(needle_2) + + # === include? (same code path) === + include_first_byte_absent: | + medium_hay.include?(first_byte_absent) + include_first_byte_common: | + medium_hay.include?(first_byte_common) + + # === byteindex === + byteindex_first_byte_absent: | + medium_hay.byteindex(first_byte_absent) + byteindex_first_byte_common: | + medium_hay.byteindex(first_byte_common) + + # === Mixed/realistic haystack === + index_mixed_absent: | + mixed_hay.index(needle_4) + index_mixed_present: | + mixed_hay.index("ijab") diff --git a/benchmark/string_scrub.yml b/benchmark/string_scrub.yml new file mode 100644 index 0000000000..4b5faaad8e --- /dev/null +++ b/benchmark/string_scrub.yml @@ -0,0 +1,48 @@ +prelude: | + + STRING_SIZE = 1024 + def duplicate_to_length(str, target_length) + return "" if target_length <= 0 + return str[0, target_length] if str.length >= target_length + + (str * ((target_length / str.length) + 1))[0, target_length] + end + base = "Hello \u{1f600} world! \u{00e9}\u{00f1}" + padding = duplicate_to_length(base, STRING_SIZE) + + valid_utf8 = (padding.b + "OK".b).force_encoding("UTF-8") + valid_utf8.valid_encoding? + unknown_but_valid_utf8 = valid_utf8.dup.b.force_encoding("UTF-8") + invalid_utf8 = (padding.b + "\x80\xFF".b).force_encoding("UTF-8") + invalid_utf8.valid_encoding? + unknown_but_invalid_utf8 = (padding.b + "\x80\xFF".b).force_encoding("UTF-8") + + worst_case_utf8 = duplicate_to_length("\u{1f600}\u{00e9}\u{00f1}", STRING_SIZE).b.force_encoding("UTF-8") + + unknown_but_valid_utf8_worst_case = worst_case_utf8.dup.b.force_encoding("UTF-8") + unknown_but_invalid_utf8_worst_case = (worst_case_utf8.b + "\x80\xFF".b).force_encoding("UTF-8") + +benchmark: + scrub_known_valid: | + string = valid_utf8.dup + string.scrub! + + scrub_known_invalid: | + string = invalid_utf8.dup + string.scrub! + + scrub_unknown_but_valid_coderange: | + string = unknown_but_valid_utf8.dup + string.scrub! + + scrub_unknown_and_invalid_coderange: | + string = unknown_but_invalid_utf8.dup + string.scrub! + + scrub_unknown_but_valid_coderange_worst_case: | + string = unknown_but_valid_utf8_worst_case.dup + string.scrub! + + scrub_unknown_and_invalid_coderange_worst_case: | + string = unknown_but_invalid_utf8_worst_case.dup + string.scrub!
\ No newline at end of file diff --git a/benchmark/struct_accessor.yml b/benchmark/struct_accessor.yml index 61176cfdd4..d95240e2dd 100644 --- a/benchmark/struct_accessor.yml +++ b/benchmark/struct_accessor.yml @@ -1,5 +1,12 @@ prelude: | C = Struct.new(:x) do + def initialize(...) + super + @ivar = 42 + end + + attr_accessor :ivar + class_eval <<-END def r #{'x;'*256} @@ -15,11 +22,16 @@ prelude: | m = method(:x=) #{'m.call(nil);'*256} end + def r_ivar + #{'ivar;'*256} + end END end + C.new(nil) # ensure common shape is known obj = C.new(nil) benchmark: member_reader: "obj.r" member_writer: "obj.w" member_reader_method: "obj.rm" member_writer_method: "obj.wm" + ivar_reader: "obj.r_ivar" diff --git a/benchmark/time_now.yml b/benchmark/time_now.yml index f6d6a31489..9336877cd4 100644 --- a/benchmark/time_now.yml +++ b/benchmark/time_now.yml @@ -1,3 +1,4 @@ benchmark: - 'Time.now' - 'Time.now(in: "+09:00")' + - 'Time.now.year' diff --git a/benchmark/vm_ivar_get.yml b/benchmark/vm_ivar_get.yml index 9174af6965..1e0dad665f 100644 --- a/benchmark/vm_ivar_get.yml +++ b/benchmark/vm_ivar_get.yml @@ -1,17 +1,75 @@ prelude: | class Example def initialize + @levar = 1 @v0 = 1 @v1 = 2 @v3 = 3 + end + + def get_value_loop + sum = 0 + + i = 0 + while i < 100_000 + # 10 times to de-emphasize loop overhead + sum += @levar + sum += @levar + sum += @levar + sum += @levar + sum += @levar + sum += @levar + sum += @levar + sum += @levar + sum += @levar + sum += @levar + i += 1 + end + + return sum + end + + @levar = 1 + @v0 = 1 + @v1 = 2 + @v3 = 3 + + def self.get_value_loop + sum = 0 + + i = 0 + while i < 100_000 + # 10 times to de-emphasize loop overhead + sum += @levar + sum += @levar + sum += @levar + sum += @levar + sum += @levar + sum += @levar + sum += @levar + sum += @levar + sum += @levar + sum += @levar + i += 1 + end + + return sum + end + end + + class GenExample < Time + def initialize @levar = 1 + @v0 = 1 + @v1 = 2 + @v3 = 3 end def get_value_loop sum = 0 i = 0 - while i < 1000000 + while i < 100_000 # 10 times to de-emphasize loop overhead sum += @levar sum += @levar @@ -31,7 +89,12 @@ prelude: | end obj = Example.new + gen = GenExample.new benchmark: - vm_ivar_get: | + vm_ivar_get_on_obj: | obj.get_value_loop + vm_ivar_get_on_class: | + Example.get_value_loop + vm_ivar_get_on_generic: | + gen.get_value_loop loop_count: 100 diff --git a/benchmark/vm_ivar_set_on_instance.yml b/benchmark/vm_ivar_set_on_instance.yml index 91857b7742..6ce53a86ec 100644 --- a/benchmark/vm_ivar_set_on_instance.yml +++ b/benchmark/vm_ivar_set_on_instance.yml @@ -1,16 +1,44 @@ prelude: | class TheClass def initialize + @levar = 1 @v0 = 1 @v1 = 2 @v3 = 3 + end + + def set_value_loop + # 100k + i = 0 + while i < 100_000 + # 10 times to de-emphasize loop overhead + @levar = i + @levar = i + @levar = i + @levar = i + @levar = i + @levar = i + @levar = i + @levar = i + @levar = i + @levar = i + i += 1 + end + end + end + + class Generic < Time + def initialize @levar = 1 + @v0 = 1 + @v1 = 2 + @v3 = 3 end def set_value_loop - # 1M + # 100k i = 0 - while i < 1000000 + while i < 100_000 # 10 times to de-emphasize loop overhead @levar = i @levar = i @@ -28,8 +56,39 @@ prelude: | end obj = TheClass.new + gen_obj = Generic.new + + class SomeClass + @levar = 1 + @v0 = 1 + @v1 = 2 + @v3 = 3 + + def self.set_value_loop + # 100k + i = 0 + while i < 100_000 + # 10 times to de-emphasize loop overhead + @levar = i + @levar = i + @levar = i + @levar = i + @levar = i + @levar = i + @levar = i + @levar = i + @levar = i + @levar = i + i += 1 + end + end + end benchmark: vm_ivar_set_on_instance: | obj.set_value_loop + vm_ivar_set_on_generic: | + gen_obj.set_value_loop + vm_ivar_set_on_class: | + SomeClass.set_value_loop loop_count: 100 diff --git a/benchmark/vm_regexp.yml b/benchmark/vm_regexp.yml index 2aa3d94dbd..80541332b1 100644 --- a/benchmark/vm_regexp.yml +++ b/benchmark/vm_regexp.yml @@ -3,6 +3,12 @@ prelude: | benchmark: vm_regexp: | /hoge/ =~ str + vm_regexp_alternating: | + /hoge/ =~ str + /huge/ =~ str vm_regexp_invert: | str =~ /hoge/ + vm_regexp_invert_alternating: | + str =~ /hoge/ + str =~ /huge/ loop_count: 6000000 |
