Skip to content

Commit d60af67

Browse files
author
Daniel Lemire
committed
adding a demo and making things look nicer
1 parent b82c5bd commit d60af67

11 files changed

Lines changed: 102 additions & 38 deletions

File tree

CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ target_include_directories(
5252
include(cmake/CPM.cmake)
5353
CPMAddPackage(
5454
NAME simdjson
55-
GITHUB_REPOSITORY the-moisrex/simdjson-custom-types
55+
GITHUB_REPOSITORY simdjson/simdjson
5656
GIT_TAG builder_development_branch
5757
) # include simdjson
5858

README.md

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ Or use one of the Macros described
5757

5858
By leveraging compile-time reflection, we can support for something as simple as:
5959

60-
```c++
60+
```cpp
6161
int main() {
6262
X s1 = {.a = '1', .b = 10, .c = 0, .d = "test string\n\r\"", .e = {1, 2, 3}, .f = {"ab", "cd", "fg"}};
6363

@@ -66,7 +66,31 @@ int main() {
6666
}
6767
```
6868

69-
As of now, we were able to implement this with ~1k lines of code (and that is including Neon + SSE2 optimizations in [json_escaping.hpp](src/json_escaping.hpp)).
69+
We can make the following example work at compile time:
70+
```cpp
71+
struct kid {
72+
int age;
73+
std::string name;
74+
std::vector<std::string> toys;
75+
};
76+
77+
void demo() {
78+
simdjson::padded_string json_str =
79+
R"({"age": 12, "name": "John", "toys": ["car", "ball"]})"_padded;
80+
simdjson::ondemand::parser parser;
81+
auto doc = parser.iterate(json_str);
82+
kid k = doc.get<kid>();
83+
std::print("I am {} years old\n", k.age);
84+
for (const auto &toy : k.toys) {
85+
std::print("I have a {}\n", toy);
86+
}
87+
std::print("My name is {}\n", k.name);
88+
89+
std::print("My JSON is {}\n", simdjson::json_builder::to_json_string(k));
90+
}
91+
```
92+
93+
7094
7195
The benchmark results show that our serialization speed can be 20/30x faster than [nlohmann](https://github.com/nlohmann/json).
7296

benchmarks/src/benchmark_serialization.cpp

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -163,8 +163,7 @@ void bench_custom(std::vector<User> &data) {
163163
}
164164

165165
template <class T> void bench_fast_simpler(std::vector<T> &data) {
166-
simdjson::json_builder::StringBuilder b(32 * 1024 *
167-
1024); // pre-allocate 32 MB
166+
simdjson::json_builder::string_builder b;
168167
simdjson::json_builder::fast_to_json_string(b, data);
169168
size_t output_volume = b.size();
170169
b.reset();
@@ -182,6 +181,23 @@ template <class T> void bench_fast_simpler(std::vector<T> &data) {
182181
}));
183182
}
184183

184+
185+
template <class T> void bench_fast_with_alloc(std::vector<T> &data) {
186+
std::string json = simdjson::json_builder::to_json_string(data);
187+
size_t output_volume = json.size();
188+
printf("# output volume: %zu bytes\n", output_volume);
189+
190+
volatile size_t measured_volume = 0;
191+
pretty_print(data.size(), output_volume, "bench_fast_with_alloc",
192+
bench([&data, &measured_volume, &output_volume]() {
193+
std::string json = simdjson::json_builder::to_json_string(data);
194+
measured_volume = json.size();
195+
if (measured_volume != output_volume) {
196+
printf("mismatch\n");
197+
}
198+
}));
199+
}
200+
185201
void bench_nlohmann(std::vector<User> &data) {
186202
std::string output = nlohmann_serialize(data);
187203
size_t output_volume = output.size();
@@ -206,8 +222,9 @@ int main() {
206222

207223
std::vector<std::vector<User>> in_array = {test_data};
208224
bench_nlohmann(test_data);
209-
bench_custom(test_data);
225+
//bench_custom(test_data);
210226
bench_fast_simpler(test_data);
227+
bench_fast_with_alloc(test_data);
211228
#if SIMDJSON_BENCH_CPP_REFLECT
212229
bench_reflect_cpp(test_data);
213230
#endif

benchmarks/src/benchmark_serialization_twitter.cpp

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,7 @@
1919
#endif
2020

2121
template <class T> void bench_fast_simpler(T &data) {
22-
simdjson::json_builder::StringBuilder b(32 * 1024 *
23-
1024); // pre-allocate 32 MB
22+
simdjson::json_builder::string_builder b;
2423
simdjson::json_builder::fast_to_json_string(b, data);
2524
size_t output_volume = b.size();
2625
b.reset();
@@ -38,6 +37,24 @@ template <class T> void bench_fast_simpler(T &data) {
3837
}));
3938
}
4039

40+
41+
template <class T> void bench_fast_with_alloc(T &data) {
42+
std::string json = simdjson::json_builder::to_json_string(data);
43+
size_t output_volume = json.size();
44+
printf("# output volume: %zu bytes\n", output_volume);
45+
46+
volatile size_t measured_volume = 0;
47+
pretty_print(sizeof(data), output_volume, "bench_fast_with_alloc",
48+
bench([&data, &measured_volume, &output_volume]() {
49+
std::string json = simdjson::json_builder::to_json_string(data);
50+
51+
measured_volume = json.size();
52+
if (measured_volume != output_volume) {
53+
printf("mismatch\n");
54+
}
55+
}));
56+
}
57+
4158
void bench_nlohmann(TwitterData &data) {
4259
std::string output = nlohmann_serialize(data);
4360
size_t output_volume = output.size();
@@ -143,8 +160,7 @@ void test_correctness(std::string_view json_str) {
143160
// Now let's do a round-trip
144161

145162
// Serializing the simd_struct back to a string
146-
simdjson::json_builder::StringBuilder b(32 * 1024 *
147-
1024); // pre-allocate 32 MB
163+
simdjson::json_builder::string_builder b;
148164
simdjson::json_builder::fast_to_json_string(b, simd_struct);
149165
std::string simd_struct_to_json = std::string(b.view());
150166

@@ -173,9 +189,10 @@ int main() {
173189
simpleparser::json_parser::JsonParser parser(json_str);
174190
auto json_value = parser.parse();
175191
TwitterData my_struct;
176-
simpleparser::json_builder::from_json(json_value, my_struct);
177-
bench_fast_simpler(my_struct);
178192
bench_nlohmann(my_struct);
193+
//simpleparser::json_builder::from_json(json_value, my_struct);
194+
bench_fast_simpler(my_struct);
195+
bench_fast_with_alloc(my_struct);
179196
#if SIMDJSON_BENCH_CPP_REFLECT
180197
bench_reflect_cpp(my_struct);
181198
#endif

examples/CMakeLists.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
add_compile_options(-Wall -Wextra -Weffc++ -Wsign-compare -Wshadow -Wwrite-strings
22
-Wpointer-arith -Winit-self -Wconversion -Wno-sign-conversion)
33

4+
add_executable(demo demo.cpp)
5+
target_link_libraries(demo PRIVATE simdjson::serialization simdjson::simdjson)
6+
47
add_executable(example example.cpp)
58
target_link_libraries(example PRIVATE simdjson::serialization simdjson::simdjson)
69

examples/example.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ int main() {
4040
.i = {1, 2, 3},
4141
.z = {.x = 1000}}};
4242

43-
simdjson::json_builder::StringBuilder sb;
43+
simdjson::json_builder::string_builder sb;
4444
simdjson::json_builder::fast_to_json_string(sb, s1);
4545
std::cout << sb.c_str() << std::endl;
4646
return EXIT_SUCCESS;

examples/example2.cpp

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -139,19 +139,5 @@ int main() {
139139
std::cout << my_struct.id << std::endl;
140140
std::cout << my_struct.name << std::endl;
141141
std::cout << my_struct.values.size() << std::endl;
142-
143-
/* simdjson::json_builder::StringBuilder sb;
144-
simdjson::json_builder::fast_to_json_string(sb, my_struct);
145-
std::cout << sb.c_str() << std::endl;
146-
147-
std::string json_str_nested =
148-
R"({"a":1,"b":10,"c":0,"d":"test string\n\r\"","e":[1,2,3],"f":["ab","cd","fg"],"y":{"g":100,"h":"test string\n\r\"","i":[1,2,3]}})";
149-
doc = parser.iterate(json_str_nested);
150-
151-
X s1 = X(doc);
152-
simdjson::json_builder::StringBuilder sb2;
153-
simdjson::json_builder::fast_to_json_string(sb2, s1);
154-
std::cout << sb2.c_str() << std::endl;*/
155-
156142
return 0;
157143
}

include/simdjson/json_builder/json_builder.h

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ template <typename R> consteval auto expand(R range) {
5454

5555
template <class T>
5656
requires(ContainerButNotString<T>)
57-
constexpr void atom(StringBuilder &b, const T &t) {
57+
constexpr void atom(string_builder &b, const T &t) {
5858
if (t.size() == 0) {
5959
b.append_unescaped("[]");
6060
return;
@@ -72,11 +72,11 @@ template <class T>
7272
requires(std::is_same_v<T, std::string> ||
7373
std::is_same_v<T, std::string_view> ||
7474
std::is_same_v<T, const char *>)
75-
constexpr void atom(StringBuilder &b, const T &t) {
75+
constexpr void atom(string_builder &b, const T &t) {
7676
b.append(t);
7777
}
7878

79-
template <arithmetic T> constexpr void atom(StringBuilder &b, const T t) {
79+
template <arithmetic T> constexpr void atom(string_builder &b, const T t) {
8080
b.append(t);
8181
}
8282

@@ -85,7 +85,7 @@ template <class T>
8585
!std::is_same_v<T, std::string> &&
8686
!std::is_same_v<T, std::string_view> &&
8787
!std::is_same_v<T, const char *>)
88-
constexpr void atom(StringBuilder &b, const T &t) {
88+
constexpr void atom(string_builder &b, const T &t) {
8989
int i = 0;
9090
b.append('{');
9191
[:expand(std::meta::nonstatic_data_members_of(^T)):] >> [&]<auto dm> {
@@ -103,7 +103,7 @@ constexpr void atom(StringBuilder &b, const T &t) {
103103
}
104104

105105
// works for struct
106-
template <class Z> void fast_to_json_string(StringBuilder &b, const Z &z) {
106+
template <class Z> void fast_to_json_string(string_builder &b, const Z &z) {
107107
int i = 0;
108108
b.append('{');
109109
[:expand(std::meta::nonstatic_data_members_of(^Z)):] >> [&]<auto dm> {
@@ -123,7 +123,7 @@ template <class Z> void fast_to_json_string(StringBuilder &b, const Z &z) {
123123
// works for container
124124
template <class Z>
125125
requires(ContainerButNotString<Z>)
126-
void fast_to_json_string(StringBuilder &b, const Z &z) {
126+
void fast_to_json_string(string_builder &b, const Z &z) {
127127
if (z.size() == 0) {
128128
b.append_unescaped("[]");
129129
return;
@@ -137,6 +137,20 @@ void fast_to_json_string(StringBuilder &b, const Z &z) {
137137
b.append(']');
138138
}
139139

140+
template <class Z>
141+
std::string to_json_string(const Z &z) {
142+
string_builder b;
143+
fast_to_json_string(b, z);
144+
return std::string(b);
145+
}
146+
147+
template <class Z>
148+
void to_json(const Z &z, std::string &s) {
149+
string_builder b;
150+
fast_to_json_string(b, z);
151+
s.assign(b.view());
152+
}
153+
140154
} // namespace json_builder
141155

142156
} // namespace simdjson

include/simdjson/json_builder/string_builder.h

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,9 @@ namespace json_builder {
1313
template <typename T>
1414
concept arithmetic = std::is_arithmetic_v<T>;
1515

16-
class StringBuilder final {
16+
class string_builder final {
1717
public:
18-
// By default, we allocate 1 MB.
19-
StringBuilder(size_t initial_capacity = 1048576)
18+
string_builder(size_t initial_capacity = 1024)
2019
: buffer(new char[initial_capacity]), position(0),
2120
capacity(initial_capacity) {}
2221

@@ -85,6 +84,10 @@ class StringBuilder final {
8584
position += len;
8685
}
8786

87+
operator std::string() const { return std::string(view()); }
88+
89+
operator std::string_view() const { return view(); }
90+
8891
std::string_view view() const {
8992
return std::string_view(buffer.get(), position);
9093
}

include/simdjson/json_builder/universal_formatter.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ namespace json_builder {
1212
struct universal_formatter {
1313
constexpr auto parse(auto &ctx) { return ctx.begin(); }
1414

15-
template <typename T> auto format(T const &t, StringBuilder &sb) const {
15+
template <typename T> auto format(T const &t, string_builder &sb) const {
1616
sb.append('{');
1717
auto delim = [first = true, &sb]() mutable {
1818
if (!first) {

0 commit comments

Comments
 (0)