Skip to main content

Compilation Modes

Zig provides four compilation modes that offer different trade-offs between runtime performance, safety checks, and binary size.

Overview

The compilation mode is specified with the -O flag:
-O [mode]
Where [mode] is one of:
  • Debug (default)
  • ReleaseSafe
  • ReleaseFast
  • ReleaseSmall

Mode Comparison

FeatureDebugReleaseSafeReleaseFastReleaseSmall
OptimizationsOffOnOnOn
Safety ChecksOnOnOffOff
Binary SizeLargestLargeMediumSmallest
Compile SpeedFastestMediumMediumSlowest
Runtime SpeedSlowestMediumFastestMedium

Debug Mode

Default mode when no -O flag is specified.
zig build-exe main.zig
zig build-exe -O Debug main.zig  # Explicit

Characteristics

  • Optimizations: Disabled
  • Safety checks: Enabled
  • Assertions: Enabled
  • Debug info: Full debug symbols included
  • Stack traces: Enabled with full information

Use Cases

Development

Day-to-day development work

Testing

Running test suites

Debugging

Troubleshooting with debuggers

Learning

Exploring Zig features

Safety Checks Enabled

const std = @import("std");

pub fn main() void {
    var x: u8 = 255;
    x += 1;  // Panic in Debug/ReleaseSafe
}

Example Output

$ zig build-exe -O Debug main.zig
$ ls -lh main
-rwxr-xr-x 1 user user 856K main

ReleaseSafe Mode

zig build-exe -O ReleaseSafe main.zig

Characteristics

  • Optimizations: Enabled for performance
  • Safety checks: Enabled (all runtime checks)
  • Assertions: Enabled
  • Debug info: Can be included with -fno-strip
  • Stack traces: Available on errors

Use Cases

Production

Production deployments requiring safety

Critical Systems

Safety-critical applications

Financial

Financial/medical software

Long-Running

Services that must not crash

Trade-offs

Performance Impact: Safety checks add overhead (typically 5-15% slower than ReleaseFast).Binary Size: Larger than ReleaseFast due to safety checking code.Best For: When correctness is more important than peak performance.

Example

zig build-exe -O ReleaseSafe server.zig

ReleaseFast Mode

zig build-exe -O ReleaseFast main.zig

Characteristics

  • Optimizations: Maximum performance optimizations
  • Safety checks: Disabled
  • Assertions: Disabled (compile-time unreachable only)
  • Debug info: Stripped by default
  • Stack traces: Not available

Use Cases

Games

Game engines and real-time graphics

HPC

High-performance computing

Embedded

Performance-critical embedded systems

Benchmarks

Competitive benchmarking

Behavior Differences

Undefined Behavior: Operations that would panic in Debug/ReleaseSafe modes may cause undefined behavior.No Safety Net: Integer overflow, out-of-bounds access, and null dereferences are unchecked.Use With Caution: Thoroughly test with Debug or ReleaseSafe first.
var x: u8 = 255;
x += 1;  // Wraps to 0 (undefined behavior)

Performance Example

$ zig build-exe -O ReleaseFast benchmark.zig
$ ./benchmark
Time: 1.23s  # Fastest execution time

ReleaseSmall Mode

zig build-exe -O ReleaseSmall main.zig

Characteristics

  • Optimizations: Optimized for binary size
  • Safety checks: Disabled
  • Assertions: Disabled
  • Debug info: Stripped by default
  • Code generation: Prefers smaller code over speed

Use Cases

Embedded

Memory-constrained embedded systems

WASM

WebAssembly applications

IoT

IoT devices with limited storage

Bootloaders

Firmware and bootloaders

Optimization Strategy

ReleaseSmall uses different optimization strategies than ReleaseFast:
  • Prefers function calls over inlining
  • Uses smaller instruction sequences
  • Optimizes for code density
  • May sacrifice some runtime performance

Size Comparison

zig build-exe -O Debug program.zig -o program-debug
zig build-exe -O ReleaseSafe program.zig -o program-safe
zig build-exe -O ReleaseFast program.zig -o program-fast
zig build-exe -O ReleaseSmall program.zig -o program-small

Choosing a Mode

Decision Tree

Recommendations

Use Debug
  • Fast compile times
  • Full error checking
  • Easy debugging
  • Clear stack traces

Mode-Specific Features

Stack Traces

Full stack traces with:
  • File names
  • Line numbers
  • Function names
  • Inlined function information

Log Levels

Default log levels by mode (from std.options.log_level):
pub const std_options: std.Options = .{
    .log_level = switch (builtin.mode) {
        .Debug => .debug,              // Show all logs
        .ReleaseSafe, .ReleaseFast => .info,  // Info and above
        .ReleaseSmall => .err,         // Errors only
    },
};

Runtime Behavior

Safety Check Examples

const std = @import("std");

pub fn main() !void {
    var x: u8 = 200;
    var y: u8 = 100;
    
    // Debug/ReleaseSafe: Panic
    // ReleaseFast/Small: Wraps (undefined)
    const sum = x + y;
    
    std.debug.print("{d}\n", .{sum});
}

Performance Impact

Benchmark Example

Typical performance differences (varies by workload):
  • Debug: Baseline (slowest)
  • ReleaseSafe: 5-8x faster than Debug
  • ReleaseFast: 6-10x faster than Debug (10-20% faster than ReleaseSafe)
  • ReleaseSmall: 4-7x faster than Debug (similar to or slower than ReleaseSafe)

Best Practices

  1. Always develop in Debug mode - Catch bugs early with safety checks
  2. Test in ReleaseSafe - Verify optimizations don’t break your code
  3. Use ReleaseSafe for production by default - Safety is usually worth the cost
  4. Only use ReleaseFast when profiled - Measure before sacrificing safety
  5. Profile to find bottlenecks - Optimize algorithms before compilation mode
  6. Keep Debug builds for debugging - Even in production environments

Next Steps

Optimization

Deep dive into optimization levels and strategies

Debugging

Learn debugging techniques for each mode

Build docs developers (and LLMs) love