Implemented the new MulDiv128 stuff on macOS. To make things easier in regards to signedness, this implementation also uses a base, like the Unix one, and the mach_timebase_info() conversion is done in timerMonotonicTime() instead. (Also added the admittedly-redundant-since-the-current(ish)-implementation-cannot-fail error check to mach_timebase_info().) As a result, timerTimeSub() can now be end - start on all platforms. Also found and fixed bugs in the actual muldiv128 algorithm implementation. Next up: Windows.
This commit is contained in:
parent
952b36b1c2
commit
288c74b026
|
@ -259,14 +259,14 @@ static void int128MulDiv64(timerprivInt128 *x, timerprivInt128 *y, timerprivInt1
|
||||||
numer.low = x64low * y64low; // b * d +
|
numer.low = x64low * y64low; // b * d +
|
||||||
add.neg = 0;
|
add.neg = 0;
|
||||||
add.high = x64high * y64low; // a * d * 2^32 +
|
add.high = x64high * y64low; // a * d * 2^32 +
|
||||||
add.low = add.high & 0xFFFFFFFF00000000;
|
add.low = (add.high & 0xFFFFFFFF) << 32;
|
||||||
add.high >>= 32;
|
add.high >>= 32;
|
||||||
int128UAdd(&numer, &add);
|
int128UAdd(&numer, &add);
|
||||||
add.high = x64low * y64high; // b * c * 2^32
|
add.high = x64low * y64high; // b * c * 2^32
|
||||||
add.low = add.high & 0xFFFFFFFF00000000;
|
add.low = (add.high & 0xFFFFFFFF) << 32;
|
||||||
add.high >>= 32;
|
add.high >>= 32;
|
||||||
int128UAdd(&numer, &add);
|
int128UAdd(&numer, &add);
|
||||||
// I did type this all by hand, btw; the idea does come from Apple's implementation, though they explain it a bit more obtusely, and the odd behavior with anding high and shifting it right is to avoid looking like I directly copied their code which does the opposite
|
// I did type this all by hand, btw; the idea does come from Apple's implementation, though they explain it a bit more obtusely, and the odd behavior with anding high into low is to avoid looking like I directly copied their code which does the opposite
|
||||||
|
|
||||||
// and now long-divide
|
// and now long-divide
|
||||||
// Apple's implementation uses Newton–Raphson division using doubles to store 1/z but I'd rather go with "slow but guaranteed to be accurate"
|
// Apple's implementation uses Newton–Raphson division using doubles to store 1/z but I'd rather go with "slow but guaranteed to be accurate"
|
||||||
|
@ -286,7 +286,7 @@ static void int128MulDiv64(timerprivInt128 *x, timerprivInt128 *y, timerprivInt1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void timerprivMulDiv64(int64_t x, int64_t y, int64_t z, timerprivInt128 *quot)
|
void timerprivMulDivInt64(int64_t x, int64_t y, int64_t z, timerprivInt128 *quot)
|
||||||
{
|
{
|
||||||
timerprivInt128 a, b, c;
|
timerprivInt128 a, b, c;
|
||||||
|
|
||||||
|
@ -296,7 +296,7 @@ void timerprivMulDiv64(int64_t x, int64_t y, int64_t z, timerprivInt128 *quot)
|
||||||
int128MulDiv64(&a, &b, &c, quot);
|
int128MulDiv64(&a, &b, &c, quot);
|
||||||
}
|
}
|
||||||
|
|
||||||
void timerprivMulDivU64(uint64_t x, uint64_t y, uint64_t z, timerprivInt128 *quot)
|
void timerprivMulDivUint64(uint64_t x, uint64_t y, uint64_t z, timerprivInt128 *quot)
|
||||||
{
|
{
|
||||||
timerprivInt128 a, b, c;
|
timerprivInt128 a, b, c;
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,8 @@
|
||||||
typedef int64_t timerDuration;
|
typedef int64_t timerDuration;
|
||||||
typedef int64_t timerTime;
|
typedef int64_t timerTime;
|
||||||
|
|
||||||
|
#define timerTimeMax ((timerTime) INT64_MAX)
|
||||||
|
|
||||||
#define timerNanosecond ((timerDuration) 1)
|
#define timerNanosecond ((timerDuration) 1)
|
||||||
#define timerMicrosecond ((timerDuration) 1000)
|
#define timerMicrosecond ((timerDuration) 1000)
|
||||||
#define timerMillisecond ((timerDuration) 1000000)
|
#define timerMillisecond ((timerDuration) 1000000)
|
||||||
|
|
|
@ -15,24 +15,50 @@
|
||||||
#include <mach/mach_time.h>
|
#include <mach/mach_time.h>
|
||||||
#endif
|
#endif
|
||||||
#include "timer.h"
|
#include "timer.h"
|
||||||
|
#include "timerpriv.h"
|
||||||
|
|
||||||
|
static void mustpthread_once(pthread_once_t *once, void (*init)(void))
|
||||||
|
{
|
||||||
|
int err;
|
||||||
|
|
||||||
|
err = pthread_once(once, init);
|
||||||
|
if (err != 0) {
|
||||||
|
fprintf(stderr, "*** internal error in timerMonotonicNow(): pthread_once() failed: %s (%d)\n", strerror(err), err);
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef __APPLE__
|
#ifdef __APPLE__
|
||||||
|
|
||||||
timerTime timerMonotonicNow(void)
|
static uint64_t base;
|
||||||
|
static mach_timebase_info_data_t mt;
|
||||||
|
static pthread_once_t baseOnce = PTHREAD_ONCE_INIT;
|
||||||
|
|
||||||
|
static void baseInit(void)
|
||||||
{
|
{
|
||||||
return (timerTime) mach_absolute_time();
|
kern_return_t err;
|
||||||
|
|
||||||
|
base = mach_absolute_time();
|
||||||
|
err = mach_timebase_info(&mt);
|
||||||
|
if (err != KERN_SUCCESS) {
|
||||||
|
fprintf(stderr, "*** internal error in timerMonotonicNow(): mach_timebase_info() failed: kern_return_t %d\n", err);
|
||||||
|
abort();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
timerDuration timerTimeSub(timerTime end, timerTime start)
|
timerTime timerMonotonicNow(void)
|
||||||
{
|
{
|
||||||
mach_timebase_info_data_t mt;
|
uint64_t t;
|
||||||
timerTime c;
|
timerprivInt128 quot;
|
||||||
timerDuration ret;
|
|
||||||
|
|
||||||
mach_timebase_info(&mt);
|
mustpthread_once(&baseOnce, baseInit);
|
||||||
c = end - start;
|
t = mach_absolute_time() - base;
|
||||||
ret = ((timerDuration) c) * mt.numer / mt.denom;
|
timerprivMulDivUint64(t, mt.numer, mt.denom, ");
|
||||||
return ret;
|
// on overflow, return the maximum possible timerTime; this is inspired by what Go does
|
||||||
|
if (quot.high == 0)
|
||||||
|
if (quot.low <= ((uint64_t) timerTimeMax))
|
||||||
|
return (timerTime) (quot.low);
|
||||||
|
return timerTimeMax;
|
||||||
}
|
}
|
||||||
|
|
||||||
#else
|
#else
|
||||||
|
@ -61,13 +87,8 @@ timerTime timerMonotonicNow(void)
|
||||||
{
|
{
|
||||||
struct timespec ts;
|
struct timespec ts;
|
||||||
timerTime ret;
|
timerTime ret;
|
||||||
int err;
|
|
||||||
|
|
||||||
err = pthread_once(&baseOnce, baseInit);
|
mustpthread_once(&baseOnce, baseInit);
|
||||||
if (err != 0) {
|
|
||||||
fprintf(stderr, "*** internal error in timerMonotonicNow(): pthread_once() failed: %s (%d)\n", strerror(err), err);
|
|
||||||
abort();
|
|
||||||
}
|
|
||||||
mustclock_gettime(CLOCK_MONOTONIC, &ts);
|
mustclock_gettime(CLOCK_MONOTONIC, &ts);
|
||||||
ts.tv_sec -= base.tv_sec;
|
ts.tv_sec -= base.tv_sec;
|
||||||
ts.tv_nsec -= base.tv_nsec;
|
ts.tv_nsec -= base.tv_nsec;
|
||||||
|
@ -80,13 +101,13 @@ timerTime timerMonotonicNow(void)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
timerDuration timerTimeSub(timerTime end, timerTime start)
|
timerDuration timerTimeSub(timerTime end, timerTime start)
|
||||||
{
|
{
|
||||||
return end - start;
|
return end - start;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
struct timeoutParams {
|
struct timeoutParams {
|
||||||
jmp_buf retpos;
|
jmp_buf retpos;
|
||||||
struct itimerval prevDuration;
|
struct itimerval prevDuration;
|
||||||
|
|
|
@ -8,5 +8,5 @@ struct timerprivInt128 {
|
||||||
uint64_t low;
|
uint64_t low;
|
||||||
};
|
};
|
||||||
|
|
||||||
extern void timerprivMulDiv64(int64_t x, int64_t y, int64_t z, timerprivInt128 *quot);
|
extern void timerprivMulDivInt64(int64_t x, int64_t y, int64_t z, timerprivInt128 *quot);
|
||||||
extern void timerprivMulDivU64(uint64_t x, uint64_t y, uint64_t z, timerprivInt128 *quot);
|
extern void timerprivMulDivUint64(uint64_t x, uint64_t y, uint64_t z, timerprivInt128 *quot);
|
||||||
|
|
Loading…
Reference in New Issue