nanoid
nanoid copied to clipboard
experiment to improve perf
This PR tries using binary lookups to improve performance. For now I've only updated just one clause in the non-secure generator.
Before
$ MIX_ENV=bench mix run bench/generate.exs
Operating System: macOS
CPU Information: Apple M1
Number of Available Cores: 8
Available memory: 8 GB
Elixir 1.15.7
Erlang 26.1.2
Benchmark suite executing with the following configuration:
warmup: 2 s
time: 5 s
memory time: 0 ns
reduction time: 0 ns
parallel: 1
inputs: none specified
Estimated total run time: 7 s
Benchmarking generate ...
Name ips average deviation median 99th %
generate 104.87 K 9.54 μs ±80.33% 9.38 μs 12.21 μs
Profiling generate with eprof...
Profile results of #PID<0.174.0>
# CALLS % TIME µS/CALL
Total 1184 100.0 135 0.11
:rand.seed58/1 3 0.00 0 0.00
:rand.exsss_seed/1 1 0.00 0 0.00
:rand.mk_alg/1 1 0.00 0 0.00
:rand.seed_s/2 1 0.00 0 0.00
:rand.seed_s/1 1 0.00 0 0.00
:rand.seed/1 1 0.00 0 0.00
:erlang.system_time/0 1 0.00 0 0.00
:erlang.unique_integer/0 1 0.00 0 0.00
String.graphemes/1 1 0.00 0 0.00
:unicode_util.gc_extend/3 1 0.00 0 0.00
:unicode_util.gc_1/1 1 0.00 0 0.00
:unicode_util.cp/1 1 0.00 0 0.00
Nanoid.NonSecure.generate/1 1 0.00 0 0.00
Nanoid.NonSecure.generate/0 1 0.00 0 0.00
Enum.reduce/3 1 0.00 0 0.00
Enum.map/2 1 0.00 0 0.00
Enum.join/2 1 0.00 0 0.00
Enum.join/1 1 0.00 0 0.00
Range.new/2 1 0.00 0 0.00
Nanoid.Configuration.default_size/0 1 0.00 0 0.00
Nanoid.Configuration.default_alphabet/0 1 0.00 0 0.00
anonymous fn/0 in :elixir_compiler_1.__FILE__/1 1 0.00 0 0.00
:rand.exsss_uniform/2 21 0.74 1 0.05
:rand.seed_get/0 21 0.74 1 0.05
:rand.uniform_s/2 21 0.74 1 0.05
:rand.default_seed/0 1 0.74 1 1.00
:erlang.phash2/1 1 0.74 1 1.00
:erlang.iolist_to_binary/1 1 0.74 1 1.00
Nanoid.NonSecure.generator/2 2 0.74 1 0.50
Enum.entry_to_string/1 21 0.74 1 0.05
:erlang.apply/2 1 1.48 2 2.00
anonymous fn/3 in Nanoid.NonSecure.generator/2 21 1.48 2 0.10
Enum.reduce_range/5 11 1.48 2 0.18
:rand.seed_put/1 22 2.22 3 0.14
Enum.random_integer/2 21 2.22 3 0.14
:rand.splitmix64_next/1 3 2.96 4 1.33
:erlang.put/2 22 2.96 4 0.18
Enum."-map/2-lists^map/1-1-"/2 22 2.96 4 0.18
:rand.uniform/1 21 3.70 5 0.24
Enum.random/1 21 3.70 5 0.24
String.do_graphemes/1 65 8.89 12 0.18
:unicode_util.gc/1 65 23.70 32 0.49
Enum.drop_list/2 776 36.30 49 0.06
After
$ MIX_ENV=bench mix run bench/generate.exs
Compiling 1 file (.ex)
Operating System: macOS
CPU Information: Apple M1
Number of Available Cores: 8
Available memory: 8 GB
Elixir 1.15.7
Erlang 26.1.2
Benchmark suite executing with the following configuration:
warmup: 2 s
time: 5 s
memory time: 0 ns
reduction time: 0 ns
parallel: 1
inputs: none specified
Estimated total run time: 7 s
Benchmarking generate ...
Name ips average deviation median 99th %
generate 651.25 K 1.54 μs ±1603.41% 1.42 μs 1.75 μs
Profiling generate with eprof...
Profile results of #PID<0.187.0>
# CALLS % TIME µS/CALL
Total 193 100.0 48 0.25
:rand.seed58/1 3 0.00 0 0.00
:rand.exsss_seed/1 1 0.00 0 0.00
:rand.mk_alg/1 1 0.00 0 0.00
:rand.seed_s/2 1 0.00 0 0.00
:rand.default_seed/0 1 0.00 0 0.00
:rand.seed_s/1 1 0.00 0 0.00
:rand.seed/1 1 0.00 0 0.00
:erlang.system_time/0 1 0.00 0 0.00
:erlang.unique_integer/0 1 0.00 0 0.00
Nanoid.NonSecure.generator/2 1 0.00 0 0.00
Nanoid.NonSecure.generate/1 1 0.00 0 0.00
Nanoid.NonSecure.generate/0 1 0.00 0 0.00
Nanoid.Configuration.default_size/0 1 0.00 0 0.00
Nanoid.Configuration.default_alphabet/0 1 0.00 0 0.00
anonymous fn/0 in :elixir_compiler_1.__FILE__/1 1 0.00 0 0.00
:rand.exsss_uniform/2 21 2.08 1 0.05
:rand.seed_get/0 21 2.08 1 0.05
:rand.uniform_s/2 21 2.08 1 0.05
:erlang.phash2/1 1 2.08 1 1.00
:binary.at/2 21 2.08 1 0.05
:rand.seed_put/1 22 4.17 2 0.09
:erlang.apply/2 1 4.17 2 2.00
:rand.splitmix64_next/1 3 10.42 5 1.67
:rand.uniform/1 21 10.42 5 0.24
:erlang.put/2 22 18.75 9 0.41
Nanoid.NonSecure._generate/2 22 41.67 20 0.91
If there is interest in this approach I can update the other functions and add workarounds for the unicode limitation.