Skip to content
This is the documentation for xc 0.87. The current documentation describes the newest version.

Benchmark sources

These are the programs behind the performance figures, each written four times with the same algorithm and the same data. Choose a language on any of them and every one switches to it.

The xc versions are plain loops over arrays and objects. None of them asks for vector instructions, threads, a matrix unit or anything else by name: where matrix_mul_f32 runs on Apple’s SME matrix unit, or array_map on AVX-512, it is because xcc saw the loop and chose to.

Each program times its own work with bench_now_us() (the monotonic clock) and prints a checksum, which must agree across the four languages for a run to count.

Allocate an object per iteration and let ARC free it.

Lines: xc 31, Objective-C 35, C++ 34, Swift 24.

// arc_alloc — allocate an object per iteration and let ARC free it.
// Exercises: heap allocation, retain/release traffic, method dispatch.
// Each object outlives its iteration (it is kept until the next one replaces
// it), so no compiler can put it on the stack or fold the loop away, and the
// result is folded in with ^ so the sum has no closed form.
#import "Stdio.xc"
#import "include/bench_time.xc"
class Node
{
u32 v;
void init(u32 x) { v = x; }
u32 get(void) { return v; }
}
i32 main(i32 argc, u8** argv)
{
u32 seed = (u32)argc;
u32 acc = (u32)0;
Node* keep = new Node(seed);
i64 t0 = bench_now_us();
for (u32 r = (u32)0; r < (u32)80000000; r++)
{
Node* n = new Node(r + seed);
acc = (acc ^ n.get()) + keep.get();
keep = n;
}
i64 t1 = bench_now_us();
Stdio.printf("%lu %lld\n", acc, t1 - t0);
return 0;
}

Hold objects in an array and walk them.

Lines: xc 21, Objective-C 31, C++ 34, Swift 25.

// arc_array — hold objects in an array and walk them.
// Exercises: retain/release on stored references, field access through a pointer.
#import "Stdio.xc"
#import "include/bench_time.xc"
#define N 1024
class Cell { u32 v; void init(u32 x) { v = x; } u32 get(void) { return v; } }
i32 main(i32 argc, u8** argv)
{
u32 seed = (u32)argc;
Cell* cells[N];
for (u32 i = (u32)0; i < (u32)N; i++) cells[i] = new Cell(i + seed);
u32 acc = (u32)0;
i64 t0 = bench_now_us();
for (u32 r = (u32)0; r < (u32)3000000; r++)
for (u32 i = (u32)0; i < (u32)N; i++) acc = acc + cells[i].get();
i64 t1 = bench_now_us();
Stdio.printf("%lu %lld\n", acc, t1 - t0);
return 0;
}

Elementwise c[i] = a[i] + b[i] * k.

Lines: xc 21, Objective-C 22, C++ 22, Swift 24.

// array_map — elementwise c[i] = a[i] + b[i] * k.
// Exercises: elementwise map loop, the shape the arm64 map vectoriser takes.
#import "Stdio.xc"
#import "include/bench_time.xc"
#define N 4096
i32 main(i32 argc, u8** argv)
{
u32 a[N]; u32 b[N]; u32 c[N];
u32 seed = (u32)argc;
for (u32 i = (u32)0; i < (u32)N; i++)
{ a[i] = i + seed; b[i] = (i * (u32)3) + seed; }
i64 t0 = bench_now_us();
for (u32 r = (u32)0; r < (u32)5000000; r++)
for (u32 i = (u32)0; i < (u32)N; i++)
c[i] = a[i] + (b[i] * (u32)7) + r;
u32 acc = (u32)0;
for (u32 i = (u32)0; i < (u32)N; i++) acc = acc + c[i];
i64 t1 = bench_now_us();
Stdio.printf("%lu %lld\n", acc, t1 - t0);
return 0;
}

Sum an array. Exercises: reduction loop, array addressing.

Lines: xc 18, Objective-C 20, C++ 21, Swift 18.

// array_sum — sum an array. Exercises: reduction loop, array addressing.
// This is the shape the arm64 reduction vectoriser recognises.
#import "Stdio.xc"
#import "include/bench_time.xc"
#define N 4096
i32 main(i32 argc, u8** argv)
{
u32 a[N];
u32 seed = (u32)argc;
for (u32 i = (u32)0; i < (u32)N; i++) a[i] = (i * (u32)2654435761) + seed;
u32 sum = (u32)0;
i64 t0 = bench_now_us();
for (u32 r = (u32)0; r < (u32)7000000; r++)
for (u32 i = (u32)0; i < (u32)N; i++) sum = sum + a[i];
i64 t1 = bench_now_us();
Stdio.printf("%lu %lld\n", sum, t1 - t0);
return 0;
}

Shifts and bitwise logic over an array.

Lines: xc 19, Objective-C 21, C++ 22, Swift 21.

// bit_ops — shifts and bitwise logic over an array.
// Exercises: shift lowering, and/or/xor, rotate idioms.
#import "Stdio.xc"
#import "include/bench_time.xc"
#define N 4096
i32 main(i32 argc, u8** argv)
{
u32 a[N];
u32 seed = (u32)argc;
for (u32 i = (u32)0; i < (u32)N; i++) a[i] = (i * (u32)2654435761) + seed;
u32 acc = (u32)0;
i64 t0 = bench_now_us();
for (u32 r = (u32)0; r < (u32)600000; r++)
for (u32 i = (u32)0; i < (u32)N; i++)
acc = acc + (((a[i] + r) << (u32)3) | ((a[i] + r) >> (u32)5)) ^ (u32)0x0F0F0F0F;
i64 t1 = bench_now_us();
Stdio.printf("%lu %lld\n", acc, t1 - t0);
return 0;
}

Data-dependent branches over an array.

Lines: xc 22, Objective-C 21, C++ 22, Swift 20.

// branch_mix — data-dependent branches over an array.
// Exercises: if-conversion, select formation, branch prediction behaviour.
#import "Stdio.xc"
#import "include/bench_time.xc"
#define N 4096
i32 main(i32 argc, u8** argv)
{
u32 a[N];
u32 seed = (u32)argc;
for (u32 i = (u32)0; i < (u32)N; i++) a[i] = (i * (u32)2654435761) + seed;
u32 acc = (u32)0;
i64 t0 = bench_now_us();
for (u32 r = (u32)0; r < (u32)500000; r++)
for (u32 i = (u32)0; i < (u32)N; i++)
{
if ((a[i] & (u32)1) == (u32)0) acc = acc + a[i];
else acc = acc ^ a[i];
}
i64 t1 = bench_now_us();
Stdio.printf("%lu %lld\n", acc, t1 - t0);
return 0;
}

A small non-inlinable call chain in a hot loop.

Lines: xc 19, Objective-C 22, C++ 22, Swift 17.

// call_depth — a small non-inlinable call chain in a hot loop.
// Exercises: call overhead, leaf inlining, tail-call conversion.
#import "Stdio.xc"
#import "include/bench_time.xc"
u32 leaf(u32 x) { return (x * (u32)3) ^ (x >> (u32)2); }
u32 mid(u32 x) { return leaf(x) + leaf(x + (u32)1); }
u32 outer(u32 x) { return mid(x) ^ mid(x + (u32)2); }
i32 main(i32 argc, u8** argv)
{
u32 seed = (u32)argc;
u32 acc = (u32)0;
i64 t0 = bench_now_us();
for (u32 r = (u32)0; r < (u32)2800000000; r++) acc = acc + outer(r + seed);
i64 t1 = bench_now_us();
Stdio.printf("%lu %lld\n", acc, t1 - t0);
return 0;
}

Float multiply, accumulated in double.

Lines: xc 29, Objective-C 24, C++ 28, Swift 24.

// float_math — float multiply, accumulated in double.
// Exercises: float arithmetic, multiply-add fusion, float register pressure.
//
// Values are small exact integers and the accumulator is double, so the sum is
// exact whatever order the additions happen in. A vectorising compiler reorders
// them, and without exactness the two languages would disagree on rounding.
#import "Stdio.xc"
#import "include/bench_time.xc"
#define N 4096
i32 main(i32 argc, u8** argv)
{
float a[N]; float b[N];
u32 seed = (u32)argc;
for (u32 i = (u32)0; i < (u32)N; i++)
{ a[i] = (float)((i + seed) % (u32)16); b[i] = (float)((i % (u32)7) + (u32)1); }
double acc = 0.0;
i64 t0 = bench_now_us();
for (u32 r = (u32)0; r < (u32)400000; r++)
for (u32 i = (u32)0; i < (u32)N; i++)
acc = acc + (double)(a[i] * b[i]);
i64 t1 = bench_now_us();
// (u32)acc directly is UNDEFINED once the sum exceeds u32: at this
// iteration count acc reaches ~4.9e10, and the two compilers chose
// differently (0 against 4294967295). The double is still exact — well
// under 2^53 — so going through u64 and letting the u32 narrowing wrap,
// which IS defined, keeps the checksum meaningful and identical.
Stdio.printf("%lu %lld\n", (u32)((u64)acc), t1 - t0);
return 0;
}

An integer avalanche chain.

Lines: xc 19, Objective-C 22, C++ 20, Swift 17.

// hash_mix — an integer avalanche chain.
// Exercises: multiply, shift and xor chains with a serial dependency.
#import "Stdio.xc"
#import "include/bench_time.xc"
i32 main(i32 argc, u8** argv)
{
u32 h = (u32)argc;
i64 t0 = bench_now_us();
for (u32 r = (u32)0; r < (u32)640000000; r++)
{
h = h ^ (h >> (u32)16);
h = h * (u32)2246822519;
h = h ^ (h >> (u32)13);
h = h + r;
}
i64 t1 = bench_now_us();
Stdio.printf("%lu %lld\n", h, t1 - t0);
return 0;
}

Integer add and xor over an array.

Lines: xc 29, Objective-C 30, C++ 24, Swift 18.

// int_accum — integer add and xor over an array.
// Exercises: loop induction, array addressing, u32 wraparound arithmetic.
//
// The array is seeded from argc so its contents are unknown at compile time,
// and the loop reads it every iteration. Without that both compilers reduce the
// whole loop to a closed form and the benchmark measures nothing.
#import "Stdio.xc"
#import "include/bench_time.xc"
#define N 4096
i32 main(i32 argc, u8** argv)
{
u32 a[N];
u32 seed = (u32)argc;
for (u32 i = (u32)0; i < (u32)N; i++)
a[i] = (i * (u32)2654435761) + seed;
u32 acc = (u32)0;
i64 t0 = bench_now_us();
for (u32 r = (u32)0; r < (u32)600000; r++)
for (u32 i = (u32)0; i < (u32)N; i++)
acc = acc + (a[i] ^ acc);
i64 t1 = bench_now_us();
Stdio.printf("%lu %lld\n", acc, t1 - t0);
return 0;
}

Integer multiply and divide by compile-time constants.

Lines: xc 19, Objective-C 21, C++ 21, Swift 18.

// int_muldiv — integer multiply and divide by compile-time constants.
// Exercises: strength reduction, magic-number division.
#import "Stdio.xc"
#import "include/bench_time.xc"
#define N 1024
i32 main(i32 argc, u8** argv)
{
u32 a[N];
u32 seed = (u32)argc;
for (u32 i = (u32)0; i < (u32)N; i++) a[i] = (i * (u32)2654435761) + seed;
u32 acc = (u32)0;
i64 t0 = bench_now_us();
for (u32 r = (u32)0; r < (u32)6800000; r++)
for (u32 i = (u32)0; i < (u32)N; i++)
acc = acc + ((a[i] * (u32)7) / (u32)3) + (a[i] / (u32)11);
i64 t1 = bench_now_us();
Stdio.printf("%lu %lld\n", acc, t1 - t0);
return 0;
}

Multiply two small square matrices, repeatedly.

Lines: xc 27, Objective-C 29, C++ 29, Swift 30.

// matrix_mul — multiply two small square matrices, repeatedly.
// Exercises: triple-nested loops, strided addressing, accumulation.
#import "Stdio.xc"
#import "include/bench_time.xc"
#define M 32
i32 main(i32 argc, u8** argv)
{
u32 a[M * M]; u32 b[M * M]; u32 c[M * M];
u32 seed = (u32)argc;
for (u32 i = (u32)0; i < (u32)(M * M); i++)
{ a[i] = (i + seed) & (u32)15; b[i] = (i ^ seed) & (u32)15; }
i64 t0 = bench_now_us();
for (u32 r = (u32)0; r < (u32)180000; r++)
for (u32 i = (u32)0; i < (u32)M; i++)
for (u32 j = (u32)0; j < (u32)M; j++)
{
u32 s = (u32)0;
for (u32 k = (u32)0; k < (u32)M; k++)
s = s + (a[i * (u32)M + k] * b[k * (u32)M + j]);
c[i * (u32)M + j] = s + r;
}
u32 acc = (u32)0;
for (u32 i = (u32)0; i < (u32)(M * M); i++) acc = acc + c[i];
i64 t1 = bench_now_us();
Stdio.printf("%lu %lld\n", acc, t1 - t0);
return 0;
}

Multiply two float matrices, repeatedly.

Lines: xc 37, Objective-C 35, C++ 33, Swift 32.

// matrix_mul_f32 — multiply two float matrices, repeatedly.
// Exercises: a dense float matrix multiply (SME outer products on arm64 macOS
// with SME; NEON or SSE elsewhere). The values are small integers, so every
// product and sum is exact and the checksum is the same in every language,
// whether or not it fuses a*b + s. `:goal(speed)` lets the SME kernel skip
// its NaN check of C: only NaN payloads could differ, and there are no NaNs.
#import "Stdio.xc"
#import "include/bench_time.xc"
#define M 128
i32 main(i32 argc, u8** argv)
{
float* a = new float[M * M];
float* b = new float[M * M];
float* c = new float[M * M];
u32 seed = (u32)argc;
for (u32 i = (u32)0; i < (u32)(M * M); i++)
{ a[i] = (float)((i + seed) & (u32)15); b[i] = (float)((i ^ seed) & (u32)15); }
u32 acc = (u32)0;
i64 t0 = bench_now_us();
for (u32 r = (u32)0; r < (u32)10000; r++)
{
a[r % (u32)(M * M)] = (float)(r & (u32)15);
for (u32 i = (u32)0; i < (u32)M; i++) :goal(speed)
for (u32 j = (u32)0; j < (u32)M; j++)
{
float s = 0.0;
for (u32 k = (u32)0; k < (u32)M; k++)
s = s + a[i * (u32)M + k] * b[k * (u32)M + j];
c[i * (u32)M + j] = s;
}
acc = acc + (u32)c[(r * (u32)7919) % (u32)(M * M)];
}
for (u32 i = (u32)0; i < (u32)(M * M); i++) acc = acc + (u32)c[i];
i64 t1 = bench_now_us();
Stdio.printf("%u %lld\n", acc, t1 - t0);
return 0;
}

Copy between arrays element by element.

Lines: xc 21, Objective-C 24, C++ 24, Swift 20.

// mem_copy — copy between arrays element by element.
// Exercises: load/store pairing, the memcpy idiom, pointer induction.
#import "Stdio.xc"
#import "include/bench_time.xc"
#define N 4096
i32 main(i32 argc, u8** argv)
{
u32 src[N]; u32 dst[N];
u32 seed = (u32)argc;
for (u32 i = (u32)0; i < (u32)N; i++) src[i] = i + seed;
u32 acc = (u32)0;
i64 t0 = bench_now_us();
for (u32 r = (u32)0; r < (u32)6800000; r++)
{
for (u32 i = (u32)0; i < (u32)N; i++) dst[i] = src[i] + r;
acc = acc + dst[r % (u32)N];
}
i64 t1 = bench_now_us();
Stdio.printf("%lu %lld\n", acc, t1 - t0);
return 0;
}

A virtual method call per iteration.

Lines: xc 27, Objective-C 32, C++ 35, Swift 24.

// method_call — a virtual method call per iteration.
// Exercises: dispatch cost, inlining across a method boundary.
// The class is chosen at run time and score() takes the iteration number, so
// the call can be neither resolved at compile time nor hoisted out of the loop.
#import "Stdio.xc"
#import "include/bench_time.xc"
class Shape { u32 k; void init(u32 x) { k = x; } u32 score(u32 x) { return k ^ x; } }
class Boxy : Shape
{
void init(u32 x) { super.init(x); }
u32 score(u32 x) { return (k ^ x) + (u32)1; }
}
i32 main(i32 argc, u8** argv)
{
u32 seed = (u32)argc;
Shape* s = new Shape(seed);
if ((argc & 1) == 0)
s = new Boxy(seed);
u32 acc = (u32)0;
i64 t0 = bench_now_us();
for (u32 r = (u32)0; r < (u32)1600000000; r++) acc = acc + s.score(r);
i64 t1 = bench_now_us();
Stdio.printf("%lu %lld\n", acc, t1 - t0);
return 0;
}

Dispatch through a mixed array of subclasses.

Lines: xc 25, Objective-C 35, C++ 52, Swift 40.

// poly_dispatch — dispatch through a mixed array of subclasses.
// Exercises: virtual dispatch the compiler cannot devirtualise.
#import "Stdio.xc"
#import "include/bench_time.xc"
#define N 256
class Op { u32 k; void init(u32 x) { k = x; } u32 apply(u32 v) { return v + k; } }
class OpMul : Op { void init(u32 x) { super.init(x); } u32 apply(u32 v) { return v * (k | (u32)1); } }
class OpXor : Op { void init(u32 x) { super.init(x); } u32 apply(u32 v) { return v ^ k; } }
i32 main(i32 argc, u8** argv)
{
Op* ops[N]; u32 seed = (u32)argc;
for (u32 i = (u32)0; i < (u32)N; i++)
{
if ((i % (u32)3) == (u32)0) ops[i] = new Op(i + seed);
else if ((i % (u32)3) == (u32)1) ops[i] = new OpMul(i + seed);
else ops[i] = new OpXor(i + seed);
}
u32 acc = (u32)1;
i64 t0 = bench_now_us();
for (u32 r = (u32)0; r < (u32)3000000; r++)
for (u32 i = (u32)0; i < (u32)N; i++) acc = ops[i].apply(acc) + r;
i64 t1 = bench_now_us();
Stdio.printf("%lu %lld\n", acc, t1 - t0);
return 0;
}

Sieve of Eratosthenes over a fixed range, repeatedly.

Lines: xc 25, Objective-C 28, C++ 30, Swift 27.

// sieve — sieve of Eratosthenes over a fixed range, repeatedly.
// Exercises: byte array writes, strided inner loops, division-free stepping.
#import "Stdio.xc"
#import "include/bench_time.xc"
#define N 8192
i32 main(i32 argc, u8** argv)
{
u8 flags[N]; u32 seed = (u32)argc; u32 acc = (u32)0;
i64 t0 = bench_now_us();
for (u32 r = (u32)0; r < (u32)150000; r++)
{
for (u32 i = (u32)0; i < (u32)N; i++) flags[i] = (u8)1;
u32 count = (u32)0;
for (u32 i = (u32)2; i < (u32)N; i++)
if (flags[i] != (u8)0)
{
count = count + (u32)1;
for (u32 j = i + i; j < (u32)N; j = j + i) flags[j] = (u8)0;
}
acc = acc + count + (r & seed);
}
i64 t1 = bench_now_us();
Stdio.printf("%lu %lld\n", acc, t1 - t0);
return 0;
}

Insertion sort of a small array, repeatedly.

Lines: xc 25, Objective-C 28, C++ 30, Swift 29.

// sort_small — insertion sort of a small array, repeatedly.
// Exercises: nested loops, data-dependent branches, swaps.
#import "Stdio.xc"
#import "include/bench_time.xc"
#define N 64
i32 main(i32 argc, u8** argv)
{
u32 a[N]; u32 seed = (u32)argc; u32 acc = (u32)0;
i64 t0 = bench_now_us();
for (u32 r = (u32)0; r < (u32)3200000; r++)
{
for (u32 i = (u32)0; i < (u32)N; i++)
a[i] = ((i * (u32)2654435761) ^ (r * (u32)40503)) + seed;
for (u32 i = (u32)1; i < (u32)N; i++)
{
u32 v = a[i]; u32 j = i;
while (j > (u32)0 && a[j - (u32)1] > v) { a[j] = a[j - (u32)1]; j = j - (u32)1; }
a[j] = v;
}
acc = acc + a[0] + a[N - 1];
}
i64 t1 = bench_now_us();
Stdio.printf("%lu %lld\n", acc, t1 - t0);
return 0;
}

Scan bytes for a delimiter and checksum them.

Lines: xc 22, Objective-C 24, C++ 26, Swift 23.

// string_scan — scan bytes for a delimiter and checksum them.
// Exercises: byte loads, comparisons, narrow-type arithmetic.
#import "Stdio.xc"
#import "include/bench_time.xc"
#define N 8192
i32 main(i32 argc, u8** argv)
{
u8 buf[N]; u32 seed = (u32)argc; u32 acc = (u32)0;
for (u32 i = (u32)0; i < (u32)N; i++)
buf[i] = (u8)(((i * (u32)31) + seed) & (u32)127);
i64 t0 = bench_now_us();
for (u32 r = (u32)0; r < (u32)3800000; r++)
{
u32 n = (u32)0;
for (u32 i = (u32)0; i < (u32)N; i++)
{ if (buf[i] == (u8)44) n = n + (u32)1; acc = acc + (u32)buf[i]; }
acc = acc + n;
}
i64 t1 = bench_now_us();
Stdio.printf("%lu %lld\n", acc, t1 - t0);
return 0;
}

Pass and return a small struct by value.

Lines: xc 17, Objective-C 19, C++ 23, Swift 26.

// struct_copy — pass and return a small struct by value.
// Exercises: struct copy semantics, field layout, argument passing.
#import "Stdio.xc"
#import "include/bench_time.xc"
struct Pt { u32 x, y; }
Pt bump(Pt p, u32 d) { Pt q; q.x = p.x + d; q.y = p.y ^ d; return q; }
i32 main(i32 argc, u8** argv)
{
u32 seed = (u32)argc; u32 acc = (u32)0;
Pt p; p.x = seed; p.y = seed;
i64 t0 = bench_now_us();
for (u32 r = (u32)0; r < (u32)2200000000; r++)
{ p = bump(p, r); acc = acc + p.x + p.y; }
i64 t1 = bench_now_us();
Stdio.printf("%lu %lld\n", acc, t1 - t0);
return 0;
}

The programs behind the performance page’s GPU tables. Each is one xc program: its work is a par block, a loop whose iterations are independent, and the runtime runs it on the CPU’s threads or on the GPU (Metal on a Mac, CUDA or Vulkan on Windows, Vulkan on Linux and Android, WebGPU in the browser), whichever it finds faster. Nothing in the source names a device, a kernel language or a thread.

A deliberately compute-heavy escape-time iteration that also

Lines: 66.

// mandel_deep — a deliberately compute-heavy escape-time iteration that also
// writes its result, so a second GPU can help. Two things have to be true for
// the tile queue to spread a block across cards: the block must be big enough
// that the per-tile cost is amortised (the light `mandelbrot` finishes in about
// a millisecond, far too little), and it must move per-item data, because the
// queue cuts tiles by the bytes they move — a block that only reduces to a
// scalar is one tile, and one tile runs on one GPU. So this renders a 4096x4096
// iteration-count image (a 64 MB array it writes) at up to 4096 iterations,
// over a window biased onto the set's body so most pixels run the full count:
// uniform, heavy work with data to tile. One item per pixel. Prints
// "<checksum> <best_us> <first_us>"; the checksum is the total iteration count
// to the nearest million, coarse enough that fast GPU maths cannot move it.
#import "Stdio.xc"
#import "Par.xc"
#import "../src/include/bench_time.xc"
#ifndef W
#define W 4096
#endif
#ifndef MAXITER
#define MAXITER 4096
#endif
#define SIZE (W * W)
u32 counts[SIZE];
i32 main(void)
{
u32 total = (u32)0;
i64 best = (i64)0;
i64 first = (i64)0;
for (u32 rep in 0..8)
{
total = (u32)0;
i64 t0 = bench_now_us();
par mandel :reduce(+ total)
{
for (u32 i in 0..SIZE)
{
// The window [-1.5, 0.0] x [-0.75, 0.75]: most pixels are inside
// the set and so run the full iteration count.
float cx = (float)(i % (u32)W) * (1.5f / (float)W) - 1.5f;
float cy = (float)(i / (u32)W) * (1.5f / (float)W) - 0.75f;
float x = 0.0f;
float y = 0.0f;
u32 k = (u32)0;
while (k < (u32)MAXITER && x * x + y * y < 4.0f)
{
float xt = x * x - y * y + cx;
y = 2.0f * x * y + cy;
x = xt;
k = k + (u32)1;
}
counts[i] = k;
total = total + k;
}
}
i64 t1 = bench_now_us();
if (rep == (u32)0)
first = t1 - t0;
if (rep == (u32)0 || t1 - t0 < best)
best = t1 - t0;
}
Stdio.printf("%u %lld %lld\n", (total + (u32)500000) / (u32)1000000, best, first);
return 0;
}

Escape-time iteration counts over a 2048x2048 grid, one item

Lines: 51.

// mandelbrot — escape-time iteration counts over a 2048x2048 grid, one item
// per pixel: arithmetic only, with a data-dependent loop. Prints
// "<checksum> <best_us> <first_us>"; the checksum is the total iteration count to
// the nearest million, coarse enough that fast GPU maths cannot move it.
#import "Stdio.xc"
#import "Par.xc"
#import "../src/include/bench_time.xc"
#define W 2048
#define SIZE (2048 * 2048)
i32 main(void)
{
// Eight runs: the first carries one-off costs (a GPU builds its kernel
// then), so the figure is the best run; the first is printed too. Auto
// decides after its fourth (two on each device), and uses the rest.
u32 total = (u32)0;
i64 best = (i64)0;
i64 first = (i64)0;
for (u32 rep in 0..8)
{
total = (u32)0;
i64 t0 = bench_now_us();
par mandel :reduce(+ total)
{
for (u32 i in 0..SIZE)
{
float cx = (float)(i % (u32)W) * (3.0f / 2048.0f) - 2.0f;
float cy = (float)(i / (u32)W) * (3.0f / 2048.0f) - 1.5f;
float x = 0.0f;
float y = 0.0f;
u32 k = (u32)0;
while (k < (u32)256 && x * x + y * y < 4.0f)
{
float xt = x * x - y * y + cx;
y = 2.0f * x * y + cy;
x = xt;
k = k + (u32)1;
}
total = total + k;
}
}
i64 t1 = bench_now_us();
if (rep == (u32)0)
first = t1 - t0;
if (rep == (u32)0 || t1 - t0 < best)
best = t1 - t0;
}
Stdio.printf("%u %lld %lld\n", (total + (u32)500000) / (u32)1000000, best, first);
return 0;
}

Hashcat’s MD5 workload in xc. Each item hashes one 8-byte candidate

Lines: 306.

// md5 — hashcat's MD5 workload in xc. Each item hashes one 8-byte candidate
// through the full MD5 compression (64 rounds over a single 512-bit block),
// the way hashcat's -m 0 kernel does: short candidates, one block, raw
// throughput measured in hashes per second.
//
// The hash runs inside a `par` block, so the device is the compiler's to
// choose: XC_PAR=cpu one CPU thread or all of them, XC_PAR=gpu the GPU
// (Metal, Vulkan or CUDA), the default auto measures both and keeps the
// faster. `md5 -t` runs the RFC 1321 known-answer tests on the CPU; the same
// MD5_ROUNDS drives the test and the kernel, so passing the test proves the
// code the GPU runs.
//
// The benchmark prints "<checksum> <best_us> <first_us>": the sum of every
// candidate's four digest words (exact, so every mode must agree), the best
// of eight runs, and the first run (which carries the one-off cost of a GPU
// building its kernel). A hashes-per-second line goes to stderr.
#import "Stdio.xc"
#import "Par.xc"
// Headline numbers go to stderr, so stdout stays the three-token line the par
// benchmarks share. write() is the libSystem call Stdio itself uses. The
// wasm32 browser build reads stdout only, so it leaves all this out.
#if !ARCH_wasm32
i32 write(i32 fd, u8* buf, i32 len);
void hc_estr(u8* s)
{
i32 n = 0;
while (s[n] != 0)
n = n + 1;
write(2, s, n);
}
void hc_eu64(u64 v)
{
u8 buf[24];
i32 start = 24;
if (v == 0)
{
start = start - 1;
buf[start] = '0';
}
while (v > 0)
{
start = start - 1;
buf[start] = (u8)('0' + (v % 10));
v = v / 10;
}
write(2, &buf[start], 24 - start);
}
#endif
// A monotonic microsecond clock, measured the same way on every target.
#if ARCH_win64
i32 QueryPerformanceCounter(i64* count);
i32 QueryPerformanceFrequency(i64* freq);
#else
i32 clock_gettime(i32 clk, pointer ts); // the library's signature (ParDevice.xc, RunLoop.xc)
#endif
struct HcSpec { i64 sec; i64 nsec; }
i64 hc_now_us(void)
{
#if ARCH_win64
i64 c = 0;
i64 f = 1;
QueryPerformanceCounter(&c);
QueryPerformanceFrequency(&f);
return (c / f) * 1000000 + ((c % f) * 1000000) / f;
#else
HcSpec ts;
#if ARCH_x86_64
clock_gettime(1, (pointer)&ts);
#else
clock_gettime(6, (pointer)&ts);
#endif
return (ts.sec * 1000000) + (ts.nsec / 1000);
#endif
}
// Candidates per par launch: large enough to saturate a GPU (at 16M the run
// is too short and launch overhead dominates; 64M reaches within a few
// percent of the peak). Each is one full MD5 compression. A serial CPU run
// of this size takes a few seconds per repetition; the default auto run
// picks the GPU and stays fast.
#define BATCH (64 * 1024 * 1024)
// The 32-bit word packed into bytes 4..7 of every candidate, so the input
// looks like an 8-byte password that varies only in its first word.
#define TAG 0x6b636168
// MD5 primitives: left rotate and the four round functions.
#define ROTL(x, c) (((x) << (c)) | ((x) >> (32 - (c))))
#define MD5F(x, y, z) (((x) & (y)) | (~(x) & (z)))
#define MD5G(x, y, z) (((x) & (z)) | ((y) & ~(z)))
#define MD5H(x, y, z) ((x) ^ (y) ^ (z))
#define MD5I(x, y, z) ((y) ^ ((x) | ~(z)))
// One step: the updated word becomes b + ((a + f(b,c,d) + m + k) rotl s).
#define FF(a, b, c, d, m, s, k) a = b + ROTL(a + MD5F(b, c, d) + (m) + (k), s)
#define GG(a, b, c, d, m, s, k) a = b + ROTL(a + MD5G(b, c, d) + (m) + (k), s)
#define HH(a, b, c, d, m, s, k) a = b + ROTL(a + MD5H(b, c, d) + (m) + (k), s)
#define II(a, b, c, d, m, s, k) a = b + ROTL(a + MD5I(b, c, d) + (m) + (k), s)
// The 64 steps over the message words m0..m15, mutating the working words
// a,b,c,d. The caller sets a,b,c,d to the IV first and adds the IV back
// afterwards.
#define MD5_ROUNDS \
FF(a, b, c, d, m0, 7, 0xd76aa478); \
FF(d, a, b, c, m1, 12, 0xe8c7b756); \
FF(c, d, a, b, m2, 17, 0x242070db); \
FF(b, c, d, a, m3, 22, 0xc1bdceee); \
FF(a, b, c, d, m4, 7, 0xf57c0faf); \
FF(d, a, b, c, m5, 12, 0x4787c62a); \
FF(c, d, a, b, m6, 17, 0xa8304613); \
FF(b, c, d, a, m7, 22, 0xfd469501); \
FF(a, b, c, d, m8, 7, 0x698098d8); \
FF(d, a, b, c, m9, 12, 0x8b44f7af); \
FF(c, d, a, b, m10, 17, 0xffff5bb1); \
FF(b, c, d, a, m11, 22, 0x895cd7be); \
FF(a, b, c, d, m12, 7, 0x6b901122); \
FF(d, a, b, c, m13, 12, 0xfd987193); \
FF(c, d, a, b, m14, 17, 0xa679438e); \
FF(b, c, d, a, m15, 22, 0x49b40821); \
GG(a, b, c, d, m1, 5, 0xf61e2562); \
GG(d, a, b, c, m6, 9, 0xc040b340); \
GG(c, d, a, b, m11, 14, 0x265e5a51); \
GG(b, c, d, a, m0, 20, 0xe9b6c7aa); \
GG(a, b, c, d, m5, 5, 0xd62f105d); \
GG(d, a, b, c, m10, 9, 0x02441453); \
GG(c, d, a, b, m15, 14, 0xd8a1e681); \
GG(b, c, d, a, m4, 20, 0xe7d3fbc8); \
GG(a, b, c, d, m9, 5, 0x21e1cde6); \
GG(d, a, b, c, m14, 9, 0xc33707d6); \
GG(c, d, a, b, m3, 14, 0xf4d50d87); \
GG(b, c, d, a, m8, 20, 0x455a14ed); \
GG(a, b, c, d, m13, 5, 0xa9e3e905); \
GG(d, a, b, c, m2, 9, 0xfcefa3f8); \
GG(c, d, a, b, m7, 14, 0x676f02d9); \
GG(b, c, d, a, m12, 20, 0x8d2a4c8a); \
HH(a, b, c, d, m5, 4, 0xfffa3942); \
HH(d, a, b, c, m8, 11, 0x8771f681); \
HH(c, d, a, b, m11, 16, 0x6d9d6122); \
HH(b, c, d, a, m14, 23, 0xfde5380c); \
HH(a, b, c, d, m1, 4, 0xa4beea44); \
HH(d, a, b, c, m4, 11, 0x4bdecfa9); \
HH(c, d, a, b, m7, 16, 0xf6bb4b60); \
HH(b, c, d, a, m10, 23, 0xbebfbc70); \
HH(a, b, c, d, m13, 4, 0x289b7ec6); \
HH(d, a, b, c, m0, 11, 0xeaa127fa); \
HH(c, d, a, b, m3, 16, 0xd4ef3085); \
HH(b, c, d, a, m6, 23, 0x04881d05); \
HH(a, b, c, d, m9, 4, 0xd9d4d039); \
HH(d, a, b, c, m12, 11, 0xe6db99e5); \
HH(c, d, a, b, m15, 16, 0x1fa27cf8); \
HH(b, c, d, a, m2, 23, 0xc4ac5665); \
II(a, b, c, d, m0, 6, 0xf4292244); \
II(d, a, b, c, m7, 10, 0x432aff97); \
II(c, d, a, b, m14, 15, 0xab9423a7); \
II(b, c, d, a, m5, 21, 0xfc93a039); \
II(a, b, c, d, m12, 6, 0x655b59c3); \
II(d, a, b, c, m3, 10, 0x8f0ccc92); \
II(c, d, a, b, m10, 15, 0xffeff47d); \
II(b, c, d, a, m1, 21, 0x85845dd1); \
II(a, b, c, d, m8, 6, 0x6fa87e4f); \
II(d, a, b, c, m15, 10, 0xfe2ce6e0); \
II(c, d, a, b, m6, 15, 0xa3014314); \
II(b, c, d, a, m13, 21, 0x4e0811a1); \
II(a, b, c, d, m4, 6, 0xf7537e82); \
II(d, a, b, c, m11, 10, 0xbd3af235); \
II(c, d, a, b, m2, 15, 0x2ad7d2bb); \
II(b, c, d, a, m9, 21, 0xeb86d391)
// MD5 of a message shorter than 56 bytes (one padded block), on the CPU.
// Writes the 32 hex digits of the digest through `out` (no NUL).
void md5_hex(u8* msg, u32 len, u8* out)
{
u32 w[16];
for (u32 i in 0..16)
w[i] = 0;
for (u32 i in 0..len)
w[i >> 2] = w[i >> 2] | ((u32)msg[i] << ((i & 3) * 8));
w[len >> 2] = w[len >> 2] | ((u32)0x80 << ((len & 3) * 8));
w[14] = len * 8;
u32 m0 = w[0]; u32 m1 = w[1]; u32 m2 = w[2]; u32 m3 = w[3];
u32 m4 = w[4]; u32 m5 = w[5]; u32 m6 = w[6]; u32 m7 = w[7];
u32 m8 = w[8]; u32 m9 = w[9]; u32 m10 = w[10]; u32 m11 = w[11];
u32 m12 = w[12]; u32 m13 = w[13]; u32 m14 = w[14]; u32 m15 = w[15];
u32 a = 0x67452301;
u32 b = 0xefcdab89;
u32 c = 0x98badcfe;
u32 d = 0x10325476;
MD5_ROUNDS;
a = a + 0x67452301;
b = b + 0xefcdab89;
c = c + 0x98badcfe;
d = d + 0x10325476;
u32 word[4];
word[0] = a; word[1] = b; word[2] = c; word[3] = d;
u8* hexd = "0123456789abcdef";
u32 o = 0;
for (u32 wi in 0..4)
for (u32 bi in 0..4)
{
u32 byte = (word[wi] >> (bi * 8)) & 0xff;
out[o] = hexd[byte >> 4];
out[o + 1] = hexd[byte & 0xf];
o = o + 2;
}
}
i32 selftest(void)
{
u8 got[33];
got[32] = 0;
u8* msgs[4];
msgs[0] = "";
msgs[1] = "abc";
msgs[2] = "message digest";
msgs[3] = "abcdefghijklmnopqrstuvwxyz";
u32 lens[4];
lens[0] = 0; lens[1] = 3; lens[2] = 14; lens[3] = 26;
u8* want[4];
want[0] = "d41d8cd98f00b204e9800998ecf8427e";
want[1] = "900150983cd24fb0d6963f7d28e17f72";
want[2] = "f96b697d7cb7938d525a2f31aaf161d0";
want[3] = "c3fcd3d76192e4007dfb496cca67e13b";
i32 fails = 0;
for (u32 i in 0..4)
{
md5_hex(msgs[i], lens[i], got);
bool ok = true;
for (u32 j in 0..32)
if (got[j] != want[i][j])
ok = false;
Stdio.printf("md5(\"%s\") = %s %s\n", msgs[i], (u8*)got, ok ? "ok" : "FAIL");
if (!ok)
fails = fails + 1;
}
return fails;
}
i32 main(i32 argc, u8** argv)
{
if (argc > 1 && argv[1][0] == '-' && argv[1][1] == 't')
return selftest() == 0 ? 0 : 1;
u32 total = 0;
i64 best = 0;
i64 first = 0;
for (u32 rep in 0..8)
{
total = 0;
i64 t0 = hc_now_us();
par md5 :reduce(+ total)
{
for (u32 i in 0..BATCH)
{
// The candidate: word 0 is the item index, word 1 the tag,
// then MD5 padding for an 8-byte message (0x80, then zeros,
// then the 64-bit length).
u32 m0 = i;
u32 m1 = TAG;
u32 m2 = 0x80;
u32 m3 = 0; u32 m4 = 0; u32 m5 = 0;
u32 m6 = 0; u32 m7 = 0; u32 m8 = 0;
u32 m9 = 0; u32 m10 = 0; u32 m11 = 0;
u32 m12 = 0; u32 m13 = 0; u32 m14 = 64;
u32 m15 = 0;
u32 a = 0x67452301;
u32 b = 0xefcdab89;
u32 c = 0x98badcfe;
u32 d = 0x10325476;
MD5_ROUNDS;
total = total + (a + 0x67452301) + (b + 0xefcdab89)
+ (c + 0x98badcfe) + (d + 0x10325476);
}
}
i64 t1 = hc_now_us();
if (rep == 0)
first = t1 - t0;
if (rep == 0 || t1 - t0 < best)
best = t1 - t0;
}
Stdio.printf("%u %lld %lld\n", total, best, first);
// Hashes per second from the best run, exact integer division. Skipped on
// wasm32, where the browser harness reads stdout and there is no stderr.
#if !ARCH_wasm32
u64 hs = best > 0 ? ((u64)BATCH * 1000000) / (u64)best : 0;
hc_estr("md5: ");
hc_eu64(hs);
hc_estr(" H/s (best run ");
hc_eu64((u64)best);
hc_estr(" us for ");
hc_eu64((u64)BATCH);
hc_estr(" candidates)\n");
#endif
return 0;
}

Hashcat’s MASK-ATTACK MD5 workload in xc. Unlike md5.xc (which

Lines: 298.

// md5_mask — hashcat's MASK-ATTACK MD5 workload in xc. Unlike md5.xc (which
// hashes one short candidate per item), this mirrors `-a 3`: a fixed prefix
// and one sweeping mask position. The MD5 steps before the mask word are
// loop-invariant, so they run once per prefix, not once per candidate -- the
// optimisation behind hashcat's faster optimized kernel.
//
// Each candidate is 52 bytes: m0..m11 the prefix (m0 = the outer index, the
// rest fixed), m12 the mask word (the inner sweep), then MD5 padding. The par
// block runs the outer loop over prefixes; for each it computes steps 0..11
// once, then the inner loop runs steps 12..63 per candidate. `md5_mask -t`
// checks one candidate's full digest against a known value.
#import "Stdio.xc"
#import "Par.xc"
i32 write(i32 fd, u8* buf, i32 len);
void hc_estr(u8* s) { i32 n = 0; while (s[n] != 0) n = n + 1; write(2, s, n); }
void hc_eu64(u64 v)
{
u8 buf[24]; i32 start = 24;
if (v == 0) { start = start - 1; buf[start] = '0'; }
while (v > 0) { start = start - 1; buf[start] = (u8)('0' + (v % 10)); v = v / 10; }
write(2, &buf[start], 24 - start);
}
#if ARCH_win64
i32 QueryPerformanceCounter(i64* count);
i32 QueryPerformanceFrequency(i64* freq);
#else
i32 clock_gettime(i32 clk, pointer ts); // the library's signature (ParDevice.xc, RunLoop.xc)
#endif
struct HcSpec { i64 sec; i64 nsec; }
i64 hc_now_us(void)
{
#if ARCH_win64
i64 c = 0; i64 f = 1; QueryPerformanceCounter(&c); QueryPerformanceFrequency(&f);
return (c / f) * 1000000 + ((c % f) * 1000000) / f;
#else
HcSpec ts;
#if ARCH_x86_64
clock_gettime(1, (pointer)&ts);
#else
clock_gettime(6, (pointer)&ts);
#endif
return (ts.sec * 1000000) + (ts.nsec / 1000);
#endif
}
// Prefixes per launch, and mask values per prefix. Total candidates is their
// product; each is one 52-byte MD5.
#define OUTER (1024 * 1024)
#define INNER 64
// The 11 fixed prefix words m1..m11 (m0 is the outer index, m12 the mask).
#define P1 0x6b636168
#define P2 0x74736168
#define P3 0x6d786378
#define P4 0x31323334
#define P5 0x35363738
#define P6 0x61626364
#define P7 0x65666768
#define P8 0x696a6b6c
#define P9 0x6d6e6f70
#define P10 0x71727374
#define P11 0x75767778
// MD5 primitives (as md5.xc).
#define ROTL(x, c) (((x) << (c)) | ((x) >> (32 - (c))))
#define MD5F(x, y, z) ((z) ^ ((x) & ((y) ^ (z))))
#define MD5G(x, y, z) ((y) ^ ((z) & ((x) ^ (y))))
#define MD5H(x, y, z) ((x) ^ (y) ^ (z))
#define MD5I(x, y, z) ((y) ^ ((x) | ~(z)))
#define FF(a, b, c, d, m, s, k) a = b + ROTL(a + MD5F(b, c, d) + (m) + (k), s)
#define GG(a, b, c, d, m, s, k) a = b + ROTL(a + MD5G(b, c, d) + (m) + (k), s)
#define HH(a, b, c, d, m, s, k) a = b + ROTL(a + MD5H(b, c, d) + (m) + (k), s)
#define II(a, b, c, d, m, s, k) a = b + ROTL(a + MD5I(b, c, d) + (m) + (k), s)
#define MD5_PREFIX(a, b, c, d) \
FF(a, b, c, d, m0, 7, 0xd76aa478); \
FF(d, a, b, c, m1, 12, 0xe8c7b756); \
FF(c, d, a, b, m2, 17, 0x242070db); \
FF(b, c, d, a, m3, 22, 0xc1bdceee); \
FF(a, b, c, d, m4, 7, 0xf57c0faf); \
FF(d, a, b, c, m5, 12, 0x4787c62a); \
FF(c, d, a, b, m6, 17, 0xa8304613); \
FF(b, c, d, a, m7, 22, 0xfd469501); \
FF(a, b, c, d, m8, 7, 0x698098d8); \
FF(d, a, b, c, m9, 12, 0x8b44f7af); \
FF(c, d, a, b, m10, 17, 0xffff5bb1); \
FF(b, c, d, a, m11, 22, 0x895cd7be)
#define MD5_REST(a, b, c, d) \
FF(a, b, c, d, m12, 7, 0x6b901122); \
FF(d, a, b, c, m13, 12, 0xfd987193); \
FF(c, d, a, b, m14, 17, 0xa679438e); \
FF(b, c, d, a, m15, 22, 0x49b40821); \
GG(a, b, c, d, m1, 5, 0xf61e2562); \
GG(d, a, b, c, m6, 9, 0xc040b340); \
GG(c, d, a, b, m11, 14, 0x265e5a51); \
GG(b, c, d, a, m0, 20, 0xe9b6c7aa); \
GG(a, b, c, d, m5, 5, 0xd62f105d); \
GG(d, a, b, c, m10, 9, 0x02441453); \
GG(c, d, a, b, m15, 14, 0xd8a1e681); \
GG(b, c, d, a, m4, 20, 0xe7d3fbc8); \
GG(a, b, c, d, m9, 5, 0x21e1cde6); \
GG(d, a, b, c, m14, 9, 0xc33707d6); \
GG(c, d, a, b, m3, 14, 0xf4d50d87); \
GG(b, c, d, a, m8, 20, 0x455a14ed); \
GG(a, b, c, d, m13, 5, 0xa9e3e905); \
GG(d, a, b, c, m2, 9, 0xfcefa3f8); \
GG(c, d, a, b, m7, 14, 0x676f02d9); \
GG(b, c, d, a, m12, 20, 0x8d2a4c8a); \
HH(a, b, c, d, m5, 4, 0xfffa3942); \
HH(d, a, b, c, m8, 11, 0x8771f681); \
HH(c, d, a, b, m11, 16, 0x6d9d6122); \
HH(b, c, d, a, m14, 23, 0xfde5380c); \
HH(a, b, c, d, m1, 4, 0xa4beea44); \
HH(d, a, b, c, m4, 11, 0x4bdecfa9); \
HH(c, d, a, b, m7, 16, 0xf6bb4b60); \
HH(b, c, d, a, m10, 23, 0xbebfbc70); \
HH(a, b, c, d, m13, 4, 0x289b7ec6); \
HH(d, a, b, c, m0, 11, 0xeaa127fa); \
HH(c, d, a, b, m3, 16, 0xd4ef3085); \
HH(b, c, d, a, m6, 23, 0x04881d05); \
HH(a, b, c, d, m9, 4, 0xd9d4d039); \
HH(d, a, b, c, m12, 11, 0xe6db99e5); \
HH(c, d, a, b, m15, 16, 0x1fa27cf8); \
HH(b, c, d, a, m2, 23, 0xc4ac5665); \
II(a, b, c, d, m0, 6, 0xf4292244); \
II(d, a, b, c, m7, 10, 0x432aff97); \
II(c, d, a, b, m14, 15, 0xab9423a7); \
II(b, c, d, a, m5, 21, 0xfc93a039); \
II(a, b, c, d, m12, 6, 0x655b59c3); \
II(d, a, b, c, m3, 10, 0x8f0ccc92); \
II(c, d, a, b, m10, 15, 0xffeff47d); \
II(b, c, d, a, m1, 21, 0x85845dd1); \
II(a, b, c, d, m8, 6, 0x6fa87e4f); \
II(d, a, b, c, m15, 10, 0xfe2ce6e0); \
II(c, d, a, b, m6, 15, 0xa3014314); \
II(b, c, d, a, m13, 21, 0x4e0811a1); \
II(a, b, c, d, m4, 6, 0xf7537e82); \
II(d, a, b, c, m11, 10, 0xbd3af235); \
II(c, d, a, b, m2, 15, 0x2ad7d2bb); \
II(b, c, d, a, m9, 21, 0xeb86d391)
#define MD5_FULL(a, b, c, d) \
FF(a, b, c, d, m0, 7, 0xd76aa478); \
FF(d, a, b, c, m1, 12, 0xe8c7b756); \
FF(c, d, a, b, m2, 17, 0x242070db); \
FF(b, c, d, a, m3, 22, 0xc1bdceee); \
FF(a, b, c, d, m4, 7, 0xf57c0faf); \
FF(d, a, b, c, m5, 12, 0x4787c62a); \
FF(c, d, a, b, m6, 17, 0xa8304613); \
FF(b, c, d, a, m7, 22, 0xfd469501); \
FF(a, b, c, d, m8, 7, 0x698098d8); \
FF(d, a, b, c, m9, 12, 0x8b44f7af); \
FF(c, d, a, b, m10, 17, 0xffff5bb1); \
FF(b, c, d, a, m11, 22, 0x895cd7be); \
FF(a, b, c, d, m12, 7, 0x6b901122); \
FF(d, a, b, c, m13, 12, 0xfd987193); \
FF(c, d, a, b, m14, 17, 0xa679438e); \
FF(b, c, d, a, m15, 22, 0x49b40821); \
GG(a, b, c, d, m1, 5, 0xf61e2562); \
GG(d, a, b, c, m6, 9, 0xc040b340); \
GG(c, d, a, b, m11, 14, 0x265e5a51); \
GG(b, c, d, a, m0, 20, 0xe9b6c7aa); \
GG(a, b, c, d, m5, 5, 0xd62f105d); \
GG(d, a, b, c, m10, 9, 0x02441453); \
GG(c, d, a, b, m15, 14, 0xd8a1e681); \
GG(b, c, d, a, m4, 20, 0xe7d3fbc8); \
GG(a, b, c, d, m9, 5, 0x21e1cde6); \
GG(d, a, b, c, m14, 9, 0xc33707d6); \
GG(c, d, a, b, m3, 14, 0xf4d50d87); \
GG(b, c, d, a, m8, 20, 0x455a14ed); \
GG(a, b, c, d, m13, 5, 0xa9e3e905); \
GG(d, a, b, c, m2, 9, 0xfcefa3f8); \
GG(c, d, a, b, m7, 14, 0x676f02d9); \
GG(b, c, d, a, m12, 20, 0x8d2a4c8a); \
HH(a, b, c, d, m5, 4, 0xfffa3942); \
HH(d, a, b, c, m8, 11, 0x8771f681); \
HH(c, d, a, b, m11, 16, 0x6d9d6122); \
HH(b, c, d, a, m14, 23, 0xfde5380c); \
HH(a, b, c, d, m1, 4, 0xa4beea44); \
HH(d, a, b, c, m4, 11, 0x4bdecfa9); \
HH(c, d, a, b, m7, 16, 0xf6bb4b60); \
HH(b, c, d, a, m10, 23, 0xbebfbc70); \
HH(a, b, c, d, m13, 4, 0x289b7ec6); \
HH(d, a, b, c, m0, 11, 0xeaa127fa); \
HH(c, d, a, b, m3, 16, 0xd4ef3085); \
HH(b, c, d, a, m6, 23, 0x04881d05); \
HH(a, b, c, d, m9, 4, 0xd9d4d039); \
HH(d, a, b, c, m12, 11, 0xe6db99e5); \
HH(c, d, a, b, m15, 16, 0x1fa27cf8); \
HH(b, c, d, a, m2, 23, 0xc4ac5665); \
II(a, b, c, d, m0, 6, 0xf4292244); \
II(d, a, b, c, m7, 10, 0x432aff97); \
II(c, d, a, b, m14, 15, 0xab9423a7); \
II(b, c, d, a, m5, 21, 0xfc93a039); \
II(a, b, c, d, m12, 6, 0x655b59c3); \
II(d, a, b, c, m3, 10, 0x8f0ccc92); \
II(c, d, a, b, m10, 15, 0xffeff47d); \
II(b, c, d, a, m1, 21, 0x85845dd1); \
II(a, b, c, d, m8, 6, 0x6fa87e4f); \
II(d, a, b, c, m15, 10, 0xfe2ce6e0); \
II(c, d, a, b, m6, 15, 0xa3014314); \
II(b, c, d, a, m13, 21, 0x4e0811a1); \
II(a, b, c, d, m4, 6, 0xf7537e82); \
II(d, a, b, c, m11, 10, 0xbd3af235); \
II(c, d, a, b, m2, 15, 0x2ad7d2bb); \
II(b, c, d, a, m9, 21, 0xeb86d391)
// Full MD5 of the candidate (p, mask), 52 bytes, on the CPU, as 32 hex digits.
void md5_mask_hex(u32 p, u32 mask, u8* out)
{
u32 m0 = p; u32 m1 = P1; u32 m2 = P2; u32 m3 = P3;
u32 m4 = P4; u32 m5 = P5; u32 m6 = P6; u32 m7 = P7;
u32 m8 = P8; u32 m9 = P9; u32 m10 = P10; u32 m11 = P11;
u32 m12 = mask; u32 m13 = 0x80; u32 m14 = 416; u32 m15 = 0;
u32 a = 0x67452301; u32 b = 0xefcdab89; u32 c = 0x98badcfe; u32 d = 0x10325476;
MD5_FULL(a, b, c, d);
a = a + 0x67452301; b = b + 0xefcdab89; c = c + 0x98badcfe; d = d + 0x10325476;
u32 word[4];
word[0] = a; word[1] = b; word[2] = c; word[3] = d;
u8* hexd = "0123456789abcdef";
u32 o = 0;
for (u32 wi in 0..4)
for (u32 bi in 0..4)
{
u32 byte = (word[wi] >> (bi * 8)) & 0xff;
out[o] = hexd[byte >> 4];
out[o + 1] = hexd[byte & 0xf];
o = o + 2;
}
}
i32 selftest(void)
{
u8 got[33];
got[32] = 0;
md5_mask_hex(0, 0, got);
u8* want = "8ac92423f2a00548a06836464f84346e";
bool ok = true;
for (u32 j in 0..32)
if (got[j] != want[j])
ok = false;
Stdio.printf("md5_mask(p=0,mask=0) = %s %s\n", (u8*)got, ok ? "ok" : "FAIL");
return ok ? 0 : 1;
}
i32 main(i32 argc, u8** argv)
{
if (argc > 1 && argv[1][0] == '-' && argv[1][1] == 't')
return selftest();
u32 total = 0;
i64 best = 0;
i64 first = 0;
for (u32 rep in 0..8)
{
total = 0;
i64 t0 = hc_now_us();
par md5mask :reduce(+ total)
{
for (u32 p in 0..OUTER)
{
u32 m0 = p; u32 m1 = P1; u32 m2 = P2; u32 m3 = P3;
u32 m4 = P4; u32 m5 = P5; u32 m6 = P6; u32 m7 = P7;
u32 m8 = P8; u32 m9 = P9; u32 m10 = P10; u32 m11 = P11;
u32 m13 = 0x80; u32 m14 = 416; u32 m15 = 0;
// The invariant prefix: steps 0..11, once per outer item.
u32 A = 0x67452301; u32 B = 0xefcdab89;
u32 C = 0x98badcfe; u32 D = 0x10325476;
MD5_PREFIX(A, B, C, D);
for (u32 j in 0..INNER)
{
u32 m12 = p * INNER + j;
u32 a = A; u32 b = B; u32 c = C; u32 d = D;
MD5_REST(a, b, c, d);
total = total + (a + 0x67452301) + (b + 0xefcdab89)
+ (c + 0x98badcfe) + (d + 0x10325476);
}
}
}
i64 t1 = hc_now_us();
if (rep == 0) first = t1 - t0;
if (rep == 0 || t1 - t0 < best) best = t1 - t0;
}
Stdio.printf("%u %lld %lld\n", total, best, first);
#if !ARCH_wasm32
u64 cand = (u64)OUTER * (u64)INNER;
u64 hs = best > 0 ? (cand * 1000000) / (u64)best : 0;
hc_estr("md5_mask: "); hc_eu64(hs); hc_estr(" H/s (best run ");
hc_eu64((u64)best); hc_estr(" us for "); hc_eu64(cand); hc_estr(" candidates)\n");
#endif
return 0;
}

The gravitational force on each of 8192 bodies from all the others:

Lines: 63.

// nbody — the gravitational force on each of 8192 bodies from all the others:
// O(n^2) arithmetic over small arrays, one item per body. Prints
// "<checksum> <best_us> <first_us>"; the checksum counts the bodies pulled right
// (positive x force), a coarse figure fast GPU maths does not move.
#import "Stdio.xc"
#import "Math.xc"
#import "Par.xc"
#import "../src/include/bench_time.xc"
#define N 8192
float px[N];
float py[N];
float mass[N];
float fx[N];
i32 main(void)
{
u32 seed = (u32)7;
for (u32 i in 0..N)
{
seed = seed * (u32)1664525 + (u32)1013904223;
px[i] = (float)(seed >> (u32)8) / 16777216.0f;
seed = seed * (u32)1664525 + (u32)1013904223;
py[i] = (float)(seed >> (u32)8) / 16777216.0f;
mass[i] = 1.0f + (float)(i % (u32)7);
}
// Eight runs: the first carries one-off costs (a GPU builds its kernel
// then), so the figure is the best run; the first is printed too. Auto
// decides after its fourth (two on each device), and uses the rest.
u32 right = (u32)0;
i64 best = (i64)0;
i64 first = (i64)0;
for (u32 rep in 0..8)
{
right = (u32)0;
i64 t0 = bench_now_us();
par forces :reduce(+ right)
{
for (u32 i in 0..N)
{
float ax = 0.0f;
for (u32 j in 0..N)
{
float dx = px[j] - px[i];
float dy = py[j] - py[i];
float d2 = dx * dx + dy * dy + 0.0001f;
ax = ax + mass[j] * dx / (d2 * Math.sqrt(d2));
}
fx[i] = ax;
if (ax > 0.0f)
right = right + (u32)1;
}
}
i64 t1 = bench_now_us();
if (rep == (u32)0)
first = t1 - t0;
if (rep == (u32)0 || t1 - t0 < best)
best = t1 - t0;
}
Stdio.printf("%u %lld %lld\n", (right + (u32)50) / (u32)100, best, first);
return 0;
}

Ken Perlin’s improved noise, 2D, four octaves, over a 2048x2048

Lines: 102.

// perlin — Ken Perlin's improved noise, 2D, four octaves, over a 2048x2048
// image: a permutation table read at data-dependent places, helper calls,
// and one store per pixel. Prints "<checksum> <best_us> <first_us>"; the checksum is
// the mean grey level to a tenth.
#import "Stdio.xc"
#import "Par.xc"
#import "../src/include/bench_time.xc"
#define SIZE (2048 * 2048)
u32 perm[512];
u8 img[SIZE];
float fade(float t) { return t * t * t * (t * (t * 6.0f - 15.0f) + 10.0f); }
float lerp(float t, float a, float b) { return a + t * (b - a); }
float grad(u32 h, float x, float y)
{
u32 g = h & (u32)7;
float u = g < (u32)4 ? x : y;
float v = g < (u32)4 ? y : x;
float a = (g & (u32)1) != (u32)0 ? 0.0f - u : u;
float b = (g & (u32)2) != (u32)0 ? 0.0f - 2.0f * v : 2.0f * v;
return a + b;
}
i32 main(void)
{
u32 seed = (u32)12345;
for (u32 i in 0..256)
perm[i] = i;
for (u32 i in 0..256)
{
seed = seed * (u32)1103515245 + (u32)12345;
u32 j = i + (seed >> (u32)16) % ((u32)256 - i);
u32 t = perm[i];
perm[i] = perm[j];
perm[j] = t;
}
for (u32 i in 0..256)
perm[i + (u32)256] = perm[i];
// Eight runs: the first carries one-off costs (a GPU builds its kernel
// then), so the figure is the best run; the first is printed too. Auto
// decides after its fourth (two on each device), and uses the rest.
u32 total = (u32)0;
i64 best = (i64)0;
i64 first = (i64)0;
for (u32 rep in 0..8)
{
total = (u32)0;
i64 t0 = bench_now_us();
par perlin :reduce(+ total)
{
for (u32 i in 0..SIZE)
{
float x = (float)(i % (u32)2048) / 256.0f;
float y = (float)(i / (u32)2048) / 256.0f;
float sum = 0.0f;
float amp = 1.0f;
float norm = 0.0f;
for (u32 o in 0..4)
{
u32 xi = (u32)x;
u32 yi = (u32)y;
float xf = x - (float)xi;
float yf = y - (float)yi;
u32 X = xi & (u32)255;
u32 Y = yi & (u32)255;
u32 aa = perm[perm[X] + Y];
u32 ab = perm[perm[X] + Y + (u32)1];
u32 ba = perm[perm[X + (u32)1] + Y];
u32 bb = perm[perm[X + (u32)1] + Y + (u32)1];
float u = fade(xf);
float v = fade(yf);
float n = lerp(v, lerp(u, grad(aa, xf, yf), grad(ba, xf - 1.0f, yf)),
lerp(u, grad(ab, xf, yf - 1.0f), grad(bb, xf - 1.0f, yf - 1.0f)));
sum = sum + n * amp;
norm = norm + amp;
amp = amp * 0.5f;
x = x * 2.0f;
y = y * 2.0f;
}
float s = (sum / norm) * 0.5f + 0.5f;
if (s < 0.0f)
s = 0.0f;
if (s > 1.0f)
s = 1.0f;
u32 p = (u32)(s * 255.0f);
img[i] = (u8)p;
total = total + p;
}
}
i64 t1 = bench_now_us();
if (rep == (u32)0)
first = t1 - t0;
if (rep == (u32)0 || t1 - t0 < best)
best = t1 - t0;
}
u32 tenths = (total * (u32)10 + (u32)(SIZE / 2)) / (u32)SIZE;
Stdio.printf("%u %lld %lld\n", tenths, best, first);
return 0;
}

Y = a*x + y over 16M integers: one multiply-add per element and

Lines: 48.

// saxpy — y = a*x + y over 16M integers: one multiply-add per element and
// two arrays to move, so memory, not arithmetic, sets the pace. On a GPU the
// copies in and out cost more than the work: this is the block auto keeps on
// the CPU. Prints "<checksum> <best_us> <first_us>"; the checksum is exact.
#import "Stdio.xc"
#import "Par.xc"
#import "../src/include/bench_time.xc"
#define SIZE (16 * 1024 * 1024)
u32 xs[SIZE];
u32 ys[SIZE];
i32 main(void)
{
for (u32 i in 0..SIZE)
{
xs[i] = i * (u32)2654435761;
ys[i] = i;
}
// Eight runs: the first carries one-off costs (a GPU builds its kernel
// then), so the figure is the best run; the first is printed too. Auto
// decides after its fourth (two on each device), and uses the rest.
u32 total = (u32)0;
i64 best = (i64)0;
i64 first = (i64)0;
for (u32 rep in 0..8)
{
total = (u32)0;
i64 t0 = bench_now_us();
par saxpy :reduce(+ total)
{
for (u32 i in 0..SIZE)
{
u32 v = (u32)3 * xs[i] + ys[i];
ys[i] = v;
total = total + v;
}
}
i64 t1 = bench_now_us();
if (rep == (u32)0)
first = t1 - t0;
if (rep == (u32)0 || t1 - t0 < best)
best = t1 - t0;
}
Stdio.printf("%u %lld %lld\n", total, best, first);
return 0;
}