# High Dynamic Range (HDR) Histogram for Elixir
Get percentile values from stream of input. Each histogram has a configurable
min, max and precision value which control both the accuracy and memory
Learn more about [HDR Histograms](http://hdrhistogram.org/).
Writes (which ought to substantially outnumber reads) are in constant time
regardless of the configuration. Querying a histogram for a specific percentile,
or other metric (max value, min value) is slightly slower as the range and/or
precision grows. However, the implementation relies on write-optimized ETS
tables and thus operations are not serialized (beyond the locks used by ETS).
A histogram with a range of 1..1_000_000 and a precision of 3 takes roughly
The fist step involves creating a registry:
defmodule MyApp.Stats do
histogrex :load_user, min: 1, max: 10_000_000, precision: 3
histogrex :db_save_settings, min: 1, max: 10_000, precision: 2
And then adding this module as a `worker` to your application's supervisor
Values can then be recorded via the `record!` or `record` functions:
`min`, `max`, `total_count` and `value_at_quantile` are used to query the
It would be reasonable to have a GenServer dump these statistics to some log
ingestor every X seconds (10? 60?). This would be the only reader (though
concurrent reads are fully supported).
## Query Multiple Metrics at Once
If you're querying multiple metrics from an histogram at once, you can get a
slight performance boost by first getting an iterator and querying that.
There is no need to reset the iterator between calls:
it = Stats.iterator(:load_user)
The core histogram implementation is taken from [the Go version](https://github.com/codahale/hdrhistogram).
In order to maintain high write throughput, data access is not serialized through
a single process. Instead, write-optimized (via the `write_concurrency: true` option)
ETS tables are used. Functions are fully executed by the calling process. A write consists of as single `:ets.update_counter` call. A read consists of a single `:ets.lookup`.
It is possible to use multiple registries, though I suspect this will have no impact
unless thousands or metrics with high load are used. Benchmark it.
You can get the memory requirements of a registry by calling:
There's a bit of additional overhead for each histogram, but that will give you
the bulk of it.