在Elixir中,我们如何轻松计时函数调用?

49

我们如何在Elixir中轻松地计时函数调用?

IEx中是否有任何隐藏的开关可以启用此功能?


5
我认为没有这样的选项。可能最简单和最快的方法是使用 Erlang 的 timer:tc/1-2-3 函数。 - whatyouhide
@whatyouhide 不确定我做错了什么。iex(54)> :timer.tc(Demo.sum 1000) ** (BadFunctionError) 预期一个函数,却得到了:500500 (stdlib) timer.erl:165: :timer.tc/1 - Charles Okwuagwu
6
在我之前的评论中链接的timer:tc/1的文档中可以看到,如果您只向timer:tc传递一个参数,那么它必须是一个函数。您的示例将是:timer.tc(fn -> Demo.sum(1000) end)。如果您想要传递模块+函数+参数三元组,请使用:timer.tc(Demo, :sum, [1000]) - whatyouhide
1
@whatyouhide 谢谢:timer.tc(Demo, :sum, [1000]) - Charles Okwuagwu
1
@whatyouhide,您的评论应该是一个答案。 - Overbryd
2个回答

87

你可以编写一个模块1来测量给定函数的运行时间。下面的函数返回给定函数的运行时间(以秒为单位):

defmodule Benchmark do
  def measure(function) do
    function
    |> :timer.tc
    |> elem(0)
    |> Kernel./(1_000_000)
  end
end

使用方法如下:

iex> Benchmark.measure(fn -> 123456*654321 end)
9.0e-6

如果您想用于基准测试,则有另一个答案。

比测量单次执行时间更好的方法是在给定时间段内测量每个操作次数。这需要将被测代码重复执行多次。这种方法可以得到更准确的结果。

有一个名为 Benchwarmer 的库可供使用:

将 Benchwarmer 添加到您的 mix.exs 文件中:

def deps do
  [ { :benchwarmer, "~> 0.0.2" } ]
end

只需传递一个内联函数:

iex> Benchwarmer.benchmark fn -> 123456*654321 end
*** #Function<20.90072148/0 in :erl_eval.expr/5> ***
1.2 sec     2M iterations   0.61 μs/op

[%Benchwarmer.Results{...}]

1
谢谢,如果能将这个集成到Iex工具中会很不错。 - Charles Okwuagwu
函数 |> :timer.tc |> elem(0) |> Kernel./(1_000_000) 返回的时间单位是什么?是微秒吗? - Sasha Fonseca
替补版本应为“~> 0.0.2”,否则安装依赖项将失败。 - sougonde
@SashaFonseca 这是秒,因为 timer.tc 返回微秒。 - sk29910
IEx的Hex文档中提到:“第一点是代码真正被评估而不是编译。这意味着在shell中进行的任何基准测试都会产生偏差的结果。因此,永远不要在shell中运行任何分析或基准测试。”这是否适用于这些导入的库?也就是说,这实际上是一个现实的基准测试吗? - Justin DeMaris
所以上面的代码可以被拿来编译和运行一个实际的基准测试。我会更新答案,这样我就不使用iex来运行示例了。 - Overbryd

16

这是对一个六岁问题的答案。但我在搜索这样一个功能时遇到了这个话题,模块建议意味着可以做到:

{time, your_func_result} = :timer.tc(&your_func/arity, [your_func-args])

如果有其他人搜索这个问题,我想在这里留下一些内容。毕竟它是基于Erlang的。因此,您不必在模块中定义另一个函数等等,时间以微秒为单位。


嗯,Overbird在他的回答中包含了这个解决方案。 - Arek Kostrzeba

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接