The slow currentTimeMillis()
pzemtsov.github.io
pzemtsov.github.io
Also:
> The second value is the nanoseconds time, relative to the last second, shifted left by gtod->shift, Or, rather, it the time measured in some units, which becomes the nanosecond time when shifted right by gtod->shift, which, in our case, is 25. This is very high precision. One nanosecond divided by 225 is about 30 attoseconds. Perhaps, Linux kernel designers looked far ahead, anticipating the future when we’ll need such accuracy for our time measurement.
That's not what's going on. This is straightforward fixed point arithmetic, where the point just happens to be variable.
Because on most modern CPUs, clock frequency is extremely unstable: Turbo Boost, SpeedStep, Cool'n'Quiet, etc. These features make TSC not suitable for time measures.
And even on Intel, TSC is not reliable: https://lwn.net/Articles/388286/
So... >99% of servers and the vast majority of desktops?
Operating System Call System Call Time Mtev-variant Call Speedup
Linux 3.10.0 gettimeofday 35.7 ns/op 35.7 ns/op x1.00
Linux 3.10.0 gethrtime 119.8 ns/op 40.4 ns/op x2.96
OmniOS r151014 gettimeofday 304.6 ns/op 47.6 ns/op x6.40
OmniOS r151014 gethrtime 297.0 ns/op 39.9 ns/op x7.44
Here is the article: https://www.circonus.com/2016/09/time-but-faster/They also use TSC and (CPU-bound) background threads to update shared memory.
The implementation is available as C library for Linux and Illumos/OmniOS: https://github.com/circonus-labs/libmtev/blob/master/src/uti...
Also the, tsc CPU flag is not sufficient since early tsc implementations had issues that made them unusuable for walltime measurements. There's also constant_tsc and nonstop_tsc which are relevant because they indicate that tsc keeps ticking independent of frequency scaling and CPU low power modes, only then do they become usable for low-overhead userspace timekeeping.
hotspot and the linux vDSO generally take care of doing the right thing, depending on CPU flags, so the TSC is used for currentTimeMillis and nanoTime if it can determine that it is reliable, otherwise more expensive but accurate methods are used.
The article's own conclusion:
> The title isn’t completely correct: currentTimeMillis() isn’t always slow. However, there is a case when it is.
It only happens under circumstances where the TSCs are not used.
Benchmarks are a class of problems where the overall time doesn't matter, only the relative time (end - start). The fact that he's using System.currentTimeMillis for his start and end values doesn't give me confidence that he's very knowledgeable about what's to follow.
GDB (≥ 7) disables ASLR by default:
https://sourceware.org/gdb/current/onlinedocs/gdb/Starting.h...
I'd be worried about that claim. The article executes System.currentTimeMillis in a loop and sums the values. That takes care of dead code elimination, but it will still be subject to other distortionary effects (https://shipilev.net/#benchmarking-1). Maybe the fact that System.currentTimeMillis() is implemented using native code reduces some of the JIT induced benchmarking pitfalls, but I would still prefer to try and use JMH to test.
I'm also surprised that performance of System.currentTimeMillis can fall down so far under some circumstances. In one of Cliff Click's talks (maybe https://www.youtube.com/watch?v=-vizTDSz8NU), he mentions that some JVM benchmarks run it many millions of times a second.
We've recently run into slow performance with gettimeofday on Xen systems, such as EC2 (see: https://blog.packagecloud.io/eng/2017/03/08/system-calls-are...) This hits particularly hard if you're running instrumented code. Switching the clocksource from xen to tsc seems to help and we're still evaluating the ramifications of doing so (various sources including the above seem to suggest that tsc is unsafe and prone to clock skew, but others suggest that it's ok on more modern processors.)
https://developer.android.com/reference/android/os/SystemClo...
(Hitting the memory address will require some level of CPU level cache coherency protocol to kick in, but that's much cheaper than the equivalent mutex).
http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc....
As explained in the article, the Windows version is so small because it can only provide a resolution limited to the timer tick's resolution (2048 Hz in this case, but unless a program explicitly requests a high resolution it can be as low as 64 Hz IIRC). The cost to having a higher resolution is also a higher number of interrupts per second, which wastes power unnecessarily when you are idle[1].
Instead, as the atthe Linux version can provide a resolution limited only by the actual clocksource, which is nanoseconds in the case of the TSC. There is no background process that "updates the fields of this structure at regular intervals"; the updates only need to happen if the speed of the clock changes, for example due to an NTP update. While in the blog post it's measured to happen every millisecond, it can be configured to be lower without affecting the resolution of gettimeofday. There is also a special mode where you make it tick as slowly as 1 Hz on CPUs that have only one runnable process (if you have more than one runnable process, the scheduler needs to do context switches so the kernel cannot tick slowly).
[1] https://randomascii.wordpress.com/2013/07/08/windows-timer-r...
On relatively contemporary systems clock_gettime(CLOCK_REALTIME) will give you a nanosecond resolution wall clock time and the cost of going through JNI will cost you around 11-13ns, so with a simple native wrapper in java the cost is negligible. Although interesting, the whole study of the cost of obtaining the current time in Java with currentTimeMillis() with clock sources other than TSC is somewhat irrelevant.
We also have a similar 2 socket machine running a similar version of CentOS that has no problems using TSC. ¯\_(ツ)_/¯
This was a solid reminder my every day work sits atop the shoulders of giants and I am much appreciative.
So to implement a clock, they are using a different thread that updates a counter every millisecond or so? Isn't this a bit inefficient?
That's my theory.
So we're taking maybe 4M cycles per second used here. In practice, there's probably quite a bit of extra software overhead.