Skip to content

Commit 10fb6bb

Browse files
AltrenGeorgii Evmenov
andauthored
Inline frequently used parsing functions (#742)
* Inline frequently used parsing functions * Fix GCC 12 warning in inlined struct parsing --------- Co-authored-by: Georgii Evmenov <alten@gmail.com>
1 parent 8866cac commit 10fb6bb

14 files changed

Lines changed: 179 additions & 23 deletions

‎.github/workflows/linux.yaml‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,7 @@ jobs:
120120
echo '## Benchmarks across different formats' >> $GITHUB_STEP_SUMMARY
121121
echo '```' >> $GITHUB_STEP_SUMMARY
122122
./build/benchmarks/all/reflect-cpp-all-format-benchmarks --benchmark_filter=canada_read >> $GITHUB_STEP_SUMMARY
123+
./build/benchmarks/all/reflect-cpp-all-format-benchmarks --benchmark_filter=parser_inlining >> $GITHUB_STEP_SUMMARY
123124
./build/benchmarks/all/reflect-cpp-all-format-benchmarks --benchmark_filter=canada_write >> $GITHUB_STEP_SUMMARY
124125
./build/benchmarks/all/reflect-cpp-all-format-benchmarks --benchmark_filter=field_dispatch >> $GITHUB_STEP_SUMMARY
125126
./build/benchmarks/all/reflect-cpp-all-format-benchmarks --benchmark_filter=licenses_read >> $GITHUB_STEP_SUMMARY

‎.github/workflows/macos.yaml‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ jobs:
6262
echo '## Benchmarks across different formats' >> $GITHUB_STEP_SUMMARY
6363
echo '```' >> $GITHUB_STEP_SUMMARY
6464
./build/benchmarks/all/reflect-cpp-all-format-benchmarks --benchmark_filter=canada_read >> $GITHUB_STEP_SUMMARY
65+
./build/benchmarks/all/reflect-cpp-all-format-benchmarks --benchmark_filter=parser_inlining >> $GITHUB_STEP_SUMMARY
6566
./build/benchmarks/all/reflect-cpp-all-format-benchmarks --benchmark_filter=canada_write >> $GITHUB_STEP_SUMMARY
6667
./build/benchmarks/all/reflect-cpp-all-format-benchmarks --benchmark_filter=field_dispatch >> $GITHUB_STEP_SUMMARY
6768
./build/benchmarks/all/reflect-cpp-all-format-benchmarks --benchmark_filter=licenses_read >> $GITHUB_STEP_SUMMARY

‎.github/workflows/windows.yaml‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ jobs:
6161
echo '## Benchmarks across different formats' >> $GITHUB_STEP_SUMMARY
6262
echo '```' >> $GITHUB_STEP_SUMMARY
6363
./build/benchmarks/all/reflect-cpp-all-format-benchmarks --benchmark_filter=canada_read >> $GITHUB_STEP_SUMMARY
64+
./build/benchmarks/all/reflect-cpp-all-format-benchmarks --benchmark_filter=parser_inlining >> $GITHUB_STEP_SUMMARY
6465
./build/benchmarks/all/reflect-cpp-all-format-benchmarks --benchmark_filter=canada_write >> $GITHUB_STEP_SUMMARY
6566
./build/benchmarks/all/reflect-cpp-all-format-benchmarks --benchmark_filter=field_dispatch >> $GITHUB_STEP_SUMMARY
6667
./build/benchmarks/all/reflect-cpp-all-format-benchmarks --benchmark_filter=licenses_read >> $GITHUB_STEP_SUMMARY

‎benchmarks/all/parser_inlining.hpp‎

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
#ifndef BENCHMARK_PARSER_INLINING_HPP_
2+
#define BENCHMARK_PARSER_INLINING_HPP_
3+
#include <benchmark/benchmark.h>
4+
5+
#include <cstdint>
6+
#include <vector>
7+
namespace parser_inlining {
8+
struct Record2 {
9+
float field0 = 0;
10+
float field1 = 1;
11+
bool operator==(const Record2&) const = default;
12+
};
13+
struct Record16 {
14+
float field0 = 0;
15+
float field1 = 1;
16+
float field2 = 2;
17+
float field3 = 3;
18+
float field4 = 4;
19+
float field5 = 5;
20+
float field6 = 6;
21+
float field7 = 7;
22+
float field8 = 8;
23+
float field9 = 9;
24+
float field10 = 10;
25+
float field11 = 11;
26+
float field12 = 12;
27+
float field13 = 13;
28+
float field14 = 14;
29+
float field15 = 15;
30+
bool operator==(const Record16&) const = default;
31+
};
32+
template <class Record>
33+
struct Data {
34+
std::vector<Record> records;
35+
bool operator==(const Data&) const = default;
36+
};
37+
template <class Record>
38+
auto make_data(std::size_t size) {
39+
Data<Record> data;
40+
data.records.resize(size);
41+
for (std::size_t i = 0; i < size; ++i) {
42+
data.records[i].field0 = static_cast<float>(i) * 0.5f;
43+
data.records[i].field1 = -static_cast<float>(i) * 0.25f;
44+
}
45+
return data;
46+
}
47+
template <class Record, class Write, class Read>
48+
void run(benchmark::State& state, Write write, Read read) {
49+
const auto input = make_data<Record>(state.range(0));
50+
const auto bytes = write(input);
51+
{
52+
const auto result = read(bytes);
53+
if (!result) {
54+
state.SkipWithError(result.error().what());
55+
return;
56+
}
57+
if (*result != input) {
58+
state.SkipWithError("Fields changed during round trip");
59+
return;
60+
}
61+
}
62+
for (auto _ : state) {
63+
auto result = read(bytes);
64+
if (!result) {
65+
state.SkipWithError(result.error().what());
66+
break;
67+
}
68+
benchmark::DoNotOptimize(result);
69+
}
70+
state.SetItemsProcessed(state.iterations() * state.range(0));
71+
state.SetBytesProcessed(state.iterations() *
72+
static_cast<std::int64_t>(bytes.size()));
73+
state.counters["serialized_bytes"] = static_cast<double>(bytes.size());
74+
}
75+
} // namespace parser_inlining
76+
#endif
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
#include <rfl/cereal.hpp>
2+
3+
#include "parser_inlining.hpp"
4+
namespace parser_inlining {
5+
template <class Record>
6+
void BM_parser_inlining_cereal(benchmark::State& state) {
7+
run<Record>(
8+
state, [&](const auto& input) { return rfl::cereal::write(input); },
9+
[&](const auto& bytes) {
10+
return rfl::cereal::read<Data<Record>>(bytes);
11+
});
12+
}
13+
BENCHMARK_TEMPLATE(BM_parser_inlining_cereal, Record2)
14+
->Arg(100000)
15+
->Unit(benchmark::kMillisecond);
16+
BENCHMARK_TEMPLATE(BM_parser_inlining_cereal, Record16)
17+
->Arg(100000)
18+
->Unit(benchmark::kMillisecond);
19+
} // namespace parser_inlining
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
#include <rfl/json.hpp>
2+
3+
#include "parser_inlining.hpp"
4+
namespace parser_inlining {
5+
template <class Record>
6+
void BM_parser_inlining_json(benchmark::State& state) {
7+
run<Record>(
8+
state, [&](const auto& input) { return rfl::json::write(input); },
9+
[&](const auto& bytes) { return rfl::json::read<Data<Record>>(bytes); });
10+
}
11+
BENCHMARK_TEMPLATE(BM_parser_inlining_json, Record2)
12+
->Arg(100000)
13+
->Unit(benchmark::kMillisecond);
14+
BENCHMARK_TEMPLATE(BM_parser_inlining_json, Record16)
15+
->Arg(100000)
16+
->Unit(benchmark::kMillisecond);
17+
} // namespace parser_inlining
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
#include <rfl/msgpack.hpp>
2+
3+
#include "parser_inlining.hpp"
4+
namespace parser_inlining {
5+
template <class Record>
6+
void BM_parser_inlining_msgpack(benchmark::State& state) {
7+
run<Record>(
8+
state, [&](const auto& input) { return rfl::msgpack::write(input); },
9+
[&](const auto& bytes) {
10+
return rfl::msgpack::read<Data<Record>>(bytes);
11+
});
12+
}
13+
BENCHMARK_TEMPLATE(BM_parser_inlining_msgpack, Record2)
14+
->Arg(100000)
15+
->Unit(benchmark::kMillisecond);
16+
BENCHMARK_TEMPLATE(BM_parser_inlining_msgpack, Record16)
17+
->Arg(100000)
18+
->Unit(benchmark::kMillisecond);
19+
} // namespace parser_inlining

‎include/rfl/cereal/Reader.hpp‎

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#include "../Result.hpp"
1515
#include "../Vectorstring.hpp"
1616
#include "../always_false.hpp"
17+
#include "../internal/always_inline.hpp"
1718
#include "../internal/is_literal.hpp"
1819
#include "../parsing/schemaful/IsSchemafulReader.hpp"
1920

@@ -64,7 +65,8 @@ struct Reader {
6465
/// @param _var The Cereal archive variable containing the value
6566
/// @return A Result containing the converted value or an error
6667
template <class T>
67-
rfl::Result<T> to_basic_type(const InputVarType& _var) const noexcept {
68+
RFL_ALWAYS_INLINE rfl::Result<T> to_basic_type(
69+
const InputVarType& _var) const noexcept {
6870
try {
6971
if constexpr (internal::is_literal_v<T>) {
7072
std::string str;
@@ -174,8 +176,9 @@ struct Reader {
174176
/// @param _obj The Cereal object structure
175177
/// @return std::nullopt on success, or an Error if reading fails
176178
template <class ObjectReader>
177-
std::optional<Error> read_object(const ObjectReader& _object_reader,
178-
const InputObjectType& _obj) const noexcept {
179+
RFL_ALWAYS_INLINE std::optional<Error> read_object(
180+
const ObjectReader& _object_reader,
181+
const InputObjectType& _obj) const noexcept {
179182
try {
180183
[&]<int... Is>(std::integer_sequence<int, Is...>) {
181184
(_object_reader.read(std::integral_constant<int, Is>{},
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
#ifndef RFL_INTERNAL_ALWAYS_INLINE_HPP_
2+
#define RFL_INTERNAL_ALWAYS_INLINE_HPP_
3+
4+
#if defined(_MSC_VER)
5+
#define RFL_ALWAYS_INLINE __forceinline
6+
#elif defined(__GNUC__) || defined(__clang__)
7+
#define RFL_ALWAYS_INLINE inline __attribute__((always_inline))
8+
#else
9+
#define RFL_ALWAYS_INLINE inline
10+
#endif
11+
12+
#endif

‎include/rfl/internal/cpp20/Result.hpp‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#include <type_traits>
99

1010
#include "../Error.hpp"
11+
#include "../always_inline.hpp"
1112

1213
namespace rfl::internal::cpp20 {
1314

@@ -331,7 +332,7 @@ class Result {
331332
/// @note This overload is for rvalue references (&&), so the contained value
332333
/// is moved.
333334
template <class F>
334-
auto transform(const F& _f) && {
335+
RFL_ALWAYS_INLINE auto transform(const F& _f) && {
335336
/// Result_U is expected to be of type Result<U>.
336337
using U = std::invoke_result_t<F, T>;
337338
if (success_) {

0 commit comments

Comments
 (0)