Files
json/integration/compile_times/index.md
T
2026-10-04 15:46:44 +00:00

133 lines
7.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Compile times
The library is header-only and makes heavy use of templates, so every translation unit that includes `<nlohmann/json.hpp>` pays for parsing the header and instantiating what it uses. This page lists the options to reduce that cost, ordered by how much they typically save.
Measurements
The numbers below are medians of nine runs compiling a single translation unit with `-std=c++17 -c` against the single-header version, with Apple clang and GCC 16 on macOS (Apple silicon). They show the order of magnitude to expect; measure your own code before and after a change.
## Include `json_fwd.hpp` in headers
Header files that only need to *name* the `json` type — for function declarations, members held by pointer or reference, or friend declarations — can include `<nlohmann/json_fwd.hpp>` instead of `<nlohmann/json.hpp>`. It only forward-declares `basic_json`, `json`, `ordered_json`, `json_pointer`, and `adl_serializer`. The translation units that actually use the values then include `<nlohmann/json.hpp>`.
person.hpp
```
#pragma once
#include <nlohmann/json_fwd.hpp>
struct person;
void to_json(nlohmann::json& j, const person& p);
void from_json(const nlohmann::json& j, person& p);
```
person.cpp
```
#include "person.hpp"
#include <nlohmann/json.hpp>
void to_json(nlohmann::json& j, const person& p) { /* ... */ }
void from_json(const nlohmann::json& j, person& p) { /* ... */ }
```
| Compiler | `json.hpp` (`-O0`) | `json_fwd.hpp` (`-O0`) | Change |
| ----------- | ------------------ | ---------------------- | ------ |
| Apple clang | 704 ms | 329 ms | −53% |
| GCC 16 | 779 ms | 242 ms | −69% |
This is the most effective option, because it avoids the full header in every translation unit that includes *your* headers.
## Opt out of the automatic user-defined string literals
The user-defined string literals [`operator""_json`](https://json.nlohmann.me/api/operator_literal_json/index.md) and [`operator""_json_pointer`](https://json.nlohmann.me/api/operator_literal_json_pointer/index.md) are ordinary inline functions whose bodies call the parser. As `<nlohmann/json.hpp>` includes them by default, every translation unit instantiates the parser, even if it never parses anything itself.
Define [`JSON_NO_AUTOMATIC_UDLS`](https://json.nlohmann.me/api/macros/json_no_automatic_udls/index.md) for the whole project and include `<nlohmann/json_literals.hpp>` instead of `<nlohmann/json.hpp>` in the files that use the literals (it includes `<nlohmann/json.hpp>` itself):
```
target_compile_definitions(my_target PRIVATE JSON_NO_AUTOMATIC_UDLS)
```
```
#include <nlohmann/json_literals.hpp> // only where "..."_json is used; includes <nlohmann/json.hpp>
```
The saving applies to translation units that do not parse JSON, for example ones that define types and their conversions or only pass `json` values around:
| Compiler | Translation unit | Default (`-O0` / `-O2`) | `JSON_NO_AUTOMATIC_UDLS` (`-O0` / `-O2`) | Change |
| ----------- | ---------------- | ----------------------- | ---------------------------------------- | ----------- |
| Apple clang | model | 776 ms / 846 ms | 629 ms / 692 ms | −19% / −18% |
| GCC 16 | model | 1022 ms / 1120 ms | 882 ms / 965 ms | −14% / −14% |
| Apple clang | parsing | 992 ms / 1815 ms | 1006 ms / 1823 ms | +1% / 0% |
| GCC 16 | parsing | 2018 ms / 3420 ms | 1990 ms / 3454 ms | −1% / +1% |
Translation units that include only the header save up to a third. Translation units that parse anyway instantiate the parser regardless and see no difference.
## Instantiate `basic_json` once
Each translation unit instantiates the member functions of `nlohmann::json` it uses. An explicit instantiation declaration tells the compiler that the non-template members are instantiated elsewhere, so it can skip them:
json_instance.hpp
```
#pragma once
#include <nlohmann/json.hpp>
extern template class nlohmann::basic_json<>;
```
json_instance.cpp
```
#include "json_instance.hpp"
template class nlohmann::basic_json<>;
```
Include `json_instance.hpp` instead of `<nlohmann/json.hpp>` and compile and link `json_instance.cpp` once.
| Compiler | Translation unit | Default (`-O0` / `-O2`) | `extern template` (`-O0` / `-O2`) | Change |
| ----------- | ------------------- | ----------------------- | --------------------------------- | ----------- |
| Apple clang | parsing | 992 ms / 1815 ms | 953 ms / 1625 ms | −4% / −10% |
| GCC 16 | parsing | 2018 ms / 3420 ms | 1522 ms / 2728 ms | −25% / −20% |
| Apple clang | `json_instance.cpp` | — | 2166 ms / 4660 ms | — |
| GCC 16 | `json_instance.cpp` | — | 5085 ms / 10616 ms | — |
Notes:
- The saving grows with the number of translation units that use `json`, while the instantiation translation unit is compiled only once (and is rarely recompiled, as it does not depend on your code).
- Member function templates (such as `get<T>()`, `parse(InputType&&)`, or `value(key, default)`) are not covered by the explicit instantiation and are still instantiated where they are used.
- The declaration covers exactly `nlohmann::json`. Add the same lines for `nlohmann::ordered_json` (`nlohmann::basic_json<nlohmann::ordered_map>`) or your own `basic_json` specializations if you use them.
## Use C++20 modules
With a toolchain that supports named modules, `import nlohmann.json;` compiles the library once into a module and avoids parsing the header in every translation unit. See [Modules](https://json.nlohmann.me/features/modules/index.md) for requirements and known issues. Module support is experimental and currently depends heavily on the compiler version.
## Use precompiled headers
Build systems can precompile `<nlohmann/json.hpp>` together with other stable headers, for example with CMake's [`target_precompile_headers`](https://cmake.org/cmake/help/latest/command/target_precompile_headers.html):
```
target_precompile_headers(my_target PRIVATE <nlohmann/json.hpp>)
```
This removes the cost of parsing the header, but not of instantiating templates in each translation unit, so it combines well with the options above.
## Options without effect on compile times
Some configuration macros change what the library declares, but do not measurably change compile times:
| Macro | Apple clang, model (`-O0` / `-O2`) | GCC 16, model (`-O0` / `-O2`) |
| ----------------------------------------------------------------------------------------------- | ---------------------------------- | ----------------------------- |
| default | 776 ms / 846 ms | 1022 ms / 1120 ms |
| [`JSON_NO_IO`](https://json.nlohmann.me/api/macros/json_no_io/index.md) | 764 ms / 836 ms | 1022 ms / 1117 ms |
| [`JSON_USE_GLOBAL_UDLS`](https://json.nlohmann.me/api/macros/json_use_global_udls/index.md)`=0` | 763 ms / 852 ms | 1019 ms / 1106 ms |
`JSON_USE_GLOBAL_UDLS` only controls *where* the literals are declared; to avoid their cost, use `JSON_NO_AUTOMATIC_UDLS` instead.
## See also
- [`JSON_NO_AUTOMATIC_UDLS`](https://json.nlohmann.me/api/macros/json_no_automatic_udls/index.md) - do not include the user-defined string literals automatically
- [Modules](https://json.nlohmann.me/features/modules/index.md) - C++20 module support
- [Header only](https://json.nlohmann.me/integration/index.md) - including the library