Skip to content

Commit 5a5bc9e

Browse files
committed
Optimize Number generators with arithmetic instead of string building
- number: generate the whole value with a single ranged rand instead of concatenating per-digit strings and re-parsing with to_i. - leading_zero_number: one rand plus rjust instead of one digit call per position; also handle digits < 2 explicitly. - decimal: build the right-hand side arithmetically (rand * 10 + non_zero_digit) instead of joining a per-digit array. - hexadecimal/binary: append with << into a pre-sized String instead of += which reallocates the string on every iteration. Benchmark (Ruby 3.4.9, arm64-darwin25, benchmark-ips): require 'benchmark/ips' require 'faker' Benchmark.ips do |x| x.config(warmup: 1, time: 2) x.report('number(digits: 10)') { Faker::Number.number(digits: 10) } x.report('leading_zero_number(digits: 10)') { Faker::Number.leading_zero_number(digits: 10) } x.report('decimal(5, 5)') { Faker::Number.decimal(l_digits: 5, r_digits: 5) } x.report('hexadecimal(digits: 32)') { Faker::Number.hexadecimal(digits: 32) } x.report('binary(digits: 32)') { Faker::Number.binary(digits: 32) } end Results: main: number(digits: 10) 516.487k (+/- 1.8%) i/s leading_zero_number(digits: 10) 532.109k (+/- 4.2%) i/s decimal(5, 5) 439.388k (+/- 2.0%) i/s hexadecimal(digits: 32) 177.646k (+/- 0.9%) i/s binary(digits: 32) 170.770k (+/- 0.8%) i/s this commit: number(digits: 10) 4.823M (+/- 3.4%) i/s (~9.3x) leading_zero_number(digits: 10) 4.551M (+/- 1.4%) i/s (~8.6x) decimal(5, 5) 1.663M (+/- 1.4%) i/s (~3.8x) hexadecimal(digits: 32) 230.063k (+/- 1.2%) i/s (~1.3x) binary(digits: 32) 220.621k (+/- 1.1%) i/s (~1.3x)
1 parent 9b07803 commit 5a5bc9e

1 file changed

Lines changed: 9 additions & 7 deletions

File tree

lib/faker/default/number.rb

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ def number(digits: 10)
1818
return rand(0..9).round if digits == 1
1919

2020
# Ensure the first digit is not zero
21-
([non_zero_digit] + generate(digits - 1)).join.to_i
21+
rand((10**(digits - 1))..(10**digits - 1))
2222
end
2323

2424
##
@@ -32,7 +32,9 @@ def number(digits: 10)
3232
#
3333
# @faker.version 1.0.0
3434
def leading_zero_number(digits: 10)
35-
"0#{(2..digits).collect { digit }.join}"
35+
return '0' if digits < 2
36+
37+
"0#{rand(10**(digits - 1)).to_s.rjust(digits - 1, '0')}"
3638
end
3739

3840
##
@@ -71,7 +73,7 @@ def decimal(l_digits: 5, r_digits: 2)
7173

7274
# Ensure the last digit is not zero
7375
# so it does not get truncated on converting to float
74-
r_d = generate(r_digits - 1).join + non_zero_digit.to_s
76+
r_d = ((rand(10**(r_digits - 1)) * 10) + non_zero_digit).to_s.rjust(r_digits, '0')
7577

7678
"#{l_d}.#{r_d}".to_f
7779
end
@@ -113,8 +115,8 @@ def digit
113115
#
114116
# @faker.version 1.0.0
115117
def hexadecimal(digits: 6)
116-
hex = ''
117-
digits.times { hex += rand(16).to_s(16) }
118+
hex = ::String.new('', capacity: digits)
119+
digits.times { hex << rand(16).to_s(16) }
118120
hex
119121
end
120122

@@ -128,8 +130,8 @@ def hexadecimal(digits: 6)
128130
#
129131
# @faker.version next
130132
def binary(digits: 4)
131-
bin = ''
132-
digits.times { bin += rand(2).to_s(2) }
133+
bin = ::String.new('', capacity: digits)
134+
digits.times { bin << rand(2).to_s(2) }
133135
bin
134136
end
135137

0 commit comments

Comments
 (0)