Using tools such as telemetry in a performant manner requires access to some sort of low cost timing primitive to be able to calculate intervals. The community seems to use a mixture of `System.system_time/0` and/or `System.monotinic_time/0` to accomplish this, though neither of these are built specifically for purpose, and neither are terribly performant on super hot paths [1].
It would be good to have first-class access to this in Elixir. Therefore, it is proposed to add the following to System:
* Add `System.perf_counter/0` & `System.perf_counter/1` as thin wrappers around `:os.perf_counter`, in the same manner as is currently done for `System.system_time` et al
* Add support for `perf_counter` to `System.convert_time_unit/3`. This is already supported in the underlying `:erlang.convert_time_unit/3`, it's just a matter of adding a clause to the private `normalize_time_unit` function.
Combined, these will allow for *significantly* faster interval calculation, which should help speed up all sorts of hot telemetry paths.
In practice, `perf_counter` is about twice as fast as `system_time`. A quick comparison (run on an M1 MacBook Air running 1.14/OTP 25.1):
```
Mix.install([{:benchee, "~> 1.0", only: :dev}])
Benchee.run(%{
system_time: &System.system_time/0,
monotonic_time: &System.monotonic_time/0,
perf_counter: &:os.perf_counter/0
})
Name ips average deviation median 99th %
perf_counter 54.03 M 18.51 ns ±439.35% 18.33 ns 20.01 ns
monotonic_time 28.10 M 35.58 ns ±2758.64% 33.30 ns 41.70 ns
system_time 23.44 M 42.67 ns ±8964.02% 37.50 ns 45.90 ns
Comparison:
perf_counter 54.03 M
monotonic_time 28.10 M - 1.92x slower +17.08 ns
system_time 23.44 M - 2.31x slower +24.16 ns
```
If folks are onboard I can get this implemented immediately.
m.