【发布时间】:2017-11-10 17:51:30
【问题描述】:
请帮我解决一个关于 Elixir 与 Ruby 性能的基准测试问题。
我尝试在两种语言中实现相同的阶乘,而 Ruby 显示出比 Elixir 更好的结果:
# ruby_factorial_with_iterator.rb
def factorial_with_iterator(n)
res = 1
(1..n).each{|time| res *= time}
res
end
p "factorial_with_iterator(200000)"
p factorial_with_iterator(200000)
运行后:
$ time ruby ruby_factorial_with_iterator.rb
real 0m18.378s
user 0m17.348s
sys 0m0.844s
还有两个 Elixir 示例:
# elixir_factorial_with_iterator.exs
defmodule FactorialWithIterator do
def of(n) do
Enum.reduce(1..n, 1, &*/2)
end
end
IO.puts "Factorial of 200000: "
IO.puts FactorialWithIterator.of(200000)
运行后:
$ time elixir elixir_factorial_with_iterator.exs
real 1m1.735s
user 1m1.556s
sys 0m0.104s
另一个例子:
# elixir_factorial_with_recursion.exs
defmodule FactorialWithRecursion do
def of(0), do: 1
def of(n) when n > 0 do
n * of(n - 1)
end
end
IO.puts "Factorial of 200000: "
IO.puts FactorialWithRecursion.of(200000)
运行后:
$ time elixir elixir_factorial_with_recursion.exs
real 1m7.149s
user 1m6.248s
sys 0m0.092s
为什么会有如此巨大的差异:Elixir - 1m1s,而 Ruby - 只有 18s?或者如何在 Elixir 中编写正确的迭代代码?
附: 环境:
- Ubuntu 16.04
- ruby 2.3.1p112(2016-04-26 修订版 54768)[x86_64-linux]
- Erlang/OTP 19 [erts-8.3] [source-d5c06c6] [64-bit] [smp:4:4] [async-threads:10] [hipe] [kernel-poll:false]
- 灵药 1.4.4
【问题讨论】:
-
数字运算is not one of Erlang's strengths。并发/并行是。
-
简单的答案是基准测试非常非常困难。我鼓励大家阅读 JMH JVM 基准测试工具的维护者之一的这封邮件:groups.google.com/d/msg/mechanical-sympathy/m4opvy4xq3U/… 是的,它是关于 JMH,是的,它是关于 JVM,但它实际上适用于所有现代高度上的所有基准测试工具——优化语言实现。您确实需要对现代高性能动态优化编译器、统计数据、硬件架构以及许多其他知识有深入的了解才能编写有意义的基准测试。
-
更不用说代码甚至没有做同样的事情。 Ruby 版本是一个命令式的、有副作用的、不纯的、有状态的循环。 Elixir 版本是折叠和递归函数,它们是三种截然不同的算法。
-
有趣的是,当我尝试并行化它时,绝大多数 CPU 时间都花在了最后几次乘法上,其中少数非常大的数字被相乘。看起来这不是 Elixir 本身,而是 Erlang 的 bignum 库缺少非常大的数字。
-
Elixir 被设计为一种编译语言,您在这里使用它的“脚本”功能(请参阅 .exs 扩展名),这不是它的主要目的。 Elixir 针对已经运行的 Erland VM 中的编译代码进行了优化,以实现最大速度,而不是运行脚本。 Ruby 是一种脚本语言,并针对该用例进行了优化。
标签: ruby performance compare elixir benchmarking