Skip to content

Commit a13da8a

Browse files
committed
[WIP] JSON-LD performance improvements
Signed-off-by: Juan Cruz Viotti <jv@jviotti.com>
1 parent 7426c88 commit a13da8a

8 files changed

Lines changed: 446 additions & 354 deletions

File tree

benchmark/micro/2020_12.cc

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
#include <benchmark/benchmark.h>
22

33
#include <cassert> // assert
4+
#include <cstddef> // std::size_t
5+
#include <cstdint> // std::int64_t
46
#include <filesystem> // std::filesystem::path
57

68
#include <sourcemeta/blaze/foundation.h>
@@ -214,6 +216,84 @@ static void Micro_2020_12_Simple_Output_Annotations(benchmark::State &state) {
214216
}
215217
}
216218

219+
static void
220+
Micro_2020_12_Simple_Output_Annotation_Dropping(benchmark::State &state) {
221+
const sourcemeta::core::JSON schema{sourcemeta::core::parse_json(R"JSON({
222+
"$schema": "https://json-schema.org/draft/2020-12/schema",
223+
"type": "array",
224+
"items": {
225+
"oneOf": [
226+
{
227+
"type": "string",
228+
"title": "String branch",
229+
"description": "This branch matches string values"
230+
},
231+
{
232+
"type": "integer",
233+
"title": "Integer branch",
234+
"description": "This branch matches integer values"
235+
},
236+
{
237+
"type": "boolean",
238+
"title": "Boolean branch",
239+
"description": "This branch matches boolean values"
240+
},
241+
{
242+
"type": "object",
243+
"title": "Object branch",
244+
"description": "This branch matches object values"
245+
},
246+
{
247+
"type": "array",
248+
"title": "Array branch",
249+
"description": "This branch matches array values"
250+
}
251+
]
252+
}
253+
})JSON")};
254+
255+
// Every element matches exactly one branch, so the array validates, but the
256+
// four losing branches each collect a title and a description annotation that
257+
// are then dropped. Exhaustive mode forces every branch to run, so this
258+
// stresses the annotation collection and retraction path heavily
259+
auto instance{sourcemeta::core::JSON::make_array()};
260+
for (std::size_t index = 0; index < 512; index += 1) {
261+
switch (index % 5) {
262+
case 0:
263+
instance.push_back(
264+
sourcemeta::core::JSON{sourcemeta::core::JSON::String{"text"}});
265+
break;
266+
case 1:
267+
instance.push_back(
268+
sourcemeta::core::JSON{static_cast<std::int64_t>(index)});
269+
break;
270+
case 2:
271+
instance.push_back(sourcemeta::core::JSON{index % 2 == 0});
272+
break;
273+
case 3:
274+
instance.push_back(sourcemeta::core::JSON::make_object());
275+
break;
276+
default:
277+
instance.push_back(sourcemeta::core::JSON::make_array());
278+
break;
279+
}
280+
}
281+
282+
const auto schema_template{
283+
sourcemeta::blaze::compile(schema, sourcemeta::blaze::schema_walker,
284+
sourcemeta::blaze::schema_resolver,
285+
sourcemeta::blaze::default_schema_compiler,
286+
sourcemeta::blaze::Mode::Exhaustive)};
287+
sourcemeta::blaze::Evaluator evaluator;
288+
for (auto _ : state) {
289+
sourcemeta::blaze::SimpleOutput output{instance};
290+
auto result{
291+
evaluator.validate(schema_template, instance, std::ref(output))};
292+
assert(result);
293+
benchmark::DoNotOptimize(result);
294+
}
295+
}
296+
217297
static void
218298
Micro_2020_12_Compile_NonCircular_Shared_Refs(benchmark::State &state) {
219299
const sourcemeta::core::JSON schema{sourcemeta::core::parse_json(R"JSON({
@@ -397,6 +477,7 @@ BENCHMARK(Micro_2020_12_Dynamic_Ref);
397477
BENCHMARK(Micro_2020_12_Dynamic_Ref_Single);
398478
BENCHMARK(Micro_2020_12_Simple_Output_Mask);
399479
BENCHMARK(Micro_2020_12_Simple_Output_Annotations);
480+
BENCHMARK(Micro_2020_12_Simple_Output_Annotation_Dropping);
400481
BENCHMARK(Micro_2020_12_Compile_NonCircular_Shared_Refs);
401482
BENCHMARK(Micro_2020_12_Exhaustive_Deep_Numeric);
402483
BENCHMARK(Micro_2020_12_Exhaustive_Deep_Numeric_SimpleOutput);

src/output/include/sourcemeta/blaze/output_simple.h

Lines changed: 15 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616
#include <map> // std::map
1717
#include <ostream> // std::ostream
1818
#include <string> // std::string
19-
#include <tuple> // std::tie
2019
#include <utility> // std::pair
2120
#include <vector> // std::vector
2221

@@ -83,6 +82,14 @@ class SOURCEMETA_BLAZE_OUTPUT_EXPORT SimpleOutput {
8382
std::reference_wrapper<const std::string> schema_location;
8483
};
8584

85+
/// A single collected annotation, in evaluation order
86+
struct AnnotationEntry {
87+
sourcemeta::core::WeakPointer instance_location;
88+
sourcemeta::core::WeakPointer evaluate_path;
89+
std::reference_wrapper<const std::string> schema_location;
90+
sourcemeta::core::JSON value;
91+
};
92+
8693
auto operator()(const EvaluationType type, const bool result,
8794
const Instruction &step,
8895
const InstructionExtra &step_metadata,
@@ -97,12 +104,6 @@ class SOURCEMETA_BLAZE_OUTPUT_EXPORT SimpleOutput {
97104
[[nodiscard]] auto cbegin() const -> const_iterator;
98105
[[nodiscard]] auto cend() const -> const_iterator;
99106

100-
/// Access annotations that were collected during evaluation, indexed by
101-
/// instance location and evaluation path
102-
[[nodiscard]] auto annotations() const -> const auto & {
103-
return this->annotations_;
104-
}
105-
106107
/// Move out the collected error entries, leaving this output empty. Useful to
107108
/// take ownership of the trace without copying when the output is no longer
108109
/// needed
@@ -114,22 +115,12 @@ class SOURCEMETA_BLAZE_OUTPUT_EXPORT SimpleOutput {
114115
return result;
115116
}
116117

117-
// NOLINTNEXTLINE(bugprone-exception-escape)
118-
struct Location {
119-
auto operator<(const Location &other) const noexcept -> bool {
120-
// Perform a lexicographical comparison
121-
return std::tie(this->instance_location, this->evaluate_path,
122-
this->schema_location.get()) <
123-
std::tie(other.instance_location, other.evaluate_path,
124-
other.schema_location.get());
125-
}
126-
127-
// NOLINTBEGIN(cppcoreguidelines-avoid-const-or-ref-data-members)
128-
const sourcemeta::core::WeakPointer instance_location;
129-
const sourcemeta::core::WeakPointer evaluate_path;
130-
const std::reference_wrapper<const std::string> schema_location;
131-
// NOLINTEND(cppcoreguidelines-avoid-const-or-ref-data-members)
132-
};
118+
/// Access the annotations collected during evaluation, as a flat log in
119+
/// evaluation order. Consumers that need them grouped by location index this
120+
/// log themselves
121+
[[nodiscard]] auto annotations() const -> const auto & {
122+
return this->annotations_;
123+
}
133124

134125
private:
135126
// Exporting symbols that depends on the standard C++ library is considered
@@ -148,7 +139,7 @@ class SOURCEMETA_BLAZE_OUTPUT_EXPORT SimpleOutput {
148139
std::pair<sourcemeta::core::WeakPointer, sourcemeta::core::WeakPointer>,
149140
std::vector<Entry>>
150141
masked_traces;
151-
std::map<Location, std::vector<sourcemeta::core::JSON>> annotations_;
142+
std::vector<AnnotationEntry> annotations_;
152143
#if defined(_MSC_VER)
153144
#pragma warning(default : 4251)
154145
#endif

0 commit comments

Comments
 (0)