Opened 7 years ago
Closed 5 years ago
#11806 closed Bugs (wontfix)
microsec_clock::universal_time() is needlessly slow
| Reported by: | Owned by: | James E. King, III | |
|---|---|---|---|
| Milestone: | To Be Determined | Component: | date_time |
| Version: | Boost 1.55.0 | Severity: | Optimization |
| Keywords: | Cc: |
Description
The documentation for microsec_clock::universal_time() says:
Get the UTC time using a sub second resolution clock. On Unix systems this is implemented using GetTimeOfDay.
In fact it is implemented partly using gettimeofday() but also gmtime_r(). Once you unwrap all the layers, the flow is something like this:
sec,nsec = gettimeofday() date,time = gmtime(sec) sec,nsec = ptime(date,time) return sec+nsec/1000
The problem is that gmtime_r() is very slow, relative to the other operations involved here, and it is completely unnecessary. A call to gettimeofday() on Unix is all you need to implement universal_time().
Change History (2)
comment:1 by , 5 years ago
| Owner: | changed from to |
|---|---|
| Status: | new → assigned |
comment:2 by , 5 years ago
| Resolution: | → wontfix |
|---|---|
| Status: | assigned → closed |

I benchmarked calling the current implementation (boost 1.66.0) in release mode on linux (ubuntu artful) 1M times and each call takes less than 100ns:
test: // benchmark for trac 11806 { check("calling microsec_clock::universal_time 1000000 times", true); boost::timer::auto_cpu_timer t; for (size_t i = 0; i < 1000000; ++i) { ptime x = microsec_clock::universal_time(); } return 0; } results: jking@ubuntu:~/boost/libs/date_time/test$ ../../../bin.v2/libs/date_time/test/testmicrosec_time_clock.test/gcc-gnu-7/release/threadapi-pthread/testmicrosec_time_clock Pass :: calling microsec_clock::universal_time 1000000 times 0.088597s wall, 0.090000s user + 0.000000s system = 0.090000s CPU (101.6%) jking@ubuntu:~/boost/libs/date_time/test$ ../../../bin.v2/libs/date_time/test/testmicrosec_time_clock.test/gcc-gnu-7/release/threadapi-pthread/testmicrosec_time_clock Pass :: calling microsec_clock::universal_time 1000000 times 0.103956s wall, 0.100000s user + 0.000000s system = 0.100000s CPU (96.2%) jking@ubuntu:~/boost/libs/date_time/test$ ../../../bin.v2/libs/date_time/test/testmicrosec_time_clock.test/gcc-gnu-7/release/threadapi-pthread/testmicrosec_time_clock Pass :: calling microsec_clock::universal_time 1000000 times 0.096352s wall, 0.090000s user + 0.000000s system = 0.090000s CPU (93.4%)I then changed the code such that if the clock source is universal time (i.e. gmtime_r) passed into create_time, we take (what I thought would be a) shortcut:
#if defined(BOOST_HAS_GETTIMEOFDAY) timeval tv; if (-1 == gettimeofday(&tv, 0)) { //gettimeofday does not support TZ adjust on Linux. boost::throw_exception(std::runtime_error("gettimeofday failed")); } if (universal) { // if the source is UTC we can take a fast shortcut here instead // of calling gmtime_r, which is slow (see Boost Trac 11806) static date_type posix_epoch(1970, 1, 1); static uint64_t res = resolution_traits_type::res_adjust(); return time_type(date_type(1970, 1, 1), time_duration_type(0, 0, tv.tv_sec, (static_cast<uint64_t>(tv.tv_usec) * res) / 1000000ull)); } std::time_t t = tv.tv_sec; boost::uint32_t sub_sec = tv.tv_usec; #elif defined(BOOST_HAS_FTIME)This slowed down the program so much I didn't even let it finish. Asking for a ptime based on a large number of seconds is incredibly inefficient. So I am resolving this as wontfix, unless you have a better suggestion for implementing it.