diff --git a/tests/benchmarks/README.md b/tests/benchmarks/README.md index 283b156e5..cb58c83f5 100644 --- a/tests/benchmarks/README.md +++ b/tests/benchmarks/README.md @@ -17,7 +17,9 @@ Micro-benchmarks for parsing, serialization and the binary formats, written with | `FromBinaryShape` | read deeply nested, container-heavy and scalar-heavy documents in every binary format | | `FromCborChunkedString` | read CBOR strings split into indefinite-length chunks | | `SaxParse` | parse with a SAX handler that only counts events, so no values are built | -| `Copy`, `Destroy`, `Equal`, `Iterate` | copy, destroy, or compare a parsed document, or visit every value with range-based `for` loops | +| `Copy`, `CopyAssign`, `CopyRing` | copy a parsed document and destroy a copy, both timed: a temporary copy, an assignment over an earlier copy, and replacing the oldest of eight live copies | +| `CopyOnly`, `DestroyOnly` | only the copy or only the destruction, the other half untimed; a fast destructor can leave work to the next allocations, so these tell the halves apart but do not compare versions | +| `Equal`, `Iterate` | compare a parsed document with a copy, or visit every value with range-based `for` loops | | `Flatten`, `FlattenOrdered`, `Unflatten` | `flatten()` for `json` and `ordered_json`, and `unflatten()` | | `Diff`, `Patch` | `diff()` against the document with every eighth scalar changed, and applying that patch | | `JsonPointerParse`, `JsonPointerToString`, `JsonPointerAt`, `JsonPointerContains` | the JSON pointers of every scalar in a document: parse them from strings, turn them back into strings, look them up with `at()` and `contains()` | diff --git a/tests/benchmarks/src/benchmarks.cpp b/tests/benchmarks/src/benchmarks.cpp index a0f65d882..3240b52ed 100644 --- a/tests/benchmarks/src/benchmarks.cpp +++ b/tests/benchmarks/src/benchmarks.cpp @@ -737,12 +737,71 @@ BENCHMARK_CAPTURE(SaxParse, twitter, TEST_DATA_DIRECTORY "/nativejson-bench // The parsed test files stand in for a document an application keeps around. // Each benchmark times one whole-document operation; setup and cleanup run // outside the timed section. +// +// A copy is always destroyed again, and how a document is freed shapes the +// heap the next copy allocates from: a destructor that frees quickly can leave +// work to the allocator's next calls. Copy, CopyAssign and CopyRing therefore +// time the copy together with the destruction of a copy; CopyOnly and +// DestroyOnly time one half each and are meant for telling the halves apart, +// not for comparing versions. ////////////////////////////////////////////////////////////////////////////// +/// copy a document and destroy the copy again, as a temporary copy is used static void Copy(benchmark::State& state, const char* filename) { const json j = load_json(filename); + for (auto _ : state) + { + json copy = j; + benchmark::DoNotOptimize(copy); + } +} +BENCHMARK_CAPTURE(Copy, canada, TEST_DATA_DIRECTORY "/nativejson-benchmark/canada.json"); +BENCHMARK_CAPTURE(Copy, citm_catalog, TEST_DATA_DIRECTORY "/nativejson-benchmark/citm_catalog.json"); +BENCHMARK_CAPTURE(Copy, twitter, TEST_DATA_DIRECTORY "/nativejson-benchmark/twitter.json"); + +/// copy-assign a document over an earlier copy of it, which is freed in the process +static void CopyAssign(benchmark::State& state, const char* filename) +{ + const json j = load_json(filename); + json target = j; + + for (auto _ : state) + { + target = j; + benchmark::DoNotOptimize(target); + } +} +BENCHMARK_CAPTURE(CopyAssign, canada, TEST_DATA_DIRECTORY "/nativejson-benchmark/canada.json"); +BENCHMARK_CAPTURE(CopyAssign, citm_catalog, TEST_DATA_DIRECTORY "/nativejson-benchmark/citm_catalog.json"); +BENCHMARK_CAPTURE(CopyAssign, twitter, TEST_DATA_DIRECTORY "/nativejson-benchmark/twitter.json"); + +/// keep eight copies alive and replace the oldest one, as a cache or an undo +/// history does, so that the copy is not made from memory just freed by an +/// identical document +static void CopyRing(benchmark::State& state, const char* filename) +{ + const json j = load_json(filename); + std::vector ring(8, j); + std::size_t oldest = 0; + + for (auto _ : state) + { + ring[oldest] = j; + benchmark::DoNotOptimize(ring[oldest]); + oldest = (oldest + 1) % ring.size(); + } +} +BENCHMARK_CAPTURE(CopyRing, canada, TEST_DATA_DIRECTORY "/nativejson-benchmark/canada.json"); +BENCHMARK_CAPTURE(CopyRing, citm_catalog, TEST_DATA_DIRECTORY "/nativejson-benchmark/citm_catalog.json"); +BENCHMARK_CAPTURE(CopyRing, twitter, TEST_DATA_DIRECTORY "/nativejson-benchmark/twitter.json"); + +/// only the copy; the copy is destroyed with the timer paused (diagnostic) +static void CopyOnly(benchmark::State& state, const char* filename) +{ + const json j = load_json(filename); + for (auto _ : state) { auto* copy = new json(j); @@ -752,11 +811,12 @@ static void Copy(benchmark::State& state, const char* filename) state.ResumeTiming(); } } -BENCHMARK_CAPTURE(Copy, canada, TEST_DATA_DIRECTORY "/nativejson-benchmark/canada.json"); -BENCHMARK_CAPTURE(Copy, citm_catalog, TEST_DATA_DIRECTORY "/nativejson-benchmark/citm_catalog.json"); -BENCHMARK_CAPTURE(Copy, twitter, TEST_DATA_DIRECTORY "/nativejson-benchmark/twitter.json"); +BENCHMARK_CAPTURE(CopyOnly, canada, TEST_DATA_DIRECTORY "/nativejson-benchmark/canada.json"); +BENCHMARK_CAPTURE(CopyOnly, citm_catalog, TEST_DATA_DIRECTORY "/nativejson-benchmark/citm_catalog.json"); +BENCHMARK_CAPTURE(CopyOnly, twitter, TEST_DATA_DIRECTORY "/nativejson-benchmark/twitter.json"); -static void Destroy(benchmark::State& state, const char* filename) +/// only the destruction; the copy is made with the timer paused (diagnostic) +static void DestroyOnly(benchmark::State& state, const char* filename) { const json j = load_json(filename); @@ -769,9 +829,9 @@ static void Destroy(benchmark::State& state, const char* filename) delete copy; } } -BENCHMARK_CAPTURE(Destroy, canada, TEST_DATA_DIRECTORY "/nativejson-benchmark/canada.json"); -BENCHMARK_CAPTURE(Destroy, citm_catalog, TEST_DATA_DIRECTORY "/nativejson-benchmark/citm_catalog.json"); -BENCHMARK_CAPTURE(Destroy, twitter, TEST_DATA_DIRECTORY "/nativejson-benchmark/twitter.json"); +BENCHMARK_CAPTURE(DestroyOnly, canada, TEST_DATA_DIRECTORY "/nativejson-benchmark/canada.json"); +BENCHMARK_CAPTURE(DestroyOnly, citm_catalog, TEST_DATA_DIRECTORY "/nativejson-benchmark/citm_catalog.json"); +BENCHMARK_CAPTURE(DestroyOnly, twitter, TEST_DATA_DIRECTORY "/nativejson-benchmark/twitter.json"); static void Equal(benchmark::State& state, const char* filename) {