48

How can we easily time function calls in Elixir?

Is there any hidden switch in IEx to enable this?

Charles Okwuagwu
  • 10,538
  • 16
  • 87
  • 157
  • 5
    I don't think there's an option to do that. Probably the easier and quickest way to do that is Erlang's [`timer:tc/1-2-3`](http://www.erlang.org/doc/man/timer.html#tc-1). – whatyouhide Apr 16 '15 at 08:02
  • @whatyouhide not sure what i'm doing wrong `iex(54)> :timer.tc(Demo.sum 1000) ** (BadFunctionError) expected a function, got: 500500 (stdlib) timer.erl:165: :timer.tc/1` – Charles Okwuagwu Apr 16 '15 at 08:21
  • 6
    as you can see in the documentation for `timer:tc/1` I linked in the previous comment, if you're only passing one argument to `timer:tc` it has to be a function. Your example would be `:timer.tc(fn -> Demo.sum(1000) end)`. If you want to pass a module+function+arguments triplet, just go with `:timer.tc(Demo, :sum, [1000])`. – whatyouhide Apr 16 '15 at 08:23
  • 1
    @whatyouhide thanks :timer.tc(Demo, :sum, [1000]) – Charles Okwuagwu Apr 16 '15 at 08:30
  • 1
    @whatyouhide your comment should be an answer. – Overbryd Apr 16 '15 at 12:28

2 Answers2

85

You can write a module1 that can measure a given function. The following function returns the runtime of a given function in seconds:

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

Use it like this:

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

If you want to use that for Benchmarking, then there is another answer.

A better approach than measuring single run execution time is to measure operations per timeframe. This takes the code under test and executes it repeatingly within a given timeframe. This methodology yields more accurate results.

There is a library called Benchwarmer you can use for that:

Add Benchwarmer to your mix.exs

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

Simply pass an inline function:

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{...}]
Overbryd
  • 4,612
  • 2
  • 33
  • 33
  • 1
    Thanks, would be nice to have this build into the Iex tool. – Charles Okwuagwu Apr 16 '15 at 12:41
  • What is the unit of time that function |> :timer.tc |> elem(0) |> Kernel./(1_000_000) returns? Is it microseconds? – Sasha Fonseca Jul 21 '16 at 20:06
  • Benchwarmer version should be "~> 0.0.2" or installing dependencies will fail. – sougonde Aug 19 '16 at 12:51
  • @SashaFonseca it's in seconds, since `timer.tc` returns microseconds. – sk29910 Oct 18 '16 at 21:28
  • The Hex docs for IEx say: "The first one is that the code is truly evaluated and not compiled. This means that any benchmarking done in the shell is going to have skewed results. So never run any profiling nor benchmarks in the shell." Does this apply to these kind of imported libraries? Aka is this actually a realistic benchmark? – Justin DeMaris Jul 13 '17 at 14:22
  • So the code above can be taken to compile and run a realistic benchmark. I will update the answer, so that I do not use iex for running the example. – Overbryd Jul 14 '17 at 10:48
12

This is an answer to a 6 year old question. But I ran into this topic while searching for such a capability and the module suggestion implies one can do:

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

Thought I would drop that here in case someone else ended up searching for this. After all it's Erlang underneath. So you don't have to define another function in a module etc. time is in microseconds.

Sainik
  • 121
  • 1
  • 2