|
| 1 | +# frozen_string_literal: true |
| 2 | + |
| 3 | +require 'bundler/setup' |
| 4 | +require 'benchmark/ips' |
| 5 | +require 'hash_kit' |
| 6 | + |
| 7 | +helper = HashKit::Helper.new |
| 8 | + |
| 9 | +# --------------------------------------------------------------------------- |
| 10 | +# Hash builders – every benchmark iteration gets a fresh hash so that |
| 11 | +# default_proc assignment is always exercised (no short-circuit). |
| 12 | +# --------------------------------------------------------------------------- |
| 13 | + |
| 14 | +def build_small_flat_hash |
| 15 | + { 'name' => 'Alice', 'age' => 30, 'active' => true } |
| 16 | +end |
| 17 | + |
| 18 | +def build_large_flat_hash(n = 100) |
| 19 | + (0...n).each_with_object({}) { |i, h| h["key_#{i}"] = "value_#{i}" } |
| 20 | +end |
| 21 | + |
| 22 | +def build_nested_hash(depth = 5) |
| 23 | + hash = { 'leaf' => 'value' } |
| 24 | + depth.times { |i| hash = { "level_#{i}" => hash, "sibling_#{i}" => 'data' } } |
| 25 | + hash |
| 26 | +end |
| 27 | + |
| 28 | +def build_hash_with_arrays |
| 29 | + { |
| 30 | + 'users' => [ |
| 31 | + { 'name' => 'Alice', 'roles' => [{ 'id' => 1, 'name' => 'admin' }] }, |
| 32 | + { 'name' => 'Bob', 'roles' => [{ 'id' => 2, 'name' => 'editor' }] }, |
| 33 | + { 'name' => 'Carol', 'roles' => [{ 'id' => 3, 'name' => 'viewer' }] } |
| 34 | + ], |
| 35 | + 'meta' => { 'page' => 1, 'total' => 3 } |
| 36 | + } |
| 37 | +end |
| 38 | + |
| 39 | +def build_large_nested_hash(width = 20, depth = 4) |
| 40 | + return (0...width).each_with_object({}) { |i, h| h["key_#{i}"] = "val_#{i}" } if depth == 0 |
| 41 | + |
| 42 | + (0...width).each_with_object({}) do |i, h| |
| 43 | + h["node_#{i}"] = build_large_nested_hash(width, depth - 1) |
| 44 | + end |
| 45 | +end |
| 46 | + |
| 47 | +# --------------------------------------------------------------------------- |
| 48 | +# Benchmark suite |
| 49 | +# --------------------------------------------------------------------------- |
| 50 | + |
| 51 | +puts 'HashKit::Helper#indifferent! benchmark' |
| 52 | +puts "Ruby #{RUBY_VERSION} / benchmark-ips #{Benchmark::IPS::VERSION}" |
| 53 | +puts '=' * 60 |
| 54 | + |
| 55 | +Benchmark.ips do |x| |
| 56 | + x.config(warmup: 2, time: 5) |
| 57 | + |
| 58 | + # --- Scenario 1: small flat hash (3 keys) --- |
| 59 | + x.report('small flat hash (3 keys)') do |
| 60 | + helper.indifferent!(build_small_flat_hash) |
| 61 | + end |
| 62 | + |
| 63 | + # --- Scenario 2: large flat hash (100 keys) --- |
| 64 | + x.report('large flat hash (100 keys)') do |
| 65 | + helper.indifferent!(build_large_flat_hash(100)) |
| 66 | + end |
| 67 | + |
| 68 | + # --- Scenario 3: deeply nested hash (5 levels) --- |
| 69 | + x.report('nested hash (depth=5)') do |
| 70 | + helper.indifferent!(build_nested_hash(5)) |
| 71 | + end |
| 72 | + |
| 73 | + # --- Scenario 4: hash containing arrays of hashes --- |
| 74 | + x.report('hash with arrays') do |
| 75 | + helper.indifferent!(build_hash_with_arrays) |
| 76 | + end |
| 77 | + |
| 78 | + # --- Scenario 5: large nested hash (wide + deep) --- |
| 79 | + x.report('large nested (10x3)') do |
| 80 | + helper.indifferent!(build_large_nested_hash(10, 3)) |
| 81 | + end |
| 82 | + |
| 83 | + # --- Scenario 6: many sequential calls on small hashes --- |
| 84 | + x.report('50x sequential small hashes') do |
| 85 | + 50.times { helper.indifferent!(build_small_flat_hash) } |
| 86 | + end |
| 87 | + |
| 88 | + # --- Scenario 7: many sequential calls on medium hashes --- |
| 89 | + x.report('50x sequential nested hashes') do |
| 90 | + 50.times { helper.indifferent!(build_nested_hash(3)) } |
| 91 | + end |
| 92 | + |
| 93 | + x.compare! |
| 94 | +end |
0 commit comments