mirror of
https://github.com/nlohmann/json.git
synced 2026-10-05 14:10:31 +00:00
133 lines
7.8 KiB
Markdown
133 lines
7.8 KiB
Markdown
# 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
|