Last active
July 4, 2023 10:30
-
-
Save andrewrk/bb124c8de6cf78ad2858041a980951d2 to your computer and use it in GitHub Desktop.
zig performance vs c in mersenne twister 64 bit
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Zig 0.371 sec | |
C 0.604 sec | |
implementations of the RNG are equivalent implementations of MT64 | |
note: I achieved the same performance with zig and C when I used clang with -O3 -fomit-frame-pointer and LTO. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// cc -O2 -g -std=c99 -fstrict-aliasing -fno-omit-frame-pointer -D_POSIX_C_SOURCE=200112L -D_XOPEN_SOURCE -ggdb -c test_rand.c | |
#include <stdint.h> | |
#include <stddef.h> | |
/* | |
* Implementation inspired by | |
* http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/emt64.html | |
*/ | |
#define RAND_MT64_ARRAY_LEN 312 | |
struct rand_mt64 { | |
uint64_t array[RAND_MT64_ARRAY_LEN]; | |
size_t index; | |
}; | |
void | |
rand_mt64_init(struct rand_mt64 *mt, uint64_t seed) | |
{ | |
static const uint64_t f = 0x5851f42d4c957f2d; | |
uint64_t prev_value = seed; | |
mt->index = RAND_MT64_ARRAY_LEN; | |
mt->array[0] = prev_value; | |
for (uint64_t i = 1; i < RAND_MT64_ARRAY_LEN; i += 1) { | |
prev_value = i + f * (prev_value ^ (prev_value >> 62)); | |
mt->array[i] = prev_value; | |
} | |
} | |
uint64_t | |
rand_mt64_get(struct rand_mt64 *mt) | |
{ | |
static const size_t m = 156; | |
static const size_t n = RAND_MT64_ARRAY_LEN; | |
static const uint64_t mag01[2] = {0, 0xB5026F5AA96619E9}; | |
static const uint64_t UM = 0xFFFFFFFF80000000; | |
static const uint64_t LM = 0x7FFFFFFF; | |
uint64_t x; | |
if (mt->index >= n) { | |
size_t i; | |
for (i = 0; i < n - m; i += 1) { | |
x = (mt->array[i] & UM) | (mt->array[i + 1] & LM); | |
mt->array[i] = mt->array[i + m] ^ (x >> 1) ^ | |
mag01[x & 0x1]; | |
} | |
for (; i < n - 1; i += 1) { | |
x = (mt->array[i] & UM) | (mt->array[i + 1] & LM); | |
mt->array[i] = mt->array[i + (m - n)] ^ (x >> 1) ^ | |
mag01[x & 0x1]; | |
} | |
x = (mt->array[i] & UM) | (mt->array[0] & LM); | |
mt->array[i] = mt->array[m - 1] ^ (x >> 1) ^ | |
mag01[x & 0x1]; | |
mt->index = 0; | |
} | |
x = mt->array[mt->index]; | |
mt->index += 1; | |
x ^= ((x >> 29) & 0x5555555555555555); | |
x ^= ((x << 17) & 0x71D67FFFEDA60000); | |
x ^= ((x << 37) & 0xFFF7EEE000000000); | |
x ^= (x >> 43); | |
return x; | |
} | |
/* Always asserts, even in release mode. */ | |
static void | |
my_assert(bool ok) | |
{ | |
if (!ok) | |
abort(); | |
} | |
static double | |
to_seconds(struct timespec *tms) | |
{ | |
return tms->tv_sec + (tms->tv_nsec / 1000000000.0); | |
} | |
struct timespec start_time; | |
static double elapsed; | |
static void | |
clock_start(void) | |
{ | |
int ret; | |
ret = clock_gettime(CLOCK_MONOTONIC, &start_time); | |
my_assert(ret == 0); | |
} | |
static void | |
clock_end(void) | |
{ | |
int ret; | |
struct timespec end_time; | |
double start; | |
double end; | |
ret = clock_gettime(CLOCK_MONOTONIC, &end_time); | |
my_assert(ret == 0); | |
start = to_seconds(&start_time); | |
end = to_seconds(&end_time); | |
elapsed = end - start; | |
} | |
static const size_t rand_iterations = 100000000; | |
static void | |
test_rand_mt64(void) | |
{ | |
struct rand_mt64 rand_state; | |
rand_mt64_init(&rand_state, 1337); | |
clock_start(); | |
for (size_t i = 0; i < rand_iterations; i += 1) { | |
uint64_t x = rand_mt64_get(&rand_state); | |
my_assert(x != 0); | |
} | |
clock_end(); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// zig build --name test_rand --export exe test_rand.zig --library c --release | |
const std = @import("std"); | |
const MT19937_64 = std.rand.MT19937_64; // see rand.zig in zig standard library for code. equivalent to c version in this gist | |
const os = std.os; | |
const c = @cImport({ | |
@cInclude("stdio.h"); | |
@cInclude("time.h"); | |
}); | |
var start_time: c.timespec = undefined; | |
var elapsed: f64 = undefined; | |
fn toSeconds(tms: &const c.timespec) -> f64 { | |
f64(tms.tv_sec) + (f64(tms.tv_nsec) / 1000000000.0) | |
} | |
fn clockStart() { | |
const ret = c.clock_gettime(c.CLOCK_MONOTONIC, &start_time); | |
assert(ret == 0); | |
} | |
fn clockEnd() { | |
var end_time: c.timespec = undefined; | |
const ret = c.clock_gettime(c.CLOCK_MONOTONIC, &end_time); | |
assert(ret == 0); | |
const start = toSeconds(&start_time); | |
const end = toSeconds(&end_time); | |
elapsed = end - start; | |
} | |
fn assert(ok: bool) { | |
if (!ok) { | |
os.abort(); | |
} | |
} | |
const rand_iterations = 100000000; | |
export fn main(argc: c_int, argv: &&u8) -> c_int { | |
var rand_state: MT19937_64 = undefined; | |
rand_state.init(1337); | |
clockStart(); | |
{var i: usize = 0; while (i < rand_iterations; i += 1) { | |
const x = rand_state.get(); | |
assert(x != 0); | |
}} | |
clockEnd(); | |
c.fprintf(c.stderr, c"%0.3f sec\n", elapsed); | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment