Skip to content

Instantly share code, notes, and snippets.

@ryanwinchester
Last active May 5, 2023 16:21
Show Gist options
  • Save ryanwinchester/e57e7c924e110a944fb94e894346fb82 to your computer and use it in GitHub Desktop.
Save ryanwinchester/e57e7c924e110a944fb94e894346fb82 to your computer and use it in GitHub Desktop.
Comparing `Enum.join/2` vs `Enum.instersperse/2` + `IO.iodata_to_binary/1`....

Question

I know IO.iodata_to_binary performs well and is used in string building situations where performance matters.

However, I had a question if Enum.intersperse/2 and then IO.iodata_to_binary/1 Is better than just Enum.join/2 + string interpolation.

Here are my results.

Results

Operating System: macOS
CPU Information: Intel(R) Core(TM) i5-9600K CPU @ 3.70GHz
Number of Available Cores: 6
Available memory: 32 GB
Elixir 1.14.3
Erlang 25.2.3

Benchmark suite executing with the following configuration:
warmup: 2 s
time: 5 s
memory time: 0 ns
reduction time: 0 ns
parallel: 1
inputs: Extra-Large, Large, Medium, Small
Estimated total run time: 1.40 min

Benchmarking intersperse + iodata_to_binary with input Extra-Large ...
Benchmarking intersperse + iodata_to_binary with input Large ...
Benchmarking intersperse + iodata_to_binary with input Medium ...
Benchmarking intersperse + iodata_to_binary with input Small ...
Benchmarking join + interpolate with input Extra-Large ...
Benchmarking join + interpolate with input Large ...
Benchmarking join + interpolate with input Medium ...
Benchmarking join + interpolate with input Small ...
Benchmarking join + join with input Extra-Large ...
Benchmarking join + join with input Large ...
Benchmarking join + join with input Medium ...
Benchmarking join + join with input Small ...

##### With input Extra-Large #####
Name                                     ips        average  deviation         median         99th %
join + interpolate                     0.163         6.14 s     ±0.00%         6.14 s         6.14 s
join + join                            0.134         7.44 s     ±0.00%         7.44 s         7.44 s
intersperse + iodata_to_binary        0.0812        12.31 s     ±0.00%        12.31 s        12.31 s

Comparison:
join + interpolate                     0.163
join + join                            0.134 - 1.21x slower +1.30 s
intersperse + iodata_to_binary        0.0812 - 2.00x slower +6.17 s

##### With input Large #####
Name                                     ips        average  deviation         median         99th %
intersperse + iodata_to_binary        368.98        2.71 ms   ±507.93%        2.27 ms        2.74 ms
join + interpolate                    338.50        2.95 ms    ±63.19%        2.84 ms        3.48 ms
join + join                           292.05        3.42 ms   ±455.98%        2.87 ms        3.43 ms

Comparison:
intersperse + iodata_to_binary        368.98
join + interpolate                    338.50 - 1.09x slower +0.24 ms
join + join                           292.05 - 1.26x slower +0.71 ms

##### With input Medium #####
Name                                     ips        average  deviation         median         99th %
join + interpolate                   10.81 K       92.52 μs  ±2567.59%       75.13 μs      117.02 μs
join + join                          10.52 K       95.06 μs  ±2873.40%       75.18 μs      120.45 μs
intersperse + iodata_to_binary       10.12 K       98.85 μs  ±2665.38%       81.06 μs      121.46 μs

Comparison:
join + interpolate                   10.81 K
join + join                          10.52 K - 1.03x slower +2.54 μs
intersperse + iodata_to_binary       10.12 K - 1.07x slower +6.32 μs

##### With input Small #####
Name                                     ips        average  deviation         median         99th %
join + interpolate                  496.88 K        2.01 μs  ±2641.54%        1.75 μs        3.00 μs
intersperse + iodata_to_binary      439.97 K        2.27 μs ±19344.53%        1.67 μs        2.21 μs
join + join                         388.93 K        2.57 μs ±17965.56%        1.82 μs        3.00 μs

Comparison:
join + interpolate                  496.88 K
intersperse + iodata_to_binary      439.97 K - 1.13x slower +0.26 μs
join + join                         388.93 K - 1.28x slower +0.56 μs

Conclusion

For small amounts of text they are pretty close. For extra-large, intersperse seems to hurt...

Mix.install([{:benchee, "~> 1.1"}])
text = """
Library for easy and nice (micro) benchmarking in Elixir. Benchee allows you to compare the performance of different pieces of code at a glance. It is also versatile and extensible, relying only on functions. There are also a bunch of plugins to draw pretty graphs and more!
"""
sm_text = String.split(text)
md_text = String.duplicate(text, 50) |> String.split()
lg_text = String.duplicate(text, 1000) |> String.split()
xl_text = String.duplicate(text, 1_000_000) |> String.split()
Benchee.run(
%{
"intersperse + iodata_to_binary" => fn input ->
foo = Enum.intersperse(input, ".")
IO.iodata_to_binary([foo, ?., "foo"])
end,
"join + interpolate" => fn input ->
foo = Enum.join(input, ".")
"#{foo}.foo"
end,
"join + join" => fn input ->
foo = Enum.join(input, ".")
Enum.join([foo, "foo"], ".")
end,
},
inputs: %{
"Small" => sm_text,
"Medium" => md_text,
"Large" => lg_text,
"Extra-Large" => xl_text
}
)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment