Skip to content

Commit af985cb

Browse files
authored
Merge pull request #14 from lpgauth/perf/serde-single-pass-decode
Single-pass serde decoder: skip intermediate sonic-rs Value DOM
2 parents 4d55dc2 + 84c906d commit af985cb

7 files changed

Lines changed: 425 additions & 61 deletions

File tree

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

README.md

Lines changed: 38 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -147,64 +147,64 @@ Apple M2 Pro, OTP 28, Elixir 1.19:
147147

148148
| Library | ips | mean | median | p99 | memory |
149149
|---|---|---|---|---|---|
150-
| torque | **262.0K** | **3.82 μs** | **3.54 μs** | **8.08 μs** | **1.56 KB** |
151-
| simdjsone | 187.7K | 5.33 μs | 4.96 μs | 12.46 μs | 1.59 KB |
152-
| jiffy | 156.1K | 6.41 μs | 5.79 μs | 16.17 μs | **1.56 KB** |
153-
| otp json | 130.9K | 7.64 μs | 7.21 μs | 15.17 μs | 7.73 KB |
154-
| jason | 107.1K | 9.34 μs | 8.54 μs | 20.92 μs | 9.54 KB |
150+
| torque | **264.1K** | **3.79 μs** | **3.63 μs** | **6.83 μs** | **1.56 KB** |
151+
| simdjsone | 182.8K | 5.47 μs | 5.21 μs | 10.00 μs | 1.59 KB |
152+
| jiffy | 145.1K | 6.89 μs | 6.25 μs | 14.50 μs | **1.56 KB** |
153+
| otp json | 132.5K | 7.55 μs | 7.25 μs | 13.21 μs | 7.73 KB |
154+
| jason | 107.9K | 9.26 μs | 8.58 μs | 19.25 μs | 9.54 KB |
155155

156156
### Decode (750 KB JSON)
157157

158158
| Library | ips | mean | median | p99 | memory |
159159
|---|---|---|---|---|---|
160-
| torque | **525.2** | **1.90 ms** | **1.58 ms** | **2.85 ms** | **1.56 KB** |
161-
| simdjsone | 462.7 | 2.16 ms | 1.82 ms | 3.18 ms | **1.56 KB** |
162-
| otp json | 198.6 | 5.04 ms | 5.08 ms | 5.87 ms | 2.49 MB |
163-
| jason | 145.3 | 6.88 ms | 6.88 ms | 7.28 ms | 3.55 MB |
164-
| jiffy | 109.0 | 9.17 ms | 9.35 ms | 10.05 ms | 5.53 MB |
160+
| torque | **536.0** | **1.87 ms** | **1.67 ms** | **2.22 ms** | **1.56 KB** |
161+
| simdjsone | 435.1 | 2.30 ms | 1.80 ms | 3.31 ms | **1.56 KB** |
162+
| otp json | 199.4 | 5.02 ms | 5.07 ms | 5.83 ms | 2.49 MB |
163+
| jason | 147.2 | 6.80 ms | 6.77 ms | 7.17 ms | 3.55 MB |
164+
| jiffy | 116.6 | 8.58 ms | 8.69 ms | 9.78 ms | 5.53 MB |
165165

166166
### Encode (1.2 KB JSON)
167167

168168
| Library | ips | mean | median | p99 | memory |
169169
|---|---|---|---|---|---|
170-
| torque: proplist => iodata | **1287.4K** | **0.78 μs** | **0.71 μs** | **0.96 μs** | **64 B** |
171-
| torque: proplist => binary | 1259.3K | 0.79 μs | **0.71 μs** | **0.96 μs** | 88 B |
172-
| torque: map => iodata | 1065.7K | 0.94 μs | 0.88 μs | 1.17 μs | **64 B** |
173-
| otp json: map => iodata | 1056.7K | 0.95 μs | 0.88 μs | 1.54 μs | 3928 B |
174-
| torque: map => binary | 1047.9K | 0.95 μs | 0.88 μs | 1.17 μs | 88 B |
175-
| jason: map => iodata | 611.8K | 1.63 μs | 1.50 μs | 3.08 μs | 3848 B |
176-
| otp json: map => binary | 580.6K | 1.72 μs | 1.54 μs | 3.42 μs | 3992 B |
177-
| jiffy: proplist => iodata | 571.2K | 1.75 μs | 1.50 μs | 3.33 μs | 120 B |
178-
| jiffy: map => iodata | 487.5K | 2.05 μs | 1.79 μs | 4.42 μs | 824 B |
179-
| simdjsone: proplist => iodata | 452.6K | 2.21 μs | 2.00 μs | 3.38 μs | 184 B |
180-
| jason: map => binary | 390.7K | 2.56 μs | 2.33 μs | 6.13 μs | 3912 B |
181-
| simdjsone: map => iodata | 384.0K | 2.60 μs | 2.38 μs | 5.50 μs | 888 B |
170+
| torque: proplist => binary | **1075.2K** | **0.93 μs** | **0.88 μs** | **1.04 μs** | 88 B |
171+
| otp json: map => iodata | 1074.0K | 0.93 μs | **0.88 μs** | 1.29 μs | 3928 B |
172+
| torque: proplist => iodata | 1069.6K | 0.93 μs | **0.88 μs** | 1.08 μs | **64 B** |
173+
| torque: map => iodata | 924.8K | 1.08 μs | 1.04 μs | 1.21 μs | **64 B** |
174+
| torque: map => binary | 914.6K | 1.09 μs | 1.04 μs | 1.25 μs | 88 B |
175+
| otp json: map => binary | 584.0K | 1.71 μs | 1.58 μs | 3.04 μs | 3992 B |
176+
| jiffy: proplist => iodata | 523.8K | 1.91 μs | 1.71 μs | 2.25 μs | 120 B |
177+
| jason: map => iodata | 515.0K | 1.94 μs | 1.54 μs | 14.17 μs | 3848 B |
178+
| jiffy: map => iodata | 431.4K | 2.32 μs | 2.13 μs | 2.83 μs | 824 B |
179+
| simdjsone: proplist => iodata | 424.3K | 2.36 μs | 2.25 μs | 2.83 μs | 184 B |
180+
| jason: map => binary | 352.6K | 2.84 μs | 2.42 μs | 15.13 μs | 3912 B |
181+
| simdjsone: map => iodata | 352.2K | 2.84 μs | 2.71 μs | 3.46 μs | 888 B |
182182

183183
### Encode (750 KB JSON)
184184

185185
| Library | ips | mean | median | p99 | memory |
186186
|---|---|---|---|---|---|
187-
| torque: proplist => iodata | **1274.1** | **0.78 ms** | **0.78 ms** | **0.90 ms** | **64 B** |
188-
| torque: proplist => binary | 1273.9 | 0.79 ms | **0.78 ms** | **0.90 ms** | 88 B |
189-
| torque: map => iodata | 1103.3 | 0.91 ms | 0.90 ms | 1.03 ms | **64 B** |
190-
| torque: map => binary | 1103.2 | 0.91 ms | 0.90 ms | 1.02 ms | 88 B |
191-
| otp json: map => iodata | 522.8 | 1.91 ms | 1.88 ms | 2.20 ms | 5.40 MB |
192-
| jiffy: proplist => iodata | 328.9 | 3.04 ms | 2.79 ms | 4.11 ms | 37.7 KB |
193-
| jiffy: map => iodata | 296.1 | 3.38 ms | 3.28 ms | 3.94 ms | 1.06 MB |
194-
| otp json: map => binary | 262.0 | 3.82 ms | 3.74 ms | 5.36 ms | 5.40 MB |
195-
| simdjsone: proplist => iodata | 238.2 | 4.20 ms | 3.89 ms | 5.19 ms | 37.7 KB |
196-
| simdjsone: map => iodata | 219.5 | 4.55 ms | 4.31 ms | 5.55 ms | 1.06 MB |
197-
| jason: map => iodata | 209.3 | 4.78 ms | 4.77 ms | 5.60 ms | 4.96 MB |
198-
| jason: map => binary | 151.7 | 6.59 ms | 6.57 ms | 7.73 ms | 4.96 MB |
187+
| torque: proplist => iodata | **1058.7** | **0.94 ms** | **0.93 ms** | **1.06 ms** | **64 B** |
188+
| torque: proplist => binary | 1048.4 | 0.95 ms | 0.93 ms | 1.09 ms | 88 B |
189+
| torque: map => iodata | 937.0 | 1.07 ms | 1.05 ms | 1.21 ms | **64 B** |
190+
| torque: map => binary | 933.8 | 1.07 ms | 1.05 ms | 1.22 ms | 88 B |
191+
| jiffy: proplist => iodata | 341.4 | 2.93 ms | 2.87 ms | 4.41 ms | 37.7 KB |
192+
| otp json: map => iodata | 278.9 | 3.59 ms | 3.66 ms | 4.69 ms | 5.40 MB |
193+
| jiffy: map => iodata | 275.8 | 3.63 ms | 3.74 ms | 4.17 ms | 1.06 MB |
194+
| jason: map => iodata | 268.0 | 3.73 ms | 3.43 ms | 5.65 ms | 4.96 MB |
195+
| simdjsone: proplist => iodata | 249.4 | 4.01 ms | 3.95 ms | 5.58 ms | 37.7 KB |
196+
| simdjsone: map => iodata | 211.6 | 4.73 ms | 4.81 ms | 5.44 ms | 1.06 MB |
197+
| otp json: map => binary | 210.7 | 4.75 ms | 4.85 ms | 6.57 ms | 5.40 MB |
198+
| jason: map => binary | 137.9 | 7.25 ms | 6.84 ms | 8.85 ms | 4.96 MB |
199199

200200
### Parse + Get (1.2 KB JSON)
201201

202202
| Library | ips | mean | median | p99 | memory |
203203
|---|---|---|---|---|---|
204-
| torque parse+get_many_nil | **196.5K** | **5.09 μs** | **4.46 μs** | **10.79 μs** | 1.59 KB |
205-
| torque parse+get_many | 190.3K | 5.26 μs | 4.54 μs | 10.88 μs | **1.58 KB** |
206-
| torque parse+get | 155.6K | 6.43 μs | 5.75 μs | 14.29 μs | 2.80 KB |
207-
| simdjsone parse+get | 125.2K | 7.99 μs | 5.83 μs | 30.83 μs | 2.28 KB |
204+
| torque parse+get_many_nil | **493.2K** | **2.03 μs** | **1.75 μs** | **3.21 μs** | **288 B** |
205+
| torque parse+get_many | 472.3K | 2.12 μs | 1.79 μs | 3.67 μs | 408 B |
206+
| torque parse+get | 428.3K | 2.34 μs | 2.00 μs | 5.75 μs | 432 B |
207+
| simdjsone parse+get | 385.0K | 2.60 μs | 1.79 μs | 6.71 μs | 408 B |
208208

209209
Run benchmarks locally:
210210

native/torque_nif/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,5 +11,6 @@ crate-type = ["cdylib"]
1111
[dependencies]
1212
rustler = "0.36"
1313
sonic-rs = "0.3"
14+
serde = "1"
1415
itoa = "1"
1516
ryu = "1"

native/torque_nif/src/decoder.rs

Lines changed: 7 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use crate::atoms;
22
use crate::nif_util::make_tuple2;
3+
use crate::serde_decode;
34
use crate::types::{value_to_term, MAX_DEPTH};
45
use crate::ParsedDocument;
56
use rustler::sys::{enif_make_list_from_array, ERL_NIF_TERM};
@@ -230,32 +231,16 @@ fn array_length<'a>(env: Env<'a>, doc: ResourceArc<ParsedDocument>, path: &str)
230231
}
231232
}
232233

233-
fn do_decode<'a>(env: Env<'a>, bytes: &[u8]) -> Term<'a> {
234-
match sonic_rs::from_slice::<sonic_rs::Value>(bytes) {
235-
Ok(value) => match value_to_term(env, &value, MAX_DEPTH) {
236-
Some(term) => make_tuple2(env, atoms::ok().as_c_arg(), term.as_c_arg()),
237-
None => make_tuple2(
238-
env,
239-
atoms::error().as_c_arg(),
240-
atoms::nesting_too_deep().as_c_arg(),
241-
),
242-
},
243-
Err(e) => make_tuple2(
244-
env,
245-
atoms::error().as_c_arg(),
246-
format!("{}", e).encode(env).as_c_arg(),
247-
),
248-
}
249-
}
250-
251234
#[rustler::nif]
252-
fn decode<'a>(env: Env<'a>, json: Binary) -> Term<'a> {
253-
do_decode(env, json.as_slice())
235+
fn decode<'a>(env: Env<'a>, json: Binary<'a>) -> Term<'a> {
236+
let input_term = json.encode(env).as_c_arg();
237+
serde_decode::decode_to_term(env, input_term, json.as_slice())
254238
}
255239

256240
#[rustler::nif(schedule = "DirtyCpu")]
257-
fn decode_dirty<'a>(env: Env<'a>, json: Binary) -> Term<'a> {
258-
do_decode(env, json.as_slice())
241+
fn decode_dirty<'a>(env: Env<'a>, json: Binary<'a>) -> Term<'a> {
242+
let input_term = json.encode(env).as_c_arg();
243+
serde_decode::decode_to_term(env, input_term, json.as_slice())
259244
}
260245

261246
#[rustler::nif]

native/torque_nif/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ mod decoder;
33
mod encoder;
44
mod escape;
55
pub(crate) mod nif_util;
6+
pub(crate) mod serde_decode;
67
mod types;
78

89
pub struct ParsedDocument {

0 commit comments

Comments
 (0)