drujensen / fib

Performance Benchmark of top Github languages

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Elixir code issue

OvermindDL1 opened this issue · comments

Assuming wanting to keep the invalid non-TCO recursion of fib, then these issues remain:

  1. It's not being compiled with the compiler made for numerical work, rather the default compiler is built for IO work, need to choose the correct compiler for the correct task.
  2. Scripts are being executed as they are compiled, not being compiled ahead of time, elixir is not designed to be used as a scripting language, it is designed for long-running systems.
  3. VM start-up time is also being measured, however this will be ignored as it is also included in the other VM languages, which is putting them not on par with the pre-compiled languages.
  4. And as a side-note, such a test as Fib doesn't test multi-core parallel work, which languages, for example, such as Ruby, Python, Crystal, and so forth do very poorly at. Consequently BEAM VM languages (like Elixir and Erlang) are not designed for numerical work at all and are designed for that work to be slaved out to another language.

To fix issue 1 you need to specify which JIT to use for the given module (file):

╰─➤  # Normal timing, yes my computer is anciently slow:
╰─➤  time elixir Fib.exs
2971215073
elixir Fib.exs  115.85s user 0.12s system 100% cpu 1:55.89 total

╰─➤  # Specify to use the numerical JIT (HiPE) for this single module (file):
╰─➤  time ERL_COMPILER_OPTIONS='[native,{hipe, [o3]}]' elixir Fib.exs
2971215073
ERL_COMPILER_OPTIONS='[native,{hipe, [o3]}]' elixir Fib.exs  27.66s user 0.14s system 100% cpu 27.584 total

╰─➤  calc 27.66/115.85
        ~0.23875701337936987484

So it is roughly 24% the time taken of the one that is not using the numerical JIT

The second issue and so on is less of an issue, the first is the big speed change, but the rest are 'usage' issues.

However, just changing that one JIT that is used changes Elixir's result on the readme, if following the same ~24% change, then:

╰─➤  calc 69.101*0.23875701337936987484
        ~16.49834838152783772132

Puts it ahead of even NodeJS (I didn't actually expect that...).

Bonus issue: C++ is using the streaming operator to output the result along with a flush, that means that the program will freeze upon that call until the flush is resolved and confirmed by the operating system. This is not something that most of the languages are doing, thus it is artificially slowing down C++ by an amount based on the terminal being used and the OS being used. Also, in C++ nowadays generally use constexpr for such functions. However, passing in the call 'to' the program would prevent such optimizations anyway and should be used for all languages since hard-coding the call can perform unintended optimizations that would not occur in normal code flow.

Interesting. I was wondering why Elixir was so slow. I will update the benchmarks later today... after work ;-)

For the C++, is there a way to fix the output flush problem? Why would it be different than the other languages that has to write to the console?

For the C++, is there a way to fix the output flush problem? Why would it be different than the other languages that has to write to the console?

Fixing the flush would involve changing the output line from this:

  std::cout << fib(46) << std::endl;

To become this:

  std::cout << fib(46) << "\n";

Calling std::endl is exactly the same as calling "\n" << std::flush, which forces the program to pause at that point until it gets a notification from the OS that the flush is complete, whether if that's a file write to a hard drive or to a terminal at the end of the listening stdout pipe. Most languages don't do that by default from what I've run across, nor do even C++. In addition the C++ streaming functions << have a high overhead because they are not just streaming the output but 'transforming' it, I.E. it will actually output the number in a different format based on locale settings and all manner of stuff, if you don't need localization work then you shouldn't be using the streaming operators and should instead just replace that above line with:

  printf("%lu", fib(46));

And of course instead of #include <iostream> instead do #include <stdio.h>.

In general, if you need to do locale transformations, use the streaming functions, if you don't then use the C functions. Do not treat them as interchangeable because they are not and you can be quite surprised at the output you get (or read in) on other systems with other locale's. Even the C functions do some locale work and there are methods of doing low level output that is even faster, but printf will be the general method (or a modern format library) nowadays even if it does still have a bit of cost.

I updated the Elixir code to match the other examples and ran using the recommended compiler flags. Also the C and C++ are using puts instead of printf.