Time Goes On in Erlang
learnyousomeerlang.com
learnyousomeerlang.com
Often, when you enabled the `lcnt` subsystem to find lock conflicts in busy systems, you would run into the timer wheel lock. Removing that improves parallelism in the system considerably.
And once you decide to take a stab at the time API, why not solve other problems around it as well, now you are working on fixing parts of it?
I suspect that this is a common-enough sentiment that there are a lot of VM optimization people (insofar as there exist a lot of VM optimization people) who would have been interested in working on this problem even years ago, if the halo effect didn't prevent them from realizing it was there. Certainly some of the people working on removing locks from Python, Ruby, the JVM, etc. would have been intrigued by hacking locks out of BEAM.
The solution is elegant, but that doesn't necessarily mean it was low hanging. (Maybe it was for the authors, I can't speak for them).
Sometimes you can't extrapolate the amount of work out of the complexity of final solution. The relationship is often inverse.
The contention on `erlang:now()` was irritating, but not impossible to work around.
At any rate, these changes are very welcome.
The common caveat against its use still holds. The timer wheel is part of the VM and is not directly accessible by Erlang programs. Only through indirect use via a number of constructs/functions manipulating timers.
It's speed depends on what options you set. If you request `erlang:unique_integer([monotonic])` then you have to lock because you need to synchronize multiple cores (lock here means atomic updates on a 64bit integer, so "lock"). Without monotonic, each scheduler can have it's own ID and then you return (SchedID << 64) + Supply or something equivalent.
If you need unique integers over node reboots, then you need to store something else on persistent storage which tells you what generation count your system is currently at. Thus, you can discriminate older integers from newer integers. If you don't need monotonic, you can just do:
unique_id() ->
ID = erlang:unique_integer(),
Generation = read_generation(),
Node = node(),
{ID, Generation, Node}.
and use the triple as your unique ID. Default ordering on tuples, which is lexicographic, then easily makes sure you can compare such ID's and you also obtain a total order on the ID's.So every node has its own generation. Every generation starts at 1, and every time it restarts, you read the current generation and increment it?
The `unique_integer` must be strictly monotonic when given the monotonic option right? When there's no monotonic, that just means the subsequent id can be higher or lower, but not the same.
Given the triple {ID, Generation, Node}, is the Node id unique & different across restarts or the same?
It's the other way around actually. I always mix them up too. See e.g. https://en.wikipedia.org/wiki/Gravitational_time_dilation
Imagine a mechanical clock battling gravity to get its hands to move. Voila, instant mnemonic.
EDIT: Flying clocks around: https://en.wikipedia.org/wiki/Hafele%E2%80%93Keating_experim... If compared to a clock remaining on earth, then it depends on the direction the plane is flying which effect wins.
It seems like the prototypical task for the OS.
Per the original announcement about the r18 release -
If time correction is enabled, the Erlang runtime system
will make use of both OS system time and OS monotonic time,
in order to make adjustments of the frequency of the Erlang
monotonic clock.
You can check if your system has support for OS monotonic
time by calling erlang:system_info(os_monotonic_time_source),
and you can check if time correction is enabled on your system by
calling erlang:system_info(time_correction).
But that's not always enough, it isn't always available, and it's not always what you want, either. Per the end of the OP (and also the official release) -To find system time: erlang:system_time/0-1
To measure time differences: call erlang:monotonic_time/0-1 twice and subtract them
To define an absolute order between events on a node: erlang:unique_integer([monotonic]) Measure time and make sure an absolute order is defined: {erlang:monotonic_time(), erlang:unique_integer([monotonic])}
Create a unique name: erlang:unique_integer([positive]). Couple it with a node name if you want the value to be unique in a cluster, or try using UUIDv1