Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/linux.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ jobs:
./build/benchmarks/all/reflect-cpp-all-format-benchmarks --benchmark_filter=licenses_write >> $GITHUB_STEP_SUMMARY
./build/benchmarks/all/reflect-cpp-all-format-benchmarks --benchmark_filter=person_read >> $GITHUB_STEP_SUMMARY
./build/benchmarks/all/reflect-cpp-all-format-benchmarks --benchmark_filter=person_write >> $GITHUB_STEP_SUMMARY
./build/benchmarks/all/reflect-cpp-all-format-benchmarks --benchmark_filter=points_read >> $GITHUB_STEP_SUMMARY
echo '```' >> $GITHUB_STEP_SUMMARY
echo '## Benchmarks for JSON' >> $GITHUB_STEP_SUMMARY
echo '```' >> $GITHUB_STEP_SUMMARY
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/macos.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ jobs:
./build/benchmarks/all/reflect-cpp-all-format-benchmarks --benchmark_filter=licenses_write >> $GITHUB_STEP_SUMMARY
./build/benchmarks/all/reflect-cpp-all-format-benchmarks --benchmark_filter=person_read >> $GITHUB_STEP_SUMMARY
./build/benchmarks/all/reflect-cpp-all-format-benchmarks --benchmark_filter=person_write >> $GITHUB_STEP_SUMMARY
./build/benchmarks/all/reflect-cpp-all-format-benchmarks --benchmark_filter=points_read >> $GITHUB_STEP_SUMMARY
echo '```' >> $GITHUB_STEP_SUMMARY
echo '## Benchmarks for JSON' >> $GITHUB_STEP_SUMMARY
echo '```' >> $GITHUB_STEP_SUMMARY
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/windows.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ jobs:
./build/benchmarks/all/reflect-cpp-all-format-benchmarks --benchmark_filter=licenses_write >> $GITHUB_STEP_SUMMARY
./build/benchmarks/all/reflect-cpp-all-format-benchmarks --benchmark_filter=person_read >> $GITHUB_STEP_SUMMARY
./build/benchmarks/all/reflect-cpp-all-format-benchmarks --benchmark_filter=person_write >> $GITHUB_STEP_SUMMARY
./build/benchmarks/all/reflect-cpp-all-format-benchmarks --benchmark_filter=points_read >> $GITHUB_STEP_SUMMARY
echo '```' >> $GITHUB_STEP_SUMMARY
echo '## Benchmarks for JSON' >> $GITHUB_STEP_SUMMARY
echo '```' >> $GITHUB_STEP_SUMMARY
Expand Down
144 changes: 144 additions & 0 deletions benchmarks/all/points_read.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
#include <benchmark/benchmark.h>

#include <cstddef>
#include <cstdint>
#include <rfl/cereal.hpp>
#include <rfl/json.hpp>
#include <rfl/msgpack.hpp>
#include <rfl/yas.hpp>
#include <vector>

namespace points_read {

struct Point2d {
float x;
float y;

bool operator==(const Point2d&) const = default;
};

using Points = std::vector<Point2d>;

static Points make_points(std::size_t count) {
Points points;
points.reserve(count);
for (std::size_t i = 0; i < count; ++i) {
// Exactly representable coordinates of both signs, varying across points.
const auto x = static_cast<std::int32_t>(i % 65536) - 32768;
const auto y = static_cast<std::int32_t>((i * 17) % 65536) - 32768;
points.push_back(
{static_cast<float>(x) * 0.125f, static_cast<float>(y) * 0.0625f});
}
return points;
}

template <class Write, class Read>
static void run(benchmark::State& state, Write write, Read read) {
// Many small structs expose per-object parser overhead, including allocating
// error storage on successful reads. Setup and validation are not timed.
const auto points = make_points(static_cast<std::size_t>(state.range(0)));
const auto data = write(points);
{
const auto result = read(data);
if (!result) {
state.SkipWithError(result.error().what());
return;
}
if (*result != points) {
state.SkipWithError("Point coordinates changed during round trip");
return;
}
}

for (auto _ : state) {
auto result = read(data);
if (!result) {
state.SkipWithError(result.error().what());
break;
}
benchmark::DoNotOptimize(result);
}

state.SetItemsProcessed(state.iterations() * state.range(0));
state.SetBytesProcessed(state.iterations() *
static_cast<std::int64_t>(data.size()));
state.counters["serialized_bytes"] = static_cast<double>(data.size());
}

// DefaultIfMissing exercises the parser's default-value path. NoFieldNames
// separates field-name lookup overhead from positional struct parsing.
template <class... Ps>
static void BM_points_read_reflect_cpp_cereal(benchmark::State& state) {
run(
state,
[](const Points& points) { return rfl::cereal::write<Ps...>(points); },
[](const auto& data) { return rfl::cereal::read<Points, Ps...>(data); });
}
BENCHMARK_TEMPLATE(BM_points_read_reflect_cpp_cereal, rfl::Processors<>)
->Arg(1000)
->Arg(1000000)
->Unit(benchmark::kMillisecond);

template <class... Ps>
static void BM_points_read_reflect_cpp_json(benchmark::State& state) {
run(
state,
[](const Points& points) { return rfl::json::write<Ps...>(points); },
[](const auto& data) { return rfl::json::read<Points, Ps...>(data); });
}
BENCHMARK_TEMPLATE(BM_points_read_reflect_cpp_json, rfl::Processors<>)
->Arg(1000)
->Arg(1000000)
->Unit(benchmark::kMillisecond);
BENCHMARK_TEMPLATE(BM_points_read_reflect_cpp_json, rfl::DefaultIfMissing)
->Arg(1000)
->Arg(1000000)
->Unit(benchmark::kMillisecond);
BENCHMARK_TEMPLATE(BM_points_read_reflect_cpp_json, rfl::NoFieldNames)
->Arg(1000)
->Arg(1000000)
->Unit(benchmark::kMillisecond);
BENCHMARK_TEMPLATE(BM_points_read_reflect_cpp_json, rfl::NoFieldNames,
rfl::DefaultIfMissing)
->Arg(1000)
->Arg(1000000)
->Unit(benchmark::kMillisecond);

template <class... Ps>
static void BM_points_read_reflect_cpp_msgpack(benchmark::State& state) {
run(
state,
[](const Points& points) { return rfl::msgpack::write<Ps...>(points); },
[](const auto& data) { return rfl::msgpack::read<Points, Ps...>(data); });
}
BENCHMARK_TEMPLATE(BM_points_read_reflect_cpp_msgpack, rfl::Processors<>)
->Arg(1000)
->Arg(1000000)
->Unit(benchmark::kMillisecond);
BENCHMARK_TEMPLATE(BM_points_read_reflect_cpp_msgpack, rfl::DefaultIfMissing)
->Arg(1000)
->Arg(1000000)
->Unit(benchmark::kMillisecond);
BENCHMARK_TEMPLATE(BM_points_read_reflect_cpp_msgpack, rfl::NoFieldNames)
->Arg(1000)
->Arg(1000000)
->Unit(benchmark::kMillisecond);
BENCHMARK_TEMPLATE(BM_points_read_reflect_cpp_msgpack, rfl::NoFieldNames,
rfl::DefaultIfMissing)
->Arg(1000)
->Arg(1000000)
->Unit(benchmark::kMillisecond);

template <class... Ps>
static void BM_points_read_reflect_cpp_yas(benchmark::State& state) {
run(
state,
[](const Points& points) { return rfl::yas::write<Ps...>(points); },
[](const auto& data) { return rfl::yas::read<Points, Ps...>(data); });
}
BENCHMARK_TEMPLATE(BM_points_read_reflect_cpp_yas, rfl::Processors<>)
->Arg(1000)
->Arg(1000000)
->Unit(benchmark::kMillisecond);

} // namespace points_read
2 changes: 0 additions & 2 deletions include/rfl/parsing/NamedTupleParser.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -382,7 +382,6 @@ struct NamedTupleParser {
auto set = std::array<bool, NamedTupleType::size()>();
set.fill(false);
std::vector<std::string> errors;
errors.reserve(size_);
const auto reader = ViewReaderType(&_r, _view, &found, &set, &errors);
if constexpr (_no_field_names) {
const auto err = _r.read_array(reader, _obj_or_arr);
Expand All @@ -407,7 +406,6 @@ struct NamedTupleParser {
const R& _r, const InputObjectOrArrayType& _obj_or_arr,
NamedTupleType* _view) noexcept {
std::vector<std::string> errors;
errors.reserve(size_);
const auto reader = ViewReaderWithDefaultType(&_r, _view, &errors);
if constexpr (_no_field_names) {
const auto err = _r.read_array(reader, _obj_or_arr);
Expand Down
3 changes: 3 additions & 0 deletions include/rfl/parsing/ViewReader.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,9 @@ class ViewReader {
std::stringstream stream;
stream << "Failed to parse field '" << name
<< "': " << res.error().what();
// Reserve lazily to avoid error-vector reallocations while keeping
// successful reads allocation-free.
_errors->reserve(size_);
_errors->emplace_back(stream.str());
return;
}
Expand Down
Loading