Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add nanosecond resolution support on unixes to jl_gettimeofday and time() #45023

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 4 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ New library features
* `tempname` can now take a suffix string to allow the file name to include a suffix and include that suffix in
the uniquing checking ([#53474])
* `RegexMatch` objects can now be used to construct `NamedTuple`s and `Dict`s ([#50988])
* `time()` now has up to nanosecond resolution on Unix based platforms. ([#45023])

Standard library changes
------------------------
Expand Down Expand Up @@ -116,6 +117,9 @@ Standard library changes
Deprecated or removed
---------------------

* `Libc.TimeVal` is deprecated in favor of `Libc.TimeSpec`, which (generally) has higher resolution.
This deprecation follows the deprecation of `gettimeofday` in libc. ([#45023])

External dependencies
---------------------

Expand Down
12 changes: 12 additions & 0 deletions base/deprecated.jl
Original file line number Diff line number Diff line change
Expand Up @@ -397,3 +397,15 @@ end
@deprecate invpermute!!(a, p::AbstractVector{<:Integer}) invpermute!(a, p) false

# END 1.11 deprecations

# BEGIN 1.12 deprecations

@deprecate splat(x) Splat(x) false
# When removing this, also remove the struct & its constructor in Libc
function Libc.TimeVal()
Base.depwarn("`TimeVal()` is deprecated, use `TimeSpec()` and convert to microseconds manually or move to the higher precision directly instead.", :TimeVal)
convert(Libc.TimeVal, Libc.TimeSpec())
end

# END 1.12 deprecations

17 changes: 12 additions & 5 deletions base/libc.jl
Original file line number Diff line number Diff line change
Expand Up @@ -176,13 +176,20 @@ struct TimeVal
usec::Int64
end

function TimeVal()
tv = Ref{TimeVal}()
status = ccall(:jl_gettimeofday, Cint, (Ref{TimeVal},), tv)
struct TimeSpec
sec::Int64
nsec::Int64
Copy link
Sponsor Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
nsec::Int64
nsec::Int32

This is actually a Clong, which is sometimes Int32 and sometimes Int64, and sometimes just plain inconvenient to know what the platform will do. But if we use libuv, then it is explicitly Int32.

end

function TimeSpec()
ts = Ref{TimeSpec}()
status = ccall(:jl_gettimeofday, Cint, (Ref{TimeSpec},), ts)
Copy link
Sponsor Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could now go through libuv to avoid our need for a custom wrapper (and get 100-nanosecond precision)

Suggested change
status = ccall(:jl_gettimeofday, Cint, (Ref{TimeSpec},), ts)
UV_CLOCK_REALTIME = 1
status = ccall(:uv_clock_gettime, Cint, (Cint, Ref{TimeSpec},), UV_CLOCK_REALTIME, ts)

https://docs.libuv.org/en/v1.x/misc.html#c.uv_clock_gettime

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this have resolution guarantees? I can't seem to find that in the libuv docs 🤔

status != 0 && error("unable to determine current time: ", status)
return tv[]
return ts[]
end

Base.convert(::Type{TimeVal}, ts::TimeSpec) = TimeVal(ts.sec, div(ts.nsec, 1000))

"""
TmStruct([seconds])

Expand Down Expand Up @@ -280,7 +287,7 @@ time(tm::TmStruct) = Float64(ccall(:mktime, Int, (Ref{TmStruct},), tm))
"""
time() -> Float64

Get the system time in seconds since the epoch, with fairly high (typically, microsecond) resolution.
Get the system time in seconds since the epoch. Resolution is system dependent and limited to milliseconds on windows, with (up to) nanosecond resolution on other platforms.
"""
time() = ccall(:jl_clock_now, Float64, ())

Expand Down
15 changes: 8 additions & 7 deletions src/support/timefuncs.c
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
#if defined(_OS_WINDOWS_)
#include <sys/timeb.h>
#else
#include <sys/time.h>
#include <time.h>
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not 100% sure about this, but was required to get it to build on my system. CI will tell if this was a mistake 🤷

#include <sys/select.h>
#endif

Expand All @@ -23,12 +23,13 @@ JL_DLLEXPORT int jl_gettimeofday(struct jl_timeval *jtv)
struct __timeb64 tb;
errno_t code = _ftime64_s(&tb);
jtv->sec = tb.time;
jtv->usec = tb.millitm * 1000;
jtv->nsec = tb.millitm * 1000000;
#else
struct timeval tv;
int code = gettimeofday(&tv, NULL);
jtv->sec = tv.tv_sec;
jtv->usec = tv.tv_usec;
struct timespec ts;
int code = clock_gettime(CLOCK_REALTIME, &ts);
// TODO: warn/error on EINVAL/EOVERFLOW?
jtv->sec = ts.tv_sec;
jtv->nsec = ts.tv_nsec;
#endif
return code;
}
Expand All @@ -37,7 +38,7 @@ JL_DLLEXPORT double jl_clock_now(void)
{
struct jl_timeval now;
jl_gettimeofday(&now);
return now.sec + now.usec * 1e-6;
return now.sec + now.nsec * 1e-9;
}

void sleep_ms(int ms)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should the select in this sleep_ms really be used here? I think using a different API for sleeping like nanosleep would be more appropriate, as it can also consume seconds & nanoseconds like this PR enables via time().

Expand Down
2 changes: 1 addition & 1 deletion src/support/timefuncs.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ extern "C" {

struct jl_timeval {
int64_t sec; /* seconds */
int64_t usec; /* microseconds */
int64_t nsec; /* nanoseconds */
};

JL_DLLEXPORT int jl_gettimeofday(struct jl_timeval *jtv);
Expand Down
6 changes: 3 additions & 3 deletions stdlib/Dates/src/conversions.jl
Original file line number Diff line number Diff line change
Expand Up @@ -66,9 +66,9 @@ Return a `DateTime` corresponding to the user's system time including the system
locale.
"""
function now()
tv = Libc.TimeVal()
tm = Libc.TmStruct(tv.sec)
return DateTime(tm.year + 1900, tm.month + 1, tm.mday, tm.hour, tm.min, tm.sec, div(tv.usec, 1000))
ts = Libc.TimeSpec()
tm = Libc.TmStruct(ts.sec)
return DateTime(tm.year + 1900, tm.month + 1, tm.mday, tm.hour, tm.min, tm.sec, div(ts.nsec, 1_000_000))
end

"""
Expand Down