mirror of
https://github.com/isar/libmdbx.git
synced 2025-01-01 22:54:12 +08:00
test: add rnd and delay tools.
This commit is contained in:
parent
99dd373215
commit
2523170806
@ -71,7 +71,7 @@ time from_ms(uint64_t ms) {
|
||||
return result;
|
||||
}
|
||||
|
||||
time now() {
|
||||
time now_realtime() {
|
||||
#if defined(_WIN32) || defined(_WIN64) || defined(_WINDOWS)
|
||||
FILETIME filetime;
|
||||
GetSystemTimeAsFileTime(&filetime);
|
||||
@ -87,4 +87,35 @@ time now() {
|
||||
#endif
|
||||
}
|
||||
|
||||
time now_motonic() {
|
||||
#if defined(_WIN32) || defined(_WIN64) || defined(_WINDOWS)
|
||||
static uint32_t reciprocal;
|
||||
static LARGE_INTEGER Frequency;
|
||||
if (reciprocal == 0) {
|
||||
if (!QueryPerformanceFrequency(&Frequency))
|
||||
failure_perror("QueryPerformanceFrequency()", GetLastError());
|
||||
reciprocal = (UINT64_C(1) << 32) / Frequency.QuadPart;
|
||||
assert(reciprocal);
|
||||
}
|
||||
|
||||
LARGE_INTEGER Counter;
|
||||
if (!QueryPerformanceCounter(&Counter))
|
||||
failure_perror("QueryPerformanceCounter()", GetLastError());
|
||||
|
||||
time result;
|
||||
result.integer = Counter.QuadPart / Frequency.QuadPart;
|
||||
uint64_t mod = Counter.QuadPart % Frequency.QuadPart;
|
||||
assert(mod < UINT32_MAX);
|
||||
result.fractional = UInt32x32To64((uint32_t)mod, reciprocal);
|
||||
assert(result.fractional == (mod << 32) / Frequency.QuadPart);
|
||||
return result;
|
||||
#else
|
||||
struct timespec ts;
|
||||
if (unlikely(clock_gettime(CLOCK_MONOTONIC, &ts)))
|
||||
failure_perror("clock_gettime(CLOCK_MONOTONIC)", errno);
|
||||
|
||||
return from_timespec(ts);
|
||||
#endif
|
||||
}
|
||||
|
||||
} /* namespace chrono */
|
||||
|
@ -25,12 +25,21 @@ typedef union time {
|
||||
struct __packed {
|
||||
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
|
||||
uint32_t fractional;
|
||||
uint32_t utc;
|
||||
union {
|
||||
uint32_t utc;
|
||||
uint32_t integer;
|
||||
};
|
||||
#else
|
||||
uint32_t utc;
|
||||
union {
|
||||
uint32_t utc;
|
||||
uint32_t integer;
|
||||
};
|
||||
uint32_t fractional;
|
||||
#endif
|
||||
};
|
||||
|
||||
void reset() { fixedpoint = 0; }
|
||||
uint32_t seconds() const { return utc; }
|
||||
} time;
|
||||
|
||||
uint32_t ns2fractional(uint32_t);
|
||||
@ -44,10 +53,21 @@ time from_ns(uint64_t us);
|
||||
time from_us(uint64_t ns);
|
||||
time from_ms(uint64_t ms);
|
||||
|
||||
inline time from_utc(time_t utc) {
|
||||
assert(utc < UINT32_MAX);
|
||||
inline time from_seconds(uint64_t seconds) {
|
||||
assert(seconds < UINT32_MAX);
|
||||
time result;
|
||||
result.fixedpoint = ((uint64_t)utc) << 32;
|
||||
result.fixedpoint = seconds << 32;
|
||||
return result;
|
||||
}
|
||||
|
||||
inline time from_utc(time_t utc) {
|
||||
assert(utc >= 0);
|
||||
return from_seconds(utc);
|
||||
}
|
||||
|
||||
inline time infinite() {
|
||||
time result;
|
||||
result.fixedpoint = UINT64_MAX;
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -70,6 +90,7 @@ inline time from_timeval(const struct timeval &tv) {
|
||||
}
|
||||
#endif /* HAVE_TIMEVAL_TV_USEC */
|
||||
|
||||
time now();
|
||||
time now_realtime();
|
||||
time now_motonic();
|
||||
|
||||
} /* namespace chrono */
|
||||
|
@ -157,6 +157,8 @@ static void handler_SIGCHLD(int unused) { (void)unused; }
|
||||
|
||||
mdbx_pid_t osal_getpid(void) { return getpid(); }
|
||||
|
||||
int osal_delay(unsigned seconds) { return sleep(seconds) ? errno : 0; }
|
||||
|
||||
int osal_actor_start(const actor_config &config, mdbx_pid_t &pid) {
|
||||
if (childs.empty())
|
||||
signal(SIGCHLD, handler_SIGCHLD);
|
||||
@ -228,3 +230,48 @@ retry:
|
||||
return errno;
|
||||
}
|
||||
}
|
||||
|
||||
void osal_yield(void) {
|
||||
if (sched_yield())
|
||||
failure_perror("sched_yield()", errno);
|
||||
}
|
||||
|
||||
void osal_udelay(unsigned us) {
|
||||
chrono::time until, now = chrono::now_motonic();
|
||||
until.fixedpoint = now.fixedpoint + chrono::from_us(us).fixedpoint;
|
||||
struct timespec ts;
|
||||
|
||||
static unsigned threshold_us;
|
||||
if (threshold_us == 0) {
|
||||
if (clock_getres(CLOCK_PROCESS_CPUTIME_ID, &ts)) {
|
||||
int rc = errno;
|
||||
failure_perror("clock_getres(CLOCK_PROCESS_CPUTIME_ID)", rc);
|
||||
}
|
||||
chrono::time threshold = chrono::from_timespec(ts);
|
||||
assert(threshold.seconds() == 0);
|
||||
|
||||
threshold_us = chrono::fractional2us(threshold.fractional);
|
||||
if (threshold_us < 1000)
|
||||
threshold_us = 1000;
|
||||
}
|
||||
|
||||
ts.tv_sec = ts.tv_nsec = 0;
|
||||
if (us > threshold_us) {
|
||||
ts.tv_sec = us / 1000000u;
|
||||
ts.tv_nsec = (us % 1000000u) * 1000u;
|
||||
}
|
||||
|
||||
do {
|
||||
if (us > threshold_us) {
|
||||
if (nanosleep(&ts, &ts)) {
|
||||
int rc = errno;
|
||||
/* if (rc == EINTR) { ... } ? */
|
||||
failure_perror("usleep()", rc);
|
||||
}
|
||||
us = ts.tv_sec * 1000000u + ts.tv_nsec / 1000u;
|
||||
}
|
||||
cpu_relax();
|
||||
|
||||
now = chrono::now_motonic();
|
||||
} while (until.fixedpoint > now.fixedpoint);
|
||||
}
|
||||
|
@ -97,6 +97,11 @@ int osal_waitfor(unsigned id) {
|
||||
|
||||
mdbx_pid_t osal_getpid(void) { return GetCurrentProcessId(); }
|
||||
|
||||
int osal_delay(unsigned seconds) {
|
||||
Sleep(seconds * 1000u);
|
||||
return 0;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
const std::string
|
||||
@ -260,3 +265,30 @@ int osal_actor_poll(mdbx_pid_t &pid, unsigned timeout) {
|
||||
|
||||
return waitstatus2errcode(rc);
|
||||
}
|
||||
|
||||
void osal_yield(void) { SwitchToThread(); }
|
||||
|
||||
void osal_udelay(unsigned us) {
|
||||
chrono::time until, now = chrono::now_motonic();
|
||||
until.fixedpoint = now.fixedpoint + chrono::from_us(us).fixedpoint;
|
||||
|
||||
static unsigned threshold_us;
|
||||
if (threshold_us == 0) {
|
||||
ULONGLONG InterruptTimePrecise_100ns;
|
||||
QueryInterruptTimePrecise(&InterruptTimePrecise_100ns);
|
||||
threshold_us = InterruptTimePrecise_100ns / 5;
|
||||
assert(threshold_us > 0);
|
||||
}
|
||||
|
||||
do {
|
||||
if (us > threshold_us && us > 1000) {
|
||||
DWORD rc = SleepEx(us / 1000, TRUE);
|
||||
if (rc)
|
||||
failure_perror("SleepEx()", waitstatus2errcode(rc));
|
||||
us = 0;
|
||||
}
|
||||
|
||||
YieldProcessor();
|
||||
now = chrono::now_motonic();
|
||||
} while (now.fixedpoint < until.fixedpoint);
|
||||
}
|
||||
|
@ -20,9 +20,13 @@ void osal_setup(const std::vector<actor_config> &actors);
|
||||
void osal_broadcast(unsigned id);
|
||||
int osal_waitfor(unsigned id);
|
||||
|
||||
mdbx_pid_t osal_getpid(void);
|
||||
int osal_actor_start(const actor_config &config, mdbx_pid_t &pid);
|
||||
actor_status osal_actor_info(const mdbx_pid_t pid);
|
||||
void osal_killall_actors(void);
|
||||
int osal_actor_poll(mdbx_pid_t &pid, unsigned timeout);
|
||||
void osal_wait4barrier(void);
|
||||
|
||||
mdbx_pid_t osal_getpid(void);
|
||||
int osal_delay(unsigned seconds);
|
||||
void osal_udelay(unsigned us);
|
||||
void osal_yield(void);
|
||||
|
192
test/utils.cc
192
test/utils.cc
@ -13,6 +13,8 @@
|
||||
*/
|
||||
|
||||
#include "test.h"
|
||||
#include <float.h>
|
||||
#include <ieee754.h>
|
||||
|
||||
std::string format(const char *fmt, ...) {
|
||||
va_list ap, ones;
|
||||
@ -88,3 +90,193 @@ bool hex2data(const char *hex_begin, const char *hex_end, void *ptr,
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifdef __mips__
|
||||
static uint64_t *mips_tsc_addr;
|
||||
|
||||
__cold static void mips_rdtsc_init() {
|
||||
int mem_fd = open("/dev/mem", O_RDONLY | O_SYNC, 0);
|
||||
HIPPEUS_ENSURE(mem_fd >= 0);
|
||||
|
||||
mips_tsc_addr = mmap(nullptr, pagesize, PROT_READ, MAP_SHARED, mem_fd,
|
||||
0x10030000 /* MIPS_ZBUS_TIMER */);
|
||||
close(mem_fd);
|
||||
}
|
||||
#endif /* __mips__ */
|
||||
|
||||
uint64_t entropy_ticks(void) {
|
||||
#if defined(__GNUC__) || defined(__clang__)
|
||||
#if defined(__ia64__)
|
||||
uint64_t ticks;
|
||||
__asm("mov %0=ar.itc" : "=r"(ticks));
|
||||
return ticks;
|
||||
#elif defined(__hppa__)
|
||||
uint64_t ticks;
|
||||
__asm("mfctl 16, %0" : "=r"(ticks));
|
||||
return ticks;
|
||||
#elif defined(__s390__)
|
||||
uint64_t ticks;
|
||||
__asm("stck 0(%0)" : : "a"(&(ticks)) : "memory", "cc");
|
||||
return ticks;
|
||||
#elif defined(__alpha__)
|
||||
uint64_t ticks;
|
||||
__asm("rpcc %0" : "=r"(ticks));
|
||||
return ticks;
|
||||
#elif defined(__sparc_v9__)
|
||||
uint64_t ticks;
|
||||
__asm("rd %%tick, %0" : "=r"(ticks));
|
||||
return ticks;
|
||||
#elif defined(__powerpc64__) || defined(__ppc64__)
|
||||
uint64_t ticks;
|
||||
__asm("mfspr %0, 268" : "=r"(ticks));
|
||||
return ticks;
|
||||
#elif defined(__ppc__) || defined(__powerpc__)
|
||||
unsigned tbl, tbu;
|
||||
|
||||
/* LY: Here not a problem if a high-part (tbu)
|
||||
* would been updated during reading. */
|
||||
__asm("mftb %0" : "=r"(tbl));
|
||||
__asm("mftbu %0" : "=r"(tbu));
|
||||
|
||||
return (((uin64_t)tbu0) << 32) | tbl;
|
||||
#elif defined(__mips__)
|
||||
if (mips_tsc_addr != MAP_FAILED) {
|
||||
if (unlikely(!mips_tsc_addr)) {
|
||||
static pthread_once_t is_initialized = PTHREAD_ONCE_INIT;
|
||||
int rc = pthread_once(&is_initialized, mips_rdtsc_init);
|
||||
if (unlikely(rc))
|
||||
failure_perror("pthread_once()", rc);
|
||||
}
|
||||
if (mips_tsc_addr != MAP_FAILED)
|
||||
return *mips_tsc_addr;
|
||||
}
|
||||
#elif defined(__x86_64__) || defined(__i386__)
|
||||
unsigned lo, hi;
|
||||
|
||||
/* LY: Using the "a" and "d" constraints is important for correct code. */
|
||||
__asm("rdtsc" : "=a"(lo), "=d"(hi));
|
||||
|
||||
return (((uint64_t)hi) << 32) + lo;
|
||||
#endif /* arch selector */
|
||||
|
||||
#elif defined(_M_IX86) || defined(_M_X64)
|
||||
return __rdtsc();
|
||||
#endif /* __GNUC__ || __clang__ */
|
||||
|
||||
#if defined(_WIN32) || defined(_WIN64) || defined(_WINDOWS)
|
||||
LARGE_INTEGER PerformanceCount;
|
||||
if (QueryPerformanceCounter(&PerformanceCount))
|
||||
return PerformanceCount.QuadPart;
|
||||
return GetTickCount64();
|
||||
#else
|
||||
struct timespec ts;
|
||||
#if defined(CLOCK_MONOTONIC_COARSE)
|
||||
clockid_t clock = CLOCK_MONOTONIC_COARSE;
|
||||
#elif defined(CLOCK_MONOTONIC_RAW)
|
||||
clockid_t clock = CLOCK_MONOTONIC_RAW;
|
||||
#else
|
||||
clockid_t clock = CLOCK_MONOTONIC;
|
||||
#endif
|
||||
int rc = clock_gettime(clock, &ts);
|
||||
if (unlikely(rc))
|
||||
failure_perror("clock_gettime()", rc);
|
||||
|
||||
return (((uint64_t)ts.tv_sec) << 32) + ts.tv_nsec;
|
||||
#endif
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
static __inline uint64_t bleach64(uint64_t dirty) {
|
||||
dirty = mul_64x64_high(bswap64(dirty), UINT64_C(17048867929148541611));
|
||||
return dirty;
|
||||
}
|
||||
|
||||
static __inline uint32_t bleach32(uint32_t dirty) {
|
||||
return (uint32_t)(
|
||||
(bswap32(dirty) * UINT64_C(/*3080105489, 4267077937 */ 2175734609)) >>
|
||||
32);
|
||||
}
|
||||
|
||||
uint64_t prng64_careless(uint64_t &state) {
|
||||
state = state * UINT64_C(6364136223846793005) + 1;
|
||||
return state;
|
||||
}
|
||||
|
||||
uint64_t prng64_white(uint64_t &state) {
|
||||
state = state * UINT64_C(6364136223846793005) + UINT64_C(1442695040888963407);
|
||||
return bleach64(state);
|
||||
}
|
||||
|
||||
uint32_t prng32(uint64_t &state) {
|
||||
return (uint32_t)(prng64_careless(state) >> 32);
|
||||
}
|
||||
|
||||
uint64_t entropy_white() { return bleach64(entropy_ticks()); }
|
||||
|
||||
double double_from_lower(uint64_t salt) {
|
||||
#ifdef IEEE754_DOUBLE_BIAS
|
||||
ieee754_double r;
|
||||
r.ieee.negative = 0;
|
||||
r.ieee.exponent = IEEE754_DOUBLE_BIAS;
|
||||
r.ieee.mantissa0 = (unsigned)(salt >> 32);
|
||||
r.ieee.mantissa1 = (unsigned)salt;
|
||||
return r.d;
|
||||
#else
|
||||
const uint64_t top = (UINT64_C(1) << DBL_MANT_DIG) - 1;
|
||||
const double scale = 1.0 / (double)top;
|
||||
return (salt & top) * scale;
|
||||
#endif
|
||||
}
|
||||
|
||||
double double_from_upper(uint64_t salt) {
|
||||
#ifdef IEEE754_DOUBLE_BIAS
|
||||
ieee754_double r;
|
||||
r.ieee.negative = 0;
|
||||
r.ieee.exponent = IEEE754_DOUBLE_BIAS;
|
||||
salt >>= 64 - DBL_MANT_DIG;
|
||||
r.ieee.mantissa0 = (unsigned)(salt >> 32);
|
||||
r.ieee.mantissa1 = (unsigned)salt;
|
||||
return r.d;
|
||||
#else
|
||||
const uint64_t top = (UINT64_C(1) << DBL_MANT_DIG) - 1;
|
||||
const double scale = 1.0 / (double)top;
|
||||
return (salt >> (64 - DBL_MANT_DIG)) * scale;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool flipcoin() { return bleach32((uint32_t)entropy_ticks()) & 1; }
|
||||
|
||||
bool jitter(unsigned probability_percent) {
|
||||
const uint32_t top = UINT32_MAX - UINT32_MAX % 100;
|
||||
uint32_t dice, edge = (top) / 100 * probability_percent;
|
||||
do
|
||||
dice = bleach32((uint32_t)entropy_ticks());
|
||||
while (dice >= top);
|
||||
return dice < edge;
|
||||
}
|
||||
|
||||
void jitter_delay(bool extra) {
|
||||
unsigned dice = entropy_white() & 3;
|
||||
if (dice == 0) {
|
||||
log_trace("== jitter.no-delay");
|
||||
} else {
|
||||
log_trace(">> jitter.delay: dice %u", dice);
|
||||
do {
|
||||
cpu_relax();
|
||||
memory_barrier();
|
||||
cpu_relax();
|
||||
if (dice > 1) {
|
||||
osal_yield();
|
||||
cpu_relax();
|
||||
if (dice > 2) {
|
||||
unsigned us = entropy_white() &
|
||||
(extra ? 0xfffff /* 1.05 s */ : 0x3fff /* 16 ms */);
|
||||
log_trace("== jitter.delay: %0.6f", us / 1000000.0);
|
||||
osal_udelay(us);
|
||||
}
|
||||
}
|
||||
} while (flipcoin());
|
||||
log_trace("<< jitter.delay: dice %u", dice);
|
||||
}
|
||||
}
|
||||
|
54
test/utils.h
54
test/utils.h
@ -271,6 +271,50 @@ static __inline size_t roundup2(size_t value, size_t granularity) {
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
static __inline void memory_barrier(void) {
|
||||
#if __has_extension(c_atomic) || __has_extension(cxx_atomic)
|
||||
__c11_atomic_thread_fence(__ATOMIC_SEQ_CST);
|
||||
#elif defined(__ATOMIC_SEQ_CST)
|
||||
__atomic_thread_fence(__ATOMIC_SEQ_CST);
|
||||
#elif defined(__clang__) || defined(__GNUC__)
|
||||
__sync_synchronize();
|
||||
#elif defined(_MSC_VER)
|
||||
MemoryBarrier();
|
||||
#elif defined(__INTEL_COMPILER) /* LY: Intel Compiler may mimic GCC and MSC */
|
||||
#if defined(__ia64__) || defined(__ia64) || defined(_M_IA64)
|
||||
__mf();
|
||||
#elif defined(__i386__) || defined(__x86_64__)
|
||||
_mm_mfence();
|
||||
#else
|
||||
#error "Unknown target for Intel Compiler, please report to us."
|
||||
#endif
|
||||
#elif defined(__SUNPRO_C) || defined(__sun) || defined(sun)
|
||||
__machine_rw_barrier();
|
||||
#elif (defined(_HPUX_SOURCE) || defined(__hpux) || defined(__HP_aCC)) && \
|
||||
(defined(HP_IA64) || defined(__ia64))
|
||||
_Asm_mf();
|
||||
#elif defined(_AIX) || defined(__ppc__) || defined(__powerpc__) || \
|
||||
defined(__ppc64__) || defined(__powerpc64__)
|
||||
__lwsync();
|
||||
#else
|
||||
#error "Could not guess the kind of compiler, please report to us."
|
||||
#endif
|
||||
}
|
||||
|
||||
static __inline void cpu_relax() {
|
||||
#if defined(__i386__) || defined(__x86_64__) || defined(_M_IX86) || \
|
||||
defined(_M_X64)
|
||||
_mm_pause();
|
||||
#elif defined(_WIN32) || defined(_WIN64) || defined(_WINDOWS) || \
|
||||
defined(YieldProcessor)
|
||||
YieldProcessor();
|
||||
#else
|
||||
/* nope */
|
||||
#endif
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
struct simple_checksum {
|
||||
uint64_t value;
|
||||
|
||||
@ -310,3 +354,13 @@ bool hex2data(const char *hex_begin, const char *hex_end, void *ptr,
|
||||
size_t bytes, simple_checksum &checksum);
|
||||
|
||||
std::string format(const char *fmt, ...);
|
||||
|
||||
uint64_t entropy_ticks(void);
|
||||
uint64_t entropy_white(void);
|
||||
uint64_t prng64_careless(uint64_t &state);
|
||||
uint64_t prng64_white(uint64_t &state);
|
||||
uint32_t prng32(uint64_t &state);
|
||||
|
||||
bool flipcoin();
|
||||
bool jitter(unsigned probability_percent);
|
||||
void jitter_delay(bool extra = false);
|
||||
|
Loading…
x
Reference in New Issue
Block a user