为什么使用Integer.to_string比字符串中的整数快{}大约6倍?

时间:2016-02-05 06:30:45

标签: performance optimization elixir

我从练习中做了polindrom_products exersice并发现了有趣的东西。

第一版:

@doc """
Generates all palindrome products from an optionally given min factor (or 1) to a given max factor.
"""
@spec generate(non_neg_integer, non_neg_integer) :: map() 
def generate(max_factor, min_factor \\ 1) do
  palindromes = for a <- min_factor..max_factor,
                    b <- a..max_factor,
                    prod = a*b,
                    palindrome?(prod),
                    do: {prod, [a, b]}

  Enum.reduce(palindromes, %{}, fn {prod, pair}, acc ->
    Map.update(acc, prod, [pair], &(&1 ++ [pair]))
  end)
end

def palindrome?(n) do
  s = Integer.to_string(n)
  s == String.reverse(s)
end

第二版:

@doc """
Generates all palindrome products from an optionally given min factor (or 1) to a given max factor.
"""
@spec generate(non_neg_integer, non_neg_integer) :: map() 
def generate(max_factor, min_factor \\ 1) do
  palindromes = for a <- min_factor..max_factor,
                    b <- a..max_factor,
                    prod = a*b,
                    "#{prod}" == String.reverse("#{prod}"),
                    do: {prod, [a, b]}

  Enum.reduce(palindromes, %{}, fn {prod, pair}, acc ->
    Map.update(acc, prod, [pair], &(&1 ++ [pair]))
  end)
end

第一个版本比第二个版本快6倍左右。替换"#{prod}" == String.reverse("#{prod}")上的Integer.to_string(prod) == String.reverse(Integer.to_string(prod))可获得收益。

例如,使用此测试:

test "smallest palindromes from triple digit factors" do
  palindromes = Palindromes.generate(999, 100)
  assert palindromes |> Dict.keys |> Enum.sort |> hd == 10201
  assert palindromes[10201] == [[101, 101]]
end

第一个版本以0.7秒执行,第二个版本以4秒执行。我正在使用Elixir v1.1.1并在本地计算机上运行Sublime Text的代码。

这是什么原因?

1 个答案:

答案 0 :(得分:3)

首先,我总是对Elixir计划报告的计时结果持谨慎态度。除非您从代码中进行基准测试,否则您将包括BEAM VM的启动时间以及可能会显着扭曲结果的小代码。

然而,问题归结为原因

"#{prod}" == String.reverse("#{prod}")

慢得多
s = Integer.to_string(n)
s == String.reverse(s)

让我们拉开第一行

"#{prod}" 

- &GT;这实际上是2个函数,内部函数使用to_string协议将prod转换为字符串,然后进行字符串插值。因为它使用协议,而不是直接的Integer.to_string 在合并协议之前,它可能会变慢。

您在同一行中执行此操作两次,因此您最多需要4次函数调用。添加String.reverse和==,这是6个函数调用。

第二个版本正确缓存结果,只执行3个功能 调用。

如果您想进一步研究这些微基准测试,我强烈推荐使用benchfella库。 https://github.com/alco/benchfella