Skip to content

# Optimization Levels

Glaze provides an `optimization_level` option that controls the trade-off between binary size and runtime performance. This is useful for embedded systems and other size-constrained environments.

## Quick Start

    // Default (normal) - maximum performance
    auto json = glz::write_json(obj);

    // Size-optimized for embedded systems
    auto json = glz::write<glz::opts_size{}>(obj);

## Available Levels

Level | Preset | Integer Serialization | Float Serialization | Key Lookup
--- | --- | --- | --- | ---
`normal` | (default) | `to_chars_40kb` (40KB tables) | zmij (~17KB pow-10 tables) | Hash tables
`size` | `opts_size` | `to_chars` (400B tables) | zmij `OptSize=true` (recomputed, ~1.4KB) | Linear search

## Optimization Level Details

### Normal (Default)

The default optimization level, optimized for maximum runtime performance.

    // These are equivalent:
    glz::write_json(obj, buffer);
    glz::write<glz::opts{}>(obj, buffer);

Characteristics:

- Uses 40KB digit lookup tables for fast integer-to-string conversion
- Uses zmij with ~17KB pow-10 lookup tables for float-to-string
- Uses compile-time generated hash tables for O(1) key lookup
- Best throughput for high-volume JSON processing

### Size

Minimizes binary size for embedded systems.

    glz::write<glz::opts_size{}>(obj, buffer);
    glz::read<glz::opts_size{}>(obj, buffer);

Characteristics:

- Uses `glz::to_chars` for integers (400B lookup tables, 1.6x faster than `std::to_chars`)
- Uses `glz::to_chars<T, OptSize=true>` for floats (zmij with the pow-10 tables dropped; recomputed on the fly)
- Defaults to linear search for key matching (no hash tables)
- Significantly smaller binary size

Approximate Binary Size Savings:

- ~39KB from using smaller integer lookup tables (400B vs 40KB)
- ~15.8KB from dropping zmij's pow-10 tables (`OptSize=true`)
- Variable savings from hash table elimination

## Build-wide Default (Embedded)

Passing `glz::opts_size{}` at every call site works, but a single forgotten call left on the default options (for example `glz::write_json(obj, buffer)` or a transitive `glz::format_error`) silently relinks the large tables. For flash-constrained targets you usually want `size` to be the default everywhere instead of opt-in per call.

Enable the CMake option:

    set(glaze_DEFAULT_OPTIMIZATION_SIZE ON)
    # or: cmake -Dglaze_DEFAULT_OPTIMIZATION_SIZE=ON ...

This injects `GLZ_DEFAULT_OPTIMIZATION_SIZE` as an `INTERFACE` compile definition, which flips the default optimization level for any options type that does not specify one (such as a plain `glz::opts{}`). Integers, floats, and per-struct hash-table elimination all switch together, reusing the same machinery as `opts_size`.

Without CMake, define the macro directly before including Glaze (it must be defined uniformly across the whole build):

    #define GLZ_DEFAULT_OPTIMIZATION_SIZE
    #include "glaze/glaze.hpp"

Key properties:

- Per-call options still win. An explicit `optimization_level::normal` keeps the fast tables even in a size-default build, so you can opt specific hot paths back into speed. Because the only way back to the large tables is an explicit, greppable `normal`, accidental inclusion is eliminated.
- The guarantee is verifiable. With the size default and no explicit `normal`, nothing ODR-uses `glz::to_chars_40kb`, so the 40 KB `digit_quads` table is never emitted, even without `-Wl,--gc-sections`. You can confirm with `nm -C <binary> | grep digit_quads` (expect no output).
- Advanced override. Define `GLZ_DEFAULT_OPTIMIZATION_LEVEL` directly to any `glz::optimization_level` value if you need finer control than the boolean.

## Custom Options Struct

You can create a custom options struct with the optimization level:

    struct my_opts : glz::opts
    {
       glz::optimization_level optimization_level = glz::optimization_level::size;
       // Add other options as needed
       bool prettify = true;
    };

    glz::write<my_opts{}>(obj, buffer);

## Combining with Other Options

The optimization level works with all other Glaze options:

    struct embedded_opts : glz::opts
    {
       glz::optimization_level optimization_level = glz::optimization_level::size;
       bool minified = true;  // Also skip whitespace parsing for smaller code
       bool error_on_unknown_keys = false;  // More lenient parsing
    };

## Platform Considerations

Both optimization levels use `glz::to_chars` (zmij) for floats. The only difference is whether the pow-10 tables are linked in (`normal`) or recomputed on the fly (`size`). Both variants work on all platforms, including bare-metal and older Apple targets.

### When to Use Each Level

Use Case | Recommended Level
--- | ---
Server-side JSON processing | `normal`
Desktop applications | `normal`
Embedded systems | `size`

## See Also

- Compile Time Options - All available options
- Optimizing Performance - General performance tips
- linear_search option - Binary size reduction via linear key search