Skip to content

Commit 528220f

Browse files
committed
format_escaped_str() fast and slow paths depending on page boundary
1 parent 29884e6 commit 528220f

File tree

4 files changed

+117
-48
lines changed

4 files changed

+117
-48
lines changed

Cargo.lock

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

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ encoding_rs = { version = "0.8", default_features = false }
5656
itoa = { version = "1", default_features = false }
5757
itoap = { version = "1", features = ["std", "simd"] }
5858
once_cell = { version = "1", default_features = false, features = ["race"] }
59+
page_size = { version = "0.6" }
5960
pyo3-ffi = { version = "^0.20.2", default_features = false, features = ["extension-module"]}
6061
ryu = { version = "1", default_features = false }
6162
serde = { version = "1", default_features = false }

src/serialize/writer/simd.rs

Lines changed: 75 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -2,67 +2,37 @@
22
// Copyright 2023-2024 liuq19, ijl
33
// adapted from sonic-rs' src/util/string.rs
44

5+
use crate::typeref::PAGE_SIZE;
56
use core::simd::cmp::{SimdPartialEq, SimdPartialOrd};
67

78
macro_rules! impl_escape_unchecked {
89
($src:expr, $dst:expr, $nb:expr, $omask:expr, $cn:expr) => {
910
$nb -= $cn;
1011
$dst = $dst.add($cn);
1112
$src = $src.add($cn);
12-
let mut mask = $omask << $cn;
13+
$omask >>= $cn;
1314
loop {
1415
$nb -= 1;
15-
mask = mask << 1;
16-
let replacement = if *($src) == b'"' {
17-
(*b"\\\"\0\0\0\0\0\0", 2)
16+
$omask = $omask >> 1;
17+
18+
if *($src) == b'"' {
19+
core::ptr::copy_nonoverlapping(b"\\\"".as_ptr(), $dst, 2);
20+
$dst = $dst.add(2);
1821
} else if *($src) == b'\\' {
19-
(*b"\\\\\0\0\0\0\0\0", 2)
22+
core::ptr::copy_nonoverlapping(b"\\\\".as_ptr(), $dst, 2);
23+
$dst = $dst.add(2);
2024
} else {
21-
match *($src) {
22-
0 => (*b"\\u0000\0\0", 6),
23-
1 => (*b"\\u0001\0\0", 6),
24-
2 => (*b"\\u0002\0\0", 6),
25-
3 => (*b"\\u0003\0\0", 6),
26-
4 => (*b"\\u0004\0\0", 6),
27-
5 => (*b"\\u0005\0\0", 6),
28-
6 => (*b"\\u0006\0\0", 6),
29-
7 => (*b"\\u0007\0\0", 6),
30-
8 => (*b"\\b\0\0\0\0\0\0", 2),
31-
9 => (*b"\\t\0\0\0\0\0\0", 2),
32-
10 => (*b"\\n\0\0\0\0\0\0", 2),
33-
11 => (*b"\\u000b\0\0", 6),
34-
12 => (*b"\\f\0\0\0\0\0\0", 2),
35-
13 => (*b"\\r\0\0\0\0\0\0", 2),
36-
14 => (*b"\\u000e\0\0", 6),
37-
15 => (*b"\\u000f\0\0", 6),
38-
16 => (*b"\\u0010\0\0", 6),
39-
17 => (*b"\\u0011\0\0", 6),
40-
18 => (*b"\\u0012\0\0", 6),
41-
19 => (*b"\\u0013\0\0", 6),
42-
20 => (*b"\\u0014\0\0", 6),
43-
21 => (*b"\\u0015\0\0", 6),
44-
22 => (*b"\\u0016\0\0", 6),
45-
23 => (*b"\\u0017\0\0", 6),
46-
24 => (*b"\\u0018\0\0", 6),
47-
25 => (*b"\\u0019\0\0", 6),
48-
26 => (*b"\\u001a\0\0", 6),
49-
27 => (*b"\\u001b\0\0", 6),
50-
28 => (*b"\\u001c\0\0", 6),
51-
29 => (*b"\\u001d\0\0", 6),
52-
30 => (*b"\\u001e\0\0", 6),
53-
31 => (*b"\\u001f\0\0", 6),
54-
_ => unreachable!(),
55-
}
25+
$dst = write_unusual_escape($src, $dst);
5626
};
57-
core::ptr::copy_nonoverlapping(replacement.0.as_ptr(), $dst, 8);
58-
$dst = $dst.add(replacement.1 as usize);
27+
5928
$src = $src.add(1);
60-
if likely!(mask & (1 << (STRIDE - 1)) != 1) {
29+
if likely!($omask & 1 != 1) {
6130
break;
6231
}
6332
}
6433
};
6534
}
35+
6636
macro_rules! impl_format_simd {
6737
($odptr:expr, $value_ptr:expr, $value_len:expr) => {
6838
let mut dptr = $odptr;
@@ -81,7 +51,7 @@ macro_rules! impl_format_simd {
8151
while nb >= STRIDE {
8252
let v = StrVector::from_slice(core::slice::from_raw_parts(sptr, STRIDE));
8353
v.copy_to_slice(core::slice::from_raw_parts_mut(dptr, STRIDE));
84-
let mask =
54+
let mut mask =
8555
(v.simd_eq(blash) | v.simd_eq(quote) | v.simd_lt(x20)).to_bitmask() as u32;
8656

8757
if likely!(mask == 0) {
@@ -95,12 +65,18 @@ macro_rules! impl_format_simd {
9565
}
9666

9767
while nb > 0 {
98-
let mut v = StrVector::default();
99-
v.as_mut_array()[..nb].copy_from_slice(core::slice::from_raw_parts(sptr, nb));
68+
let v = if unlikely!(is_cross_page!(sptr)) {
69+
let mut v = StrVector::default();
70+
v.as_mut_array()[..nb].copy_from_slice(core::slice::from_raw_parts(sptr, nb));
71+
v
72+
} else {
73+
StrVector::from_slice(core::slice::from_raw_parts(sptr, STRIDE))
74+
};
10075
v.copy_to_slice(core::slice::from_raw_parts_mut(dptr, STRIDE));
101-
let mask = (v.simd_eq(blash) | v.simd_eq(quote) | v.simd_lt(x20)).to_bitmask()
76+
let mut mask = (v.simd_eq(blash) | v.simd_eq(quote) | v.simd_lt(x20)).to_bitmask()
10277
as u32
103-
& (STRIDE_SATURATION >> (STRIDE - nb));
78+
& (STRIDE_SATURATION >> (32 - STRIDE - nb));
79+
10480
if likely!(mask == 0) {
10581
dptr = dptr.add(nb);
10682
break;
@@ -118,6 +94,57 @@ macro_rules! impl_format_simd {
11894
};
11995
}
12096

97+
macro_rules! is_cross_page {
98+
($src:expr) => {
99+
unsafe { (($src as usize & (PAGE_SIZE - 1)) + STRIDE) > PAGE_SIZE }
100+
};
101+
}
102+
103+
#[cold]
104+
#[inline(never)]
105+
fn write_unusual_escape(sptr: *const u8, dptr: *mut u8) -> *mut u8 {
106+
unsafe {
107+
debug_assert!(*sptr < 32);
108+
let replacement = match *(sptr) {
109+
0 => (*b"\\u0000\0\0", 6),
110+
1 => (*b"\\u0001\0\0", 6),
111+
2 => (*b"\\u0002\0\0", 6),
112+
3 => (*b"\\u0003\0\0", 6),
113+
4 => (*b"\\u0004\0\0", 6),
114+
5 => (*b"\\u0005\0\0", 6),
115+
6 => (*b"\\u0006\0\0", 6),
116+
7 => (*b"\\u0007\0\0", 6),
117+
8 => (*b"\\b\0\0\0\0\0\0", 2),
118+
9 => (*b"\\t\0\0\0\0\0\0", 2),
119+
10 => (*b"\\n\0\0\0\0\0\0", 2),
120+
11 => (*b"\\u000b\0\0", 6),
121+
12 => (*b"\\f\0\0\0\0\0\0", 2),
122+
13 => (*b"\\r\0\0\0\0\0\0", 2),
123+
14 => (*b"\\u000e\0\0", 6),
124+
15 => (*b"\\u000f\0\0", 6),
125+
16 => (*b"\\u0010\0\0", 6),
126+
17 => (*b"\\u0011\0\0", 6),
127+
18 => (*b"\\u0012\0\0", 6),
128+
19 => (*b"\\u0013\0\0", 6),
129+
20 => (*b"\\u0014\0\0", 6),
130+
21 => (*b"\\u0015\0\0", 6),
131+
22 => (*b"\\u0016\0\0", 6),
132+
23 => (*b"\\u0017\0\0", 6),
133+
24 => (*b"\\u0018\0\0", 6),
134+
25 => (*b"\\u0019\0\0", 6),
135+
26 => (*b"\\u001a\0\0", 6),
136+
27 => (*b"\\u001b\0\0", 6),
137+
28 => (*b"\\u001c\0\0", 6),
138+
29 => (*b"\\u001d\0\0", 6),
139+
30 => (*b"\\u001e\0\0", 6),
140+
31 => (*b"\\u001f\0\0", 6),
141+
_ => unreachable!(),
142+
};
143+
core::ptr::copy_nonoverlapping(replacement.0.as_ptr(), dptr, 8);
144+
dptr.add(replacement.1 as usize)
145+
}
146+
}
147+
121148
#[inline(never)]
122149
pub unsafe fn format_escaped_str_impl_128(
123150
odptr: *mut u8,

src/typeref.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,9 @@ pub static mut DESCR_STR: *mut PyObject = null_mut();
7575
pub static mut VALUE_STR: *mut PyObject = null_mut();
7676
pub static mut INT_ATTR_STR: *mut PyObject = null_mut();
7777

78+
#[cfg(feature = "unstable-simd")]
79+
pub static mut PAGE_SIZE: usize = 0;
80+
7881
#[cfg(feature = "yyjson")]
7982
pub const YYJSON_BUFFER_SIZE: usize = 1024 * 1024 * 8;
8083

@@ -135,6 +138,11 @@ pub fn init_typerefs() {
135138
fn _init_typerefs_impl() -> bool {
136139
unsafe {
137140
debug_assert!(crate::opt::MAX_OPT < u16::MAX as i32);
141+
142+
#[cfg(feature = "unstable-simd")]
143+
{
144+
PAGE_SIZE = page_size::get();
145+
}
138146
assert!(crate::deserialize::KEY_MAP
139147
.set(crate::deserialize::KeyMap::default())
140148
.is_ok());

0 commit comments

Comments
 (0)