Optimization
Detailed guide to Zig’s optimization capabilities, compiler flags, and performance tuning strategies.
Optimization Modes
Zig’s optimization modes are set with -O and determine the overall compilation strategy. See Compilation Modes for details.
-O Debug # No optimizations, full safety
-O ReleaseSafe # Optimized with safety checks
-O ReleaseFast # Maximum performance, no safety
-O ReleaseSmall # Optimized for binary size
Compiler Backend Selection
LLVM Backend
The LLVM backend provides the most mature and optimized code generation.
-fllvm # Force LLVM backend
-fno-llvm # Prevent LLVM backend
-flibllvm # Use LLVM API
-fno-libllvm # Don't use LLVM API
Build with LLVM
Self-Hosted Backend
zig build-exe -O ReleaseFast -fllvm main.zig
zig build-exe -O ReleaseFast -fno-llvm main.zig
C Backend
-fclang # Force Clang for C/C++ code
-fno-clang # Prevent using Clang
Code Generation Options
Function Sections
Places each function in a separate section, enabling better dead code elimination.
-ffunction-sections # Each function in separate section
-fno-function-sections # All functions in same section
Useful with --gc-sections linker flag:
zig build-exe -ffunction-sections --gc-sections main.zig
Data Sections
-fdata-sections # Each data item in separate section
-fno-data-sections # All data in same section
Position Independent Code (PIC)
-fPIC # Force Position Independent Code
-fno-PIC # Disable PIC
-fPIE # Position Independent Executable
-fno-PIE # Disable PIE
Shared Library
Security Hardened
zig build-lib -dynamic -fPIC mylib.zig
zig build-exe -fPIE main.zig
Link Time Optimization (LTO)
Requires LLVM extensions.
-flto # Full LTO
-fno-lto # Disable LTO (default)
LTO modes:
zig build-exe -O ReleaseFast -flto main.zig
# or explicit:
zig build-exe -O ReleaseFast -flto=full main.zig
LTO Trade-offs
Mode Compile Time Binary Size Performance No LTO Fast Larger Good Thin LTO Medium Medium Better Full LTO Slow Smallest Best
Thin LTO provides a good balance - most of the performance benefits with faster compile times than full LTO.
Frame Pointer
-fomit-frame-pointer # Omit frame pointer (optimization)
-fno-omit-frame-pointer # Keep frame pointer (debugging)
Omitting frame pointer:
Frees up a register for optimizations
Makes stack unwinding harder
Can improve performance by ~1-2%
Keeping frame pointer:
Better stack traces and profiling
Required by some debugging tools
Recommended for production when debugging needed
Stack Features
Stack Checking
-fstack-check # Enable stack probing
-fno-stack-check # Disable stack probing
Detects stack overflows at runtime (small performance cost).
Stack Protection
-fstack-protector # Enable stack canaries
-fno-stack-protector # Disable stack protection
Protects against stack buffer overflows with security canaries.
Red Zone
-mred-zone # Enable red zone optimization
-mno-red-zone # Disable red zone
The red zone is an optimization where leaf functions can use stack space beyond the stack pointer without adjusting it. Required to be disabled for interrupt handlers.
CPU-Specific Options
Target CPU
-mcpu [cpu] # Specify target CPU and features
Native
Specific CPU
Baseline
zig build-exe -mcpu=native main.zig
Optimizes for the current CPU. zig build-exe -mcpu=x86_64_v3 main.zig
Targets x86-64-v3 microarchitecture level. zig build-exe -mcpu=baseline main.zig
Most compatible (default).
Code Model
-mcmodel = [model ] # Set code model
Available models:
tiny - Smallest code model
small - Default for most targets
kernel - Kernel code
medium - Medium-sized programs
large - Large programs
default - Target-specific default
zig build-exe -mcmodel=kernel kernel.zig
Strip Debug Info
-fstrip # Remove debug symbols
-fno-strip # Keep debug symbols (default in Debug)
Stripping reduces binary size by 30-70% but makes debugging impossible.
-gdwarf32 # Use 32-bit DWARF
-gdwarf64 # Use 64-bit DWARF
Debug Compression
--compress-debug-sections = [format ]
Formats:
none - No compression
zlib - zlib compression (default)
zstd - Zstandard compression (better ratio)
zig build-exe --compress-debug-sections=none main.zig
Unwind Tables
-funwind-tables # Sync unwind tables
-fasync-unwind-tables # Async unwind tables
-fno-unwind-tables # No unwind tables
zig c++ -funwind-tables program.cpp
zig build-exe -funwind-tables main.zig
Enables better profiler stack traces.
Sanitizers
UndefinedBehaviorSanitizer (C code)
-fsanitize-c[ =mode] # Enable UBSan for C
-fno-sanitize-c # Disable UBSan
Modes:
trap - Insert trap instructions
full - Insert runtime calls (default)
zig build-exe -fsanitize-c=trap main.zig helper.c
ThreadSanitizer
-fsanitize-thread # Enable ThreadSanitizer
-fno-sanitize-thread # Disable ThreadSanitizer
Detects data races in multithreaded code:
zig build-exe -fsanitize-thread server.zig
Valgrind Support
-fvalgrind # Include Valgrind client requests
-fno-valgrind # Omit Valgrind requests
Useful for memory debugging:
zig build-exe -O ReleaseSafe -fvalgrind main.zig
valgrind ./main
Linker Optimizations
Garbage Collection
--gc-sections # Remove unused code/data
--no-gc-sections # Keep all sections
Combine with -ffunction-sections and -fdata-sections for best results.
zig build-exe -ffunction-sections -fdata-sections --gc-sections main.zig
Dead Code Elimination (macOS)
-dead_strip # Remove unused code (macOS)
-dead_strip_dylibs # Remove unused libraries (macOS)
Symbol Binding
-Bsymbolic # Bind global references locally
Reduces symbol lookup overhead in shared libraries.
LLVM-Specific Options
Optimization Pass Limit
-fopt-bisect-limit = [N ] # Only run first N optimization passes
Useful for debugging optimization-related bugs.
zig build-exe -O ReleaseFast -fopt-bisect-limit=100 main.zig
Incremental Compilation
-fincremental # Enable incremental compilation
-fno-incremental # Disable incremental compilation
Incremental compilation can significantly speed up rebuilds during development.
Builtin Functions
-fbuiltin # Enable implicit builtin knowledge
-fno-builtin # Disable builtin knowledge
Affects C/C++ builtins like memcpy, strlen, etc.
Single-Threaded
-fsingle-threaded # Code assumes single thread
-fno-single-threaded # Multi-threaded code
Single-Threaded
Multi-Threaded
zig build-exe -fsingle-threaded embedded.zig
Benefits:
No atomic operations overhead
Smaller binaries
Simpler code generation
zig build-exe -fno-single-threaded server.zig
Required for:
Thread-safe code
Concurrent operations
Async I/O
Error Tracing
-ferror-tracing # Enable error return traces
-fno-error-tracing # Disable error traces
Error traces show the path errors take through the program. Disabled in ReleaseFast/Small by default.
Reference Tracing
-freference-trace[ =N] # Show N lines of reference trace per error
-fno-reference-trace # Disable reference traces
zig build-exe -freference-trace=10 main.zig
Fuzzing
-ffuzz # Enable fuzzing instrumentation
-fno-fuzz # Disable fuzzing
For use with fuzzing tools like AFL or libFuzzer.
DLL Export
-fdll-export-fns # Mark exported functions as DLL exports (Windows)
-fno-dll-export-fns # Don't mark as DLL exports
Optimization Strategy Examples
zig build-exe \
-O ReleaseFast \
-fllvm \
-flto=thin \
-mcpu=native \
-ffunction-sections \
--gc-sections \
-fomit-frame-pointer \
main.zig
Minimum Binary Size
zig build-exe \
-O ReleaseSmall \
-fstrip \
-ffunction-sections \
-fdata-sections \
--gc-sections \
-fno-unwind-tables \
main.zig
Balanced Production
zig build-exe \
-O ReleaseSafe \
-fllvm \
-fno-strip \
-ferror-tracing \
--compress-debug-sections=zstd \
main.zig
Debug Build
zig build-exe \
-O Debug \
-fno-strip \
-ferror-tracing \
-freference-trace=256 \
main.zig
Before optimizing compilation flags, profile your code to find actual bottlenecks.
Time Report
--time-report # Show compilation time breakdown
Sends timing diagnostics when using --listen.
Stack Report
-fstack-report # Show stack size diagnostics
Benchmarking
Benchmark Setup
Build and Run
const std = @import ( "std" );
const time = std . time ;
pub fn main () ! void {
const start = try time . Instant . now ();
// Your code here
benchmark ();
const end = try time . Instant . now ();
const elapsed = end . since ( start );
std . debug . print ( "Time: {d}ms \n " , .{ elapsed / time . ns_per_ms });
}
Best Practices
Profile First Use profilers to find real bottlenecks before changing compiler flags.
Start Simple Begin with -O ReleaseSafe, only move to ReleaseFast if needed.
Measure Impact Benchmark before and after optimization flag changes.
Test Thoroughly Always test optimized builds - optimizations can expose bugs.
Algorithm matters more - A better algorithm beats compiler optimizations
Measure, don’t guess - Profile to find real bottlenecks
Safety first - Use ReleaseSafe unless profiling proves it’s too slow
Test optimizations - Ensure optimized builds work correctly
Document choices - Explain why specific optimization flags are used
Next Steps
Debugging Debug optimized and unoptimized builds
Compilation Modes Understand Debug, ReleaseSafe, ReleaseFast, ReleaseSmall