defmodule Tim do
@readme "README.md"
@external_resource @readme
@moduledoc_readme @readme
|> File.read!()
|> String.split("<!-- END HEADER -->")
|> Enum.fetch!(1)
|> String.trim()
@moduledoc """
#{@moduledoc_readme}
"""
alias Tim.Stats
@type unit :: :microsecond | :millisecond | :second | :minute | :hour
@default_opts %{n: 1, unit: :microsecond}
@doc """
Takes an Elixir expression and returns a map of timing stats and the evaluated result.
`time` has the following optional keyword arguments:
* `:n` - number of times the expression is evaluated to build statistics; defaults to 1
* `:unit` - unit of time for the timing statistics; `:microsecond` (default) | `:millisecond` | `:second` | `:minute` | `:hour`
"""
defmacro time(expr, opts \\ []) do
%{n: n, unit: unit} = merge_opts(@default_opts, opts)
scale = unit_to_scale(unit)
expr_string = Macro.to_string(expr)
quote do
{times, [result | _]} =
Enum.map(1..unquote(n), fn _ ->
:timer.tc(fn -> unquote(expr) end)
end)
|> Enum.unzip()
Map.merge(
%{expr: unquote(expr_string), n: unquote(n), result: result, unit: unquote(unit)},
Stats.collect(times, unquote(scale))
)
end
end
@doc """
Takes an Elixir expression and returns the evaluated result while also
applying `IO.inspect` to the map of timing stats.
`inspect` has the following optional keyword arguments:
* `:n` - number of times the expression is evaluated to build statistics (defaults to 1)
* `:unit` - unit of time for the timing statistics: `:microsecond` (default) | `:millisecond` | `:second` | `:minute` | `:hour`
* `:label` - a string that decorates the inspected output (passed to `IO.inspect`)
"""
defmacro inspect(expr, opts \\ []) do
quote do
unquote(expr)
|> Tim.time(unquote(opts))
|> then(fn %{result: result} = data ->
data
|> Map.delete(:result)
|> IO.inspect(label: unquote(Keyword.get(opts, :label)))
result
end)
end
end
defp unit_to_scale(:microsecond), do: 1
defp unit_to_scale(:millisecond), do: 1.0e-3
defp unit_to_scale(:second), do: 1.0e-6
defp unit_to_scale(:minute), do: 1.6667e-8
defp unit_to_scale(:hour), do: 2.7778e-10
defp merge_opts(defaults, opts) do
Map.merge(defaults, Enum.into(opts, %{}))
end
end