mirror of
https://github.com/janet-lang/janet
synced 2025-06-01 14:14:11 +00:00
Merge pull request #1146 from zevv/os-clock
Add clock sources to os/clock (:realtime, :monotonic, :cputime)
This commit is contained in:
commit
8680aef42f
@ -1278,14 +1278,32 @@ JANET_CORE_FN(os_time,
|
|||||||
}
|
}
|
||||||
|
|
||||||
JANET_CORE_FN(os_clock,
|
JANET_CORE_FN(os_clock,
|
||||||
"(os/clock)",
|
"(os/clock &opt source)",
|
||||||
"Return the number of whole + fractional seconds since some fixed point in time. The clock "
|
"Return the number of whole + fractional seconds of the requested clock source.\n\n"
|
||||||
"is guaranteed to be non-decreasing in real time.") {
|
"The `source` argument selects the clock source to use, when not specified the default "
|
||||||
|
"is `:realtime`:\n"
|
||||||
|
"- :realtime: Return the real (i.e., wall-clock) time. This clock is affected by discontinuous "
|
||||||
|
" jumps in the system time\n"
|
||||||
|
"- :monotonic: Return the number of whole + fractional seconds since some fixed point in "
|
||||||
|
" time. The clock is guaranteed to be non-decreasing in real time.\n"
|
||||||
|
"- :cputime: Return the CPU time consumed by this process (i.e. all threads in the process)\n") {
|
||||||
janet_sandbox_assert(JANET_SANDBOX_HRTIME);
|
janet_sandbox_assert(JANET_SANDBOX_HRTIME);
|
||||||
janet_fixarity(argc, 0);
|
janet_arity(argc, 0, 1);
|
||||||
(void) argv;
|
enum JanetTimeSource source = JANET_TIME_REALTIME;
|
||||||
|
if (argc == 1) {
|
||||||
|
JanetKeyword sourcestr = janet_getkeyword(argv, 0);
|
||||||
|
if (janet_cstrcmp(sourcestr, "realtime") == 0) {
|
||||||
|
source = JANET_TIME_REALTIME;
|
||||||
|
} else if (janet_cstrcmp(sourcestr, "monotonic") == 0) {
|
||||||
|
source = JANET_TIME_MONOTONIC;
|
||||||
|
} else if (janet_cstrcmp(sourcestr, "cputime") == 0) {
|
||||||
|
source = JANET_TIME_CPUTIME;
|
||||||
|
} else {
|
||||||
|
janet_panicf("expected :realtime, :monotonic, or :cputime, got %v", argv[0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
struct timespec tv;
|
struct timespec tv;
|
||||||
if (janet_gettime(&tv)) janet_panic("could not get time");
|
if (janet_gettime(&tv, source)) janet_panic("could not get time");
|
||||||
double dtime = tv.tv_sec + (tv.tv_nsec / 1E9);
|
double dtime = tv.tv_sec + (tv.tv_nsec / 1E9);
|
||||||
return janet_wrap_number(dtime);
|
return janet_wrap_number(dtime);
|
||||||
}
|
}
|
||||||
|
@ -875,34 +875,73 @@ int32_t janet_sorted_keys(const JanetKV *dict, int32_t cap, int32_t *index_buffe
|
|||||||
/* Clock shims for various platforms */
|
/* Clock shims for various platforms */
|
||||||
#ifdef JANET_GETTIME
|
#ifdef JANET_GETTIME
|
||||||
#ifdef JANET_WINDOWS
|
#ifdef JANET_WINDOWS
|
||||||
int janet_gettime(struct timespec *spec) {
|
#include <profileapi.h>
|
||||||
FILETIME ftime;
|
int janet_gettime(struct timespec *spec, enum JanetTimeSource source) {
|
||||||
GetSystemTimeAsFileTime(&ftime);
|
if (source == JANET_TIME_REALTIME) {
|
||||||
int64_t wintime = (int64_t)(ftime.dwLowDateTime) | ((int64_t)(ftime.dwHighDateTime) << 32);
|
FILETIME ftime;
|
||||||
/* Windows epoch is January 1, 1601 apparently */
|
GetSystemTimeAsFileTime(&ftime);
|
||||||
wintime -= 116444736000000000LL;
|
int64_t wintime = (int64_t)(ftime.dwLowDateTime) | ((int64_t)(ftime.dwHighDateTime) << 32);
|
||||||
spec->tv_sec = wintime / 10000000LL;
|
/* Windows epoch is January 1, 1601 apparently */
|
||||||
/* Resolution is 100 nanoseconds. */
|
wintime -= 116444736000000000LL;
|
||||||
spec->tv_nsec = wintime % 10000000LL * 100;
|
spec->tv_sec = wintime / 10000000LL;
|
||||||
|
/* Resolution is 100 nanoseconds. */
|
||||||
|
spec->tv_nsec = wintime % 10000000LL * 100;
|
||||||
|
} else if (source == JANET_TIME_MONOTONIC) {
|
||||||
|
LARGE_INTEGER count;
|
||||||
|
LARGE_INTEGER perf_freq;
|
||||||
|
QueryPerformanceCounter(&count);
|
||||||
|
QueryPerformanceFrequency(&perf_freq);
|
||||||
|
spec->tv_sec = count.QuadPart / perf_freq.QuadPart;
|
||||||
|
spec->tv_nsec = (long)((count.QuadPart % perf_freq.QuadPart) * 1000000000 / perf_freq.QuadPart);
|
||||||
|
} else if (source == JANET_TIME_CPUTIME) {
|
||||||
|
FILETIME creationTime, exitTime, kernelTime, userTime;
|
||||||
|
GetProcessTimes(GetCurrentProcess(), &creationTime, &exitTime, &kernelTime, &userTime);
|
||||||
|
int64_t tmp = ((int64_t)userTime.dwHighDateTime << 32) + userTime.dwLowDateTime;
|
||||||
|
spec->tv_sec = tmp / 10000000LL;
|
||||||
|
spec->tv_nsec = tmp % 10000000LL * 100;
|
||||||
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
/* clock_gettime() wasn't available on Mac until 10.12. */
|
/* clock_gettime() wasn't available on Mac until 10.12. */
|
||||||
#elif defined(JANET_APPLE) && !defined(MAC_OS_X_VERSION_10_12)
|
#elif defined(JANET_APPLE) && !defined(MAC_OS_X_VERSION_10_12)
|
||||||
#include <mach/clock.h>
|
#include <mach/clock.h>
|
||||||
#include <mach/mach.h>
|
#include <mach/mach.h>
|
||||||
int janet_gettime(struct timespec *spec) {
|
int janet_gettime(struct timespec *spec, enum JanetTimeSource source) {
|
||||||
clock_serv_t cclock;
|
if (source == JANET_TIME_REALTIME) {
|
||||||
mach_timespec_t mts;
|
clock_serv_t cclock;
|
||||||
host_get_clock_service(mach_host_self(), CALENDAR_CLOCK, &cclock);
|
mach_timespec_t mts;
|
||||||
clock_get_time(cclock, &mts);
|
host_get_clock_service(mach_host_self(), CALENDAR_CLOCK, &cclock);
|
||||||
mach_port_deallocate(mach_task_self(), cclock);
|
clock_get_time(cclock, &mts);
|
||||||
spec->tv_sec = mts.tv_sec;
|
mach_port_deallocate(mach_task_self(), cclock);
|
||||||
spec->tv_nsec = mts.tv_nsec;
|
spec->tv_sec = mts.tv_sec;
|
||||||
|
spec->tv_nsec = mts.tv_nsec;
|
||||||
|
} else if (source == JANET_TIME_MONOTONIC) {
|
||||||
|
clock_serv_t cclock;
|
||||||
|
int nsecs;
|
||||||
|
mach_msg_type_number_t count;
|
||||||
|
host_get_clock_service(mach_host_self(), clock, &cclock);
|
||||||
|
clock_get_attributes(cclock, CLOCK_GET_TIME_RES, (clock_attr_t)&nsecs, &count);
|
||||||
|
mach_port_deallocate(mach_task_self(), cclock);
|
||||||
|
clock_getres(CLOCK_MONOTONIC, spec);
|
||||||
|
}
|
||||||
|
if (source == JANET_TIME_CPUTIME) {
|
||||||
|
clock_t tmp = clock();
|
||||||
|
spec->tv_sec = tmp;
|
||||||
|
spec->tv_nsec = (tmp - spec->tv_sec) * 1.0e9;
|
||||||
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
int janet_gettime(struct timespec *spec) {
|
int janet_gettime(struct timespec *spec, enum JanetTimeSource source) {
|
||||||
return clock_gettime(CLOCK_REALTIME, spec);
|
clockid_t cid = JANET_TIME_REALTIME;
|
||||||
|
if (source == JANET_TIME_REALTIME) {
|
||||||
|
cid = CLOCK_REALTIME;
|
||||||
|
} else if (source == JANET_TIME_MONOTONIC) {
|
||||||
|
cid = CLOCK_MONOTONIC;
|
||||||
|
} else if (source == JANET_TIME_CPUTIME) {
|
||||||
|
cid = CLOCK_PROCESS_CPUTIME_ID;
|
||||||
|
}
|
||||||
|
return clock_gettime(cid, spec);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
@ -126,7 +126,12 @@ void janet_core_cfuns_ext(JanetTable *env, const char *regprefix, const JanetReg
|
|||||||
|
|
||||||
/* Clock gettime */
|
/* Clock gettime */
|
||||||
#ifdef JANET_GETTIME
|
#ifdef JANET_GETTIME
|
||||||
int janet_gettime(struct timespec *spec);
|
enum JanetTimeSource {
|
||||||
|
JANET_TIME_REALTIME,
|
||||||
|
JANET_TIME_MONOTONIC,
|
||||||
|
JANET_TIME_CPUTIME
|
||||||
|
};
|
||||||
|
int janet_gettime(struct timespec *spec, enum JanetTimeSource source);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* strdup */
|
/* strdup */
|
||||||
|
@ -333,4 +333,29 @@
|
|||||||
|
|
||||||
(assert (pos? (length (gensym))) "gensym not empty, regression #753")
|
(assert (pos? (length (gensym))) "gensym not empty, regression #753")
|
||||||
|
|
||||||
|
|
||||||
|
# os/clock. These tests might prove fragile under CI because they
|
||||||
|
# rely on measured time. We'll see.
|
||||||
|
|
||||||
|
(defmacro measure-time [clocks & body]
|
||||||
|
(def [t1 t2] [(gensym) (gensym)])
|
||||||
|
~(do
|
||||||
|
(def ,t1 (map |(os/clock $) ,clocks))
|
||||||
|
,;body
|
||||||
|
(def ,t2 (map |(os/clock $) ,clocks))
|
||||||
|
(zipcoll ,clocks (map |(- ;$) (map tuple ,t2 ,t1))))
|
||||||
|
)
|
||||||
|
|
||||||
|
# Spin for 0.1 seconds
|
||||||
|
(def dt (measure-time [:realtime :monotonic :cputime]
|
||||||
|
(def t1 (os/clock :monotonic))
|
||||||
|
(while (< (- (os/clock :monotonic) t1) 0.1) true)))
|
||||||
|
(assert (> (dt :monotonic) 0.10))
|
||||||
|
(assert (> (dt :cputime) 0.05))
|
||||||
|
|
||||||
|
# Sleep for 0.1 seconds
|
||||||
|
(def dt (measure-time [:realtime :monotonic :cputime] (os/sleep 0.1)))
|
||||||
|
(assert (> (dt :monotonic) 0.10))
|
||||||
|
(assert (< (dt :cputime) 0.05))
|
||||||
|
|
||||||
(end-suite)
|
(end-suite)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user