jj1bdx

jj1bdx

String concatenation of multiple pieces of string

Related question: Implications of String concatenation vs. IOList for ANSI color codes

I’m wondering the performance difference between

ANSI.red <> "red" <> ANSI.reset <> ANSI.green <> "green" <> ANSI.reset

and

:erlang.list_to_binary([ANSI.red, "red", ANSI.reset, ANSI.green, "green", ANSI.reset])

I guess the one with :erlang.list_to_binary/1 is faster, but I might be wrong. Any suggestion welcome.

Most Liked

gregvaughn

gregvaughn

Here’s one write-up of how IO lists can be helpful without flattening all the way down at the OS level: https://www.bignerdranch.com/blog/elixir-and-io-lists-part-2-io-lists-in-phoenix/

gregvaughn

gregvaughn

IO lists offer some amazing performance benefits, but if you’re new to Elixir, first focus on “make it work make it work right, make it work fast” in that order. Actually, calling :erlang.list_to_binary/1 explicitly is a bit of a red flag. The true speed comes from passing IO lists into the final methods that write to the device, whether that is an IO.puts or a socket, etc.

To focus on your explicit question, I’m not entirely sure. I’d expect the 2nd one to be faster, but you’d want to benchmark it to be certain. The first one will create a new binary with each <> operator, but I would hope the 2nd one would create a single binary and consecutively write each piece of the IO list into it.

jmitchell

jmitchell

@gregvaughn is absolutely right that correctness is the first priority. After that if you’re not satisfied with performance you need to measure, measure, measure.

Benchmarking

Thanks to your question I took the opportunity to try the bmark benchmarking tool. Here’s a quick benchmark test I wrote based on the direction in the project’s README and your suggested expressions.

defmodule StringContenation do
  use Bmark

  alias IO.ANSI
  
  bmark :binary_concat_operator, runs: 1000 do
    ANSI.red <> "red" <> ANSI.reset <> ANSI.green <> "green" <> ANSI.reset
    |> IO.puts
  end

  bmark :erlang_list_to_binary, runs: 1000 do
    :erlang.list_to_binary([ANSI.red, "red", ANSI.reset, ANSI.green, "green", ANSI.reset])
    |> IO.puts
  end

I’m piping to IO.puts here so we can compare these approaches against IO lists. By default bmark does 20 runs per benchmark block, but I’ve increased them to 1000 to get more statistically significant results.

Next I run mix bmark. As expected I see a bunch of redgreen lines printed and colored appropriately.

Results

The benchmark timing data are in the $PROJ_ROOT/results/ directory. To compare the two implementations I run

mix bmark.cmp results/stringcontenation.binary_concat_operator.results \
              results/stringcontenation.erlang_list_to_binary.results

And the results!

results/stringcontenation.binary_concat_operator.results: results/stringcontenation.erlang_list_to_binary.results:
35 50
27 37
74 73

65 53
49 63
103 65

76.677 → 142.257 (+85.53%) with p < 0.025
t = 2.2981012392583193, 1998 degrees of freedom

In this particular example, the :erlang.list_to_binary, on average, took 1.8553 times as long as the <>. Don’t interpret this to mean you should always use <>. The inputs in these examples are rather small. The README for the bmark project has a bit more detail on how to interpret these results.

Moar data

Now let’s try benchmarking a couple more approaches:

  bmark :io_list_all_known_data, runs: 1000 do
    [ANSI.red, "red", ANSI.reset, ANSI.green, "green", ANSI.reset]
    |> IO.puts
  end

  bmark :io_list_append_to_end, runs: 1000 do
    # Simulates converting an incoming stream of unknown data to
    # into an IO list and printing.
    [[[[[[ANSI.red], "red"], ANSI.reset], ANSI.green], "green"], ANSI.reset]
    |> IO.puts
  end

Let’s compare each of these new approaches against the <> results:

Left: :binary_concat_operator, Right: :io_list_all_known_data

65.195 → 68.618 (+5.25%) with p < 1
t = 0.48428845990199687, 1998 degrees of freedom

Interestingly <> is still faster, although we have little confidence about that because the p-value is as high as it can get!

Left: :binary_concat_operator, Right: :io_list_append_to_end

65.195 → 125.968 (+93.22%) with p < 0.025
t = 2.1281669445218134, 1998 degrees of freedom

<> crushes the deeply nested IO list, and we can be pretty confident about that for these particular inputs on my test machine. However, that may not be true for concatenating sequences of strings in general. To check that you’d need to do a lot more benchmarks with different kinds of inputs and ideally on several different kinds of machines.

Exercises for the reader

  1. Determine how long IO lists have to get before they beat <>, if at all.
  2. Does size of the binaries within the lists or the nesting depth affect the results, and if so how?
  3. Are any of these expressions reduced at compile-time? We’re benchmarking run-time speeds, so any work done by the compiler to optimize our inputs may explain surprising results.
  4. Do you suspect Elixir, Erlang, or any part of your cache hierarchy are giving you unrealistic results? How can you tell? Could you somehow reduce the influence of caching?
  5. How well do your benchmarks correspond to the way your code will be exercised in production? Do you have any data about production response times?

Where Next?

Popular in Questions Top

sen
Hi All, I set a environment variables in dev.exs , like below code. when i start server, how can i set the ${enable} value? thanks. d...
New
nobody
How to bind a phoenix app to a specific ip address? could not find anything about that, nowhere, unfortunately, but for me this is quite...
New
beno
I will often find my self writing things similar to: case some_value do nil -&gt; something() "" -&gt; something() _ -&gt; somethi...
New
Darmani72
If I have a post route which an argument: post /my_post_route/:my_param1, MyController.my_post_handler How would get the post params ...
New
baxterw3b
Hi guys, i’m new in the Elixir world, and i have to say, that i love it! i’m having some problem to understand anonymous functions with ...
New
stefanluptak
Hello everybody, usually, I use a 29" ultra-wide monitor for VSCode which can easily accomodate explorer (files panel) + file with code ...
New
sergio_101
I am VERY much an elixir newbie. I have taken one elixir course and one phoenix course on Udemy. During that course, I saw the instructor...
New
komlanvi
Hi everyone, I was playing with phoenix liveView but I run into an issue. I have a form and want to validate each input text when the te...
New
yawaramin
In the Dialyzer docs ( dialyzer — OTP 29.0.2 (dialyzer 6.0.1) ), there is a way to turn off a specific warning for a function: -dialyzer...
New
jaysoifer
Is there a way to rollback a specific migration and only that one (“skipping” all the other ones)? Would mix ecto.rollback -v 200809061...
New

Other popular topics Top

Darmani72
If I have a post route which an argument: post /my_post_route/:my_param1, MyController.my_post_handler How would get the post params ...
New
electic
Hi, I am new to Elixir. I am trying to use the DateTime component to insert a date into MySQL however the there seems to be no way to fo...
New
minhajuddin
I have seen a lot of code which picks the first element from a list using Enum.at(0) instead of List.first. Is there a reason why people ...
New
ovidiubadita
Hey all, I discovered Elixir and I love it. I always wanted to learn a functional programming and I intended to go for Haskell, but afte...
New
Qqwy
Original source of discussion: This topic on the Pragmatic Programmers’ Functional Web Development with Elixir, OTP, and Phoenix forum. ...
New
SoCreat
i’m a new one to elixir which editor can i use vs code? or atom? Thanks! :smiley:
New
sergio_101
I am VERY much an elixir newbie. I have taken one elixir course and one phoenix course on Udemy. During that course, I saw the instructor...
New
nobody
Hi! In PHP: $_SERVER[‘SERVER_ADDR’] - in Elixir? Searched the docs for ip address and the web, no good results. Thanks!
New
JorisKok
I have a server on AWS, and was running a load test using artillery. When looking at the Phoenix dashboard I see the Ports going to 100% ...
New
dokuzbir
I want to highlight html closing tags when i click a html tag. That works in .html files but doesnt work for html.eex templates. How can...
New

Latest on Elixir Forum

Elixir Forum

We're in Beta

About us Mission Statement