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:
Pietro Gagliardi 2019-05-05 03:17:11 -04:00
parent 952b36b1c2
commit 288c74b026
4 changed files with 48 additions and 25 deletions

View File

@ -259,14 +259,14 @@ static void int128MulDiv64(timerprivInt128 *x, timerprivInt128 *y, timerprivInt1
numer.low = x64low * y64low; // b * d +
add.neg = 0;
add.high = x64high * y64low; // a * d * 2^32 +
add.low = add.high & 0xFFFFFFFF00000000;
add.low = (add.high & 0xFFFFFFFF) << 32;
add.high >>= 32;
int128UAdd(&numer, &add);
add.high = x64low * y64high; // b * c * 2^32
add.low = add.high & 0xFFFFFFFF00000000;
add.low = (add.high & 0xFFFFFFFF) << 32;
add.high >>= 32;
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
// Apple's implementation uses NewtonRaphson 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;
@ -296,7 +296,7 @@ void timerprivMulDiv64(int64_t x, int64_t y, int64_t z, timerprivInt128 *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;

View File

@ -5,6 +5,8 @@
typedef int64_t timerDuration;
typedef int64_t timerTime;
#define timerTimeMax ((timerTime) INT64_MAX)
#define timerNanosecond ((timerDuration) 1)
#define timerMicrosecond ((timerDuration) 1000)
#define timerMillisecond ((timerDuration) 1000000)

View File

@ -15,24 +15,50 @@
#include <mach/mach_time.h>
#endif
#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__
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;
timerTime c;
timerDuration ret;
uint64_t t;
timerprivInt128 quot;
mach_timebase_info(&mt);
c = end - start;
ret = ((timerDuration) c) * mt.numer / mt.denom;
return ret;
mustpthread_once(&baseOnce, baseInit);
t = mach_absolute_time() - base;
timerprivMulDivUint64(t, mt.numer, mt.denom, &quot);
// 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
@ -61,13 +87,8 @@ timerTime timerMonotonicNow(void)
{
struct timespec ts;
timerTime ret;
int err;
err = pthread_once(&baseOnce, baseInit);
if (err != 0) {
fprintf(stderr, "*** internal error in timerMonotonicNow(): pthread_once() failed: %s (%d)\n", strerror(err), err);
abort();
}
mustpthread_once(&baseOnce, baseInit);
mustclock_gettime(CLOCK_MONOTONIC, &ts);
ts.tv_sec -= base.tv_sec;
ts.tv_nsec -= base.tv_nsec;
@ -80,13 +101,13 @@ timerTime timerMonotonicNow(void)
return ret;
}
#endif
timerDuration timerTimeSub(timerTime end, timerTime start)
{
return end - start;
}
#endif
struct timeoutParams {
jmp_buf retpos;
struct itimerval prevDuration;

View File

@ -8,5 +8,5 @@ struct timerprivInt128 {
uint64_t low;
};
extern void timerprivMulDiv64(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 timerprivMulDivInt64(int64_t x, int64_t y, int64_t z, timerprivInt128 *quot);
extern void timerprivMulDivUint64(uint64_t x, uint64_t y, uint64_t z, timerprivInt128 *quot);