(data from waggle are labled with c_, while zaggle is not labeled; baseline for naively iterating, 4thrd for tiling 4 thread implementation, 4async for tiling 4 std.Io.async implementation;
C version built with -O3, Zig version built with -Doptimize=fast)
You can see that the Zig baseline outperforms the C baseline, even close to 4 threaded C.
I heard that Zig might be faster than C, that it can do more optimization tricks, but didn’t expect this much tbh
Did you compile the C code with Zig, and/or -march=native? And if your C code is spread across multiple source files, are you building with -flto to enable optimization across compilation units?
AFAIK these are the two main differences between C and Zig’s build models. Another smaller issue might be more aggressive optimizations around pointer aliasing in Zig vs C.
But in general, giving the same memory access patterns (e.g. same code structure and data types) and giving the C compiler the same aggressive compilation options that Zig uses by default, I would except nearly byte-by-byte identical output (assuming both compilers are LLVM based) - and that should be true for all compiled languages btw, not just Zig vs C.
Or maybe when on x86 you’re lucky and you hit a sweet spot in Zig’s non-LLVM backend
PS: looking at the code, this may already be the culprit:
… it looks like you’re calling a callback for each pixel from within equa_draw_4thrd, and I guess that function lives in another C source file.
In Zig the per-pixel callback function body will almost certainly be inlined into the caller because Zig builds the entire program as a single compilation unit, while in C it definitely won’t be inlined unless you build with -flto (and even then it’s not guaranteed)… and this inlining may in turn unlock more optimizations (like loop unrolling or vectorization)…