Skip to content

Instantly share code, notes, and snippets.

@andrewrk
Last active July 4, 2023 10:30
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save andrewrk/bb124c8de6cf78ad2858041a980951d2 to your computer and use it in GitHub Desktop.
Save andrewrk/bb124c8de6cf78ad2858041a980951d2 to your computer and use it in GitHub Desktop.
zig performance vs c in mersenne twister 64 bit
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.
// 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();
}
// 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