Counter Functions
This module provides a set of functions to do operations towards shared mutable counter variables. The implementation does not utilize any software level locking, which makes it very efficient for concurrent access. The counters are organized into arrays with the following semantics:
-
Counters are 64 bit signed integers.
-
Counters wrap around at overflow and underflow operations.
Counters are initialized to zero and can then only be written to by adding or subtracting.
-
Write operations guarantee atomicity. No intermediate results can be seen from a single write operation.
-
Two types of counter arrays can be created with options
atomicsorwrite_concurrency. Theatomicscounters have good allround performance with nice consistent semantics whilewrite_concurrencycounters offers even better concurrent write performance at the expense of some potential read inconsistencies. Seenew/2. -
Indexes into counter arrays are one-based. A counter array of size N contains N counters with index from 1 to N.
Functions
new(Size, Opts) -> counters_ref()
Size = integer() >= 1Opts = [Opt]Opt = atomics | write_concurrency
Create a new counter array of counters.
Argument is a list of the following possible
options:
atomics (Default)Counters will be sequentially consistent. If write operation A is done sequentially before write operation B, then a concurrent reader may see none of them, only A, or both A and B. It cannot see only B.
write_concurrencyThis is an optimization to achieve very efficient concurrent
add and sub operations at the expense of potential read
inconsistency and memory consumption per counter.
Read operations may see sequentially inconsistent results with regard to concurrent write operations. Even if write operation A is done sequentially before write operation B, a concurrent reader may see any combination of A and B, including only B. A read operation is only guaranteed to see all writes done sequentially before the read. No writes are ever lost, but will eventually all be seen.
The typical use case for write_concurrency is when
concurrent calls to add and
sub toward the same counters
are very frequent, while calls to get
and put are much
less frequent. The lack of absolute read consistency must also be
acceptable.
Counters are not tied to the current process and are automatically garbage collected when they are no longer referenced.
sub(Ref, Ix, Decr) -> ok
Ref = counters_ref()Ix = Decr = integer()
Subtract from counter at index
.
put(Ref, Ix, Value) -> ok
Ref = counters_ref()Ix = Value = integer()
Write to counter at index
.
info(Ref) -> Info
Ref = counters_ref()Info = #{size := Size, memory := Memory}Size = Memory = integer() >= 0
Return information about a counter array in a map. The map has the following keys (at least):
sizeThe number of counters in the array.
memoryApproximate memory consumption for the array in bytes.