Skip to content

Commit 0601a80

Browse files
JuanVqzkonung
andcommitted
Step 1: Add Symbol vs frozen String vs String keys in a 1000-key Hash (#135)
From #135 by @konung: whether String keys cost more than Symbol keys, and whether freezing the String closes the gap. Symbol keys are the faster ones, both to build a Hash with and to read it by. Changes to the PR: - Its 1000-pair file measured two things, so it is split into hash/symbol-vs-string-keys-building.rb and hash/symbol-vs-string-keys-reading.rb. - Fixed keys and values instead of shuffle, rand and sample, so every run measures the same work. - Rank names: faster Symbol, fast frozen String, slow plain String. A comment says why frozen keys are close when building: a Hash copies and freezes a plain String key but keeps a frozen one as it is (checked on Ruby 2.1 and 4.0). - Not taken: the one-pair literal file ({symbol: 42} and friends). On Ruby 3.4 and 4.0 each literal compiles to one duphash instruction, a copy of a prebuilt Hash, so the reports measure the same work. It also used Ruby 2.2 syntax ('sym_str': 42) and called the wrong method in one report. - Not taken either: code/hash/.tool-versions, and the CONTRIBUTING typo fix, which main already has. No README entry: the results move to the results site. Co-authored-by: Nick Gorbikoff <72493+konung@users.noreply.github.com>
1 parent 3ff4aba commit 0601a80

2 files changed

Lines changed: 61 additions & 0 deletions

File tree

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
require 'benchmark/ips'
2+
3+
# The same 1000 keys, as Symbols, frozen Strings and plain Strings, each mapped to its index.
4+
SYMBOL_KEYS = (1..1000).map { |i| :"key_#{i}" }
5+
FROZEN_KEYS = (1..1000).map { |i| "key_#{i}".freeze }
6+
STRING_KEYS = (1..1000).map { |i| "key_#{i}" }
7+
8+
def build(keys)
9+
keys.each_with_index.map { |key, index| [key, index] }.to_h
10+
end
11+
12+
# Symbol and frozen String keys tie on Ruby 4.0.
13+
def faster
14+
build(SYMBOL_KEYS)
15+
end
16+
17+
# A Hash copies and freezes a plain String key, but keeps a frozen one as it is.
18+
def fast
19+
build(FROZEN_KEYS)
20+
end
21+
22+
def slow
23+
build(STRING_KEYS)
24+
end
25+
26+
Benchmark.ips do |x|
27+
x.report('Symbol Keys') { faster }
28+
x.report('Frozen Keys') { fast }
29+
x.report('String Keys') { slow }
30+
x.compare!
31+
end
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
require 'benchmark/ips'
2+
3+
# The same 1000 keys, as Symbols, frozen Strings and plain Strings, each mapped to its index.
4+
SYMBOL_KEYS = (1..1000).map { |i| :"key_#{i}" }
5+
FROZEN_KEYS = (1..1000).map { |i| "key_#{i}".freeze }
6+
STRING_KEYS = (1..1000).map { |i| "key_#{i}" }
7+
8+
SYMBOL_HASH = SYMBOL_KEYS.each_with_index.map { |key, index| [key, index] }.to_h
9+
FROZEN_HASH = FROZEN_KEYS.each_with_index.map { |key, index| [key, index] }.to_h
10+
STRING_HASH = STRING_KEYS.each_with_index.map { |key, index| [key, index] }.to_h
11+
12+
# Reads every key once.
13+
def faster
14+
SYMBOL_KEYS.each { |key| SYMBOL_HASH[key] }
15+
end
16+
17+
def fast
18+
FROZEN_KEYS.each { |key| FROZEN_HASH[key] }
19+
end
20+
21+
def slow
22+
STRING_KEYS.each { |key| STRING_HASH[key] }
23+
end
24+
25+
Benchmark.ips do |x|
26+
x.report('Symbol Keys') { faster }
27+
x.report('Frozen Keys') { fast }
28+
x.report('String Keys') { slow }
29+
x.compare!
30+
end

0 commit comments

Comments
 (0)