diff --git a/.github/workflows/artifact.yaml b/.github/workflows/artifact.yaml index 591ba840..e41d681d 100644 --- a/.github/workflows/artifact.yaml +++ b/.github/workflows/artifact.yaml @@ -1,7 +1,7 @@ name: artifact on: push env: - RUST_TOOLCHAIN: "nightly-2024-04-30" + RUST_TOOLCHAIN: "nightly-2024-06-10" CARGO_UNSTABLE_SPARSE_REGISTRY: "true" UNSAFE_PYO3_SKIP_VERSION_CHECK: "1" jobs: @@ -38,7 +38,7 @@ jobs: - run: python3 -m pip install --user -r test/requirements.txt -r integration/requirements.txt mypy - - run: pytest -s -rxX -v -n 4 test + - run: pytest -s -rxX -v -n 2 test env: PYTHONMALLOC: "debug" @@ -72,7 +72,7 @@ jobs: CC: "clang" CFLAGS: "-Os -fstrict-aliasing -fno-plt -flto=full -emit-llvm" LDFLAGS: "-fuse-ld=lld -Wl,-plugin-opt=also-emit-llvm -Wl,--as-needed -Wl,-zrelro,-znow" - RUSTFLAGS: "-C linker=clang -C link-arg=-fuse-ld=lld -C linker-plugin-lto -C lto=fat -C link-arg=-Wl,-zrelro,-znow -Z mir-opt-level=4 -Z virtual-function-elimination -Z threads=4 -D warnings" + RUSTFLAGS: "-C linker=clang -C link-arg=-fuse-ld=lld -C linker-plugin-lto -C lto=fat -C link-arg=-Wl,-zrelro,-znow -Z mir-opt-level=4 -Z virtual-function-elimination -Z threads=2 -D warnings" PATH: "/__w/orjson/orjson/.venv/bin:/github/home/.cargo/bin:/root/.local/bin:/root/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" container: image: fedora:41 @@ -108,7 +108,7 @@ jobs: --target=x86_64-unknown-linux-gnu uv pip install target/wheels/orjson*.whl - - run: pytest -s -rxX -v -n 4 test + - run: pytest -s -rxX -v -n 2 test env: PYTHONMALLOC: "debug" @@ -146,9 +146,11 @@ jobs: - target: aarch64-unknown-linux-musl arch: aarch64 platform: linux/arm64 + features: no-panic,unstable-simd,unwind,yyjson - target: x86_64-unknown-linux-musl arch: x86_64 platform: linux/amd64 + features: avx512,no-panic,unstable-simd,unwind,yyjson steps: - uses: actions/checkout@v4 @@ -163,13 +165,13 @@ jobs: CC: "gcc" CFLAGS: "-Os" LDFLAGS: "-Wl,--as-needed" - RUSTFLAGS: "-Z mir-opt-level=4 -Z threads=4 -D warnings -C target-feature=-crt-static" + RUSTFLAGS: "-C lto=fat -Z mir-opt-level=4 -Z virtual-function-elimination -Z threads=2 -D warnings -C target-feature=-crt-static" with: - rust-toolchain: nightly-2024-04-30 + rust-toolchain: nightly-2024-06-10 rustup-components: rust-src target: ${{ matrix.platform.target }} manylinux: musllinux_1_2 - args: --release --strip --out=dist --features=no-panic,unstable-simd,unwind,yyjson -i python${{ matrix.python.version }} + args: --release --strip --out=dist --features=${{ matrix.platform.features }} -i python${{ matrix.python.version }} - name: QEMU if: matrix.platform.arch != 'x86_64' @@ -192,7 +194,7 @@ jobs: venv/bin/pip install -U pip wheel venv/bin/pip install -r test/requirements.txt venv/bin/pip install orjson --no-index --find-links dist/ --force-reinstall - venv/bin/python -m pytest -s -rxX -v -n 4 test + venv/bin/python -m pytest -s -rxX -v -n 2 test - name: Store wheels if: "startsWith(github.ref, 'refs/tags/')" @@ -254,7 +256,7 @@ jobs: RUSTFLAGS: "${{ matrix.target.rustflags }}" with: target: ${{ matrix.target.target }} - rust-toolchain: nightly-2024-04-30 + rust-toolchain: nightly-2024-06-10 rustup-components: rust-src manylinux: auto args: --release --strip --out=dist --features=${{ matrix.target.features }} -i python${{ matrix.python.version }} @@ -317,7 +319,7 @@ jobs: - uses: dtolnay/rust-toolchain@master with: - toolchain: "nightly-2024-04-30" + toolchain: "nightly-2024-06-10" targets: "aarch64-apple-darwin, x86_64-apple-darwin" components: "rust-src" @@ -387,7 +389,7 @@ jobs: - uses: dtolnay/rust-toolchain@master with: - toolchain: "nightly-2024-04-30" + toolchain: "nightly-2024-06-10" targets: "aarch64-apple-darwin, x86_64-apple-darwin" components: "rust-src" diff --git a/.github/workflows/debug.yaml b/.github/workflows/debug.yaml index 098c0c83..9dcf041d 100644 --- a/.github/workflows/debug.yaml +++ b/.github/workflows/debug.yaml @@ -10,8 +10,8 @@ jobs: profile: [ { rust: "1.72", features: "" }, { rust: "1.72", features: "--features=yyjson" }, - { rust: "nightly-2024-04-30", features: "--features=yyjson,unstable-simd"}, - { rust: "nightly-2024-04-30", features: "--features=avx512,yyjson,unstable-simd"}, + { rust: "nightly-2024-06-10", features: "--features=yyjson,unstable-simd"}, + { rust: "nightly-2024-06-10", features: "--features=avx512,yyjson,unstable-simd"}, ] python: [ { version: '3.13' }, diff --git a/Cargo.lock b/Cargo.lock index b44ec58b..303fb0ab 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -32,9 +32,9 @@ checksum = "b993cd767a2bc7307dd87622311ca22c44329cc7a21366206bfa0896827b2bad" [[package]] name = "autocfg" -version = "1.2.0" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1fdabc7756949593fe60f30ec81974b613357de856987752631dea1e3394c80" +checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" [[package]] name = "beef" @@ -62,9 +62,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.0.96" +version = "1.0.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "065a29261d53ba54260972629f9ca6bffa69bac13cd1fed61420f7fa68b9f8bd" +checksum = "96c51067fd44124faa7f870b4b1c969379ad32b2ba805aa959430ceaa384f695" [[package]] name = "cfg-if" @@ -132,9 +132,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.14" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94b22e06ecb0110981051723910cbf0b5f5e09a2062dd7663334ee79a9d1286c" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" dependencies = [ "cfg-if", "libc", @@ -174,15 +174,15 @@ checksum = "9028f49264629065d057f340a86acb84867925865f73bbf8d47b4d149a7e88b8" [[package]] name = "libc" -version = "0.2.154" +version = "0.2.155" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae743338b92ff9146ce83992f766a31066a91a8c84a45e0e9f21e7cf6de6d346" +checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" [[package]] name = "no-panic" -version = "0.1.29" +version = "0.1.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c711522eedec2a96bb3672ad60a03561cb28934ab1e9b97d2ecb58e07c79ef52" +checksum = "8540b7d99a20166178b42a05776aef900cdbfec397f861dfc7819bf1d7760b3d" dependencies = [ "proc-macro2", "quote", @@ -191,9 +191,9 @@ dependencies = [ [[package]] name = "num-traits" -version = "0.2.18" +version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ "autocfg", ] @@ -234,9 +234,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.81" +version = "1.0.85" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d1597b0c024618f09a9c3b8655b7e430397a36d23fdafec26d6965e9eec3eba" +checksum = "22244ce15aa966053a896d1accb3a6e68469b97c7f33f284b99f0d576879fc23" dependencies = [ "unicode-ident", ] @@ -272,33 +272,33 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.15" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80af6f9131f277a45a3fba6ce8e2258037bb0477a67e610d3c1fe046ab31de47" +checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6" [[package]] name = "ryu" -version = "1.0.17" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" dependencies = [ "no-panic", ] [[package]] name = "serde" -version = "1.0.200" +version = "1.0.203" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddc6f9cc94d67c0e21aaf7eda3a010fd3af78ebf6e096aa6e2e13c79749cce4f" +checksum = "7253ab4de971e72fb7be983802300c30b5a7f0c2e56fab8abfc6a214307c0094" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.200" +version = "1.0.203" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "856f046b9400cee3c8c94ed572ecdb752444c24528c035cd35882aad6f492bcb" +checksum = "500cbc0ebeb6f46627f50f3f5811ccf6bf00643be300b4c3eabc0ef55dc5b5ba" dependencies = [ "proc-macro2", "quote", @@ -307,9 +307,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.116" +version = "1.0.117" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e17db7126d17feb94eb3fad46bf1a96b034e8aacbc2e775fe81505f8b0b2813" +checksum = "455182ea6142b14f93f4bc5320a2b31c1f266b66a4a5c858b013302a5d8cbfc3" dependencies = [ "itoa", "ryu", @@ -336,9 +336,9 @@ checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" [[package]] name = "syn" -version = "2.0.60" +version = "2.0.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "909518bc7b1c9b779f1bbf07f2929d35af9f0f37e47c6e9ef7f9dddc1e1821f3" +checksum = "c42f3f41a2de00b01c0aaad383c5a45241efc8b2d1eda5661812fda5f3cdcff5" dependencies = [ "proc-macro2", "quote", @@ -390,18 +390,18 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "zerocopy" -version = "0.7.32" +version = "0.7.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74d4d3961e53fa4c9a25a8637fc2bfaf2595b3d3ae34875568a5cf64787716be" +checksum = "ae87e3fcd617500e5d106f0380cf7b77f3c6092aae37191433159dda23cfb087" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.7.32" +version = "0.7.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" +checksum = "15e934569e47891f7d9411f1a451d947a60e000ab3bd24fbb970f000387d1b3b" dependencies = [ "proc-macro2", "quote", diff --git a/README.md b/README.md index b17fc7c6..cd1743f7 100644 --- a/README.md +++ b/README.md @@ -1205,7 +1205,7 @@ It benefits from also having a C build environment to compile a faster deserialization backend. See this project's `manylinux_2_28` builds for an example using clang and LTO. -The project's own CI tests against `nightly-2024-04-30` and stable 1.72. It +The project's own CI tests against `nightly-2024-06-10` and stable 1.72. It is prudent to pin the nightly version because that channel can introduce breaking changes. diff --git a/build.rs b/build.rs index 538d3a4b..9d9527b7 100644 --- a/build.rs +++ b/build.rs @@ -8,6 +8,7 @@ fn main() { println!("cargo:rerun-if-env-changed=CC"); println!("cargo:rerun-if-env-changed=CFLAGS"); println!("cargo:rerun-if-env-changed=LDFLAGS"); + println!("cargo:rerun-if-env-changed=ORJSON_DISABLE_AVX512"); println!("cargo:rerun-if-env-changed=ORJSON_DISABLE_SIMD"); println!("cargo:rerun-if-env-changed=ORJSON_DISABLE_YYJSON"); println!("cargo:rerun-if-env-changed=RUSTFLAGS"); @@ -40,6 +41,10 @@ fn main() { if env::var("ORJSON_DISABLE_SIMD").is_err() { if let Some(true) = version_check::supports_feature("portable_simd") { println!("cargo:rustc-cfg=feature=\"unstable-simd\""); + #[cfg(all(target_arch = "x86_64", target_feature = "avx512vl"))] + if env::var("ORJSON_DISABLE_AVX512").is_err() { + println!("cargo:rustc-cfg=feature=\"avx512\""); + } } } diff --git a/ci/azure-pipelines.yml b/ci/azure-pipelines.yml index cdc04f8d..7e64ad1c 100644 --- a/ci/azure-pipelines.yml +++ b/ci/azure-pipelines.yml @@ -1,5 +1,5 @@ variables: - toolchain: nightly-2024-04-30 + toolchain: nightly-2024-06-10 jobs: diff --git a/src/deserialize/error.rs b/src/deserialize/error.rs index 00e9dd02..710b9c8e 100644 --- a/src/deserialize/error.rs +++ b/src/deserialize/error.rs @@ -9,6 +9,7 @@ pub struct DeserializeError<'a> { #[cfg(not(feature = "yyjson"))] pub column: usize, // start at 1 pub data: Option<&'a str>, + #[cfg(feature = "yyjson")] pub pos: i64, } @@ -22,6 +23,7 @@ impl<'a> DeserializeError<'a> { #[cfg(not(feature = "yyjson"))] column: 0, data: None, + #[cfg(feature = "yyjson")] pos: 0, } } @@ -34,7 +36,6 @@ impl<'a> DeserializeError<'a> { line, column, data: Some(data), - pos: 0, } } diff --git a/src/deserialize/json.rs b/src/deserialize/json.rs index bb40ab85..9afd51e4 100644 --- a/src/deserialize/json.rs +++ b/src/deserialize/json.rs @@ -47,8 +47,8 @@ impl<'de> DeserializeSeed<'de> for JsonValue { impl<'de> Visitor<'de> for JsonValue { type Value = NonNull; - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter.write_str("JSON") + fn expecting(&self, _formatter: &mut fmt::Formatter) -> fmt::Result { + Ok(()) } fn visit_unit(self) -> Result { diff --git a/src/deserialize/yyjson.rs b/src/deserialize/yyjson.rs index 5ef0b762..41519749 100644 --- a/src/deserialize/yyjson.rs +++ b/src/deserialize/yyjson.rs @@ -199,18 +199,16 @@ fn populate_yy_array(list: *mut pyo3_ffi::PyObject, elem: *mut yyjson_val) { if unlikely!(unsafe_yyjson_is_ctn(val)) { next = unsafe_yyjson_get_next_container(val); if is_yyjson_tag!(val, TAG_ARRAY) { - let pyval = nonnull!(ffi!(PyList_New(unsafe_yyjson_get_len(val) as isize))); - append_to_list!(dptr, pyval.as_ptr()); + let pyval = ffi!(PyList_New(unsafe_yyjson_get_len(val) as isize)); + append_to_list!(dptr, pyval); if unsafe_yyjson_get_len(val) > 0 { - populate_yy_array(pyval.as_ptr(), val); + populate_yy_array(pyval, val); } } else { - let pyval = nonnull!(ffi!(_PyDict_NewPresized( - unsafe_yyjson_get_len(val) as isize - ))); - append_to_list!(dptr, pyval.as_ptr()); + let pyval = ffi!(_PyDict_NewPresized(unsafe_yyjson_get_len(val) as isize)); + append_to_list!(dptr, pyval); if unsafe_yyjson_get_len(val) > 0 { - populate_yy_object(pyval.as_ptr(), val); + populate_yy_object(pyval, val); } } } else { @@ -246,30 +244,32 @@ fn populate_yy_object(dict: *mut pyo3_ffi::PyObject, elem: *mut yyjson_val) { let mut next_key = unsafe_yyjson_get_first(elem); let mut next_val = next_key.add(1); for _ in 0..len { - let key = next_key; let val = next_val; - let key_str = str_from_slice!((*key).uni.str_ as *const u8, unsafe_yyjson_get_len(key)); - let pykey = get_unicode_key(key_str); + let pykey = { + let key_str = str_from_slice!( + (*next_key).uni.str_ as *const u8, + unsafe_yyjson_get_len(next_key) + ); + get_unicode_key(key_str) + }; if unlikely!(unsafe_yyjson_is_ctn(val)) { next_key = unsafe_yyjson_get_next_container(val); next_val = next_key.add(1); if is_yyjson_tag!(val, TAG_ARRAY) { - let pyval = nonnull!(ffi!(PyList_New(unsafe_yyjson_get_len(val) as isize))); - add_to_dict!(dict, pykey, pyval.as_ptr()); + let pyval = ffi!(PyList_New(unsafe_yyjson_get_len(val) as isize)); + add_to_dict!(dict, pykey, pyval); reverse_pydict_incref!(pykey); - reverse_pydict_incref!(pyval.as_ptr()); + reverse_pydict_incref!(pyval); if unsafe_yyjson_get_len(val) > 0 { - populate_yy_array(pyval.as_ptr(), val); + populate_yy_array(pyval, val); } } else { - let pyval = nonnull!(ffi!(_PyDict_NewPresized( - unsafe_yyjson_get_len(val) as isize - ))); - add_to_dict!(dict, pykey, pyval.as_ptr()); + let pyval = ffi!(_PyDict_NewPresized(unsafe_yyjson_get_len(val) as isize)); + add_to_dict!(dict, pykey, pyval); reverse_pydict_incref!(pykey); - reverse_pydict_incref!(pyval.as_ptr()); + reverse_pydict_incref!(pyval); if unsafe_yyjson_get_len(val) > 0 { - populate_yy_object(pyval.as_ptr(), val); + populate_yy_object(pyval, val); } } } else { diff --git a/src/lib.rs b/src/lib.rs index 3815fa74..0ae348ee 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -343,7 +343,7 @@ pub unsafe extern "C" fn dumps( if num_args & 3 == 3 { optsptr = Some(NonNull::new_unchecked(*args.offset(2))); } - if !kwnames.is_null() { + if unlikely!(!kwnames.is_null()) { for i in 0..=Py_SIZE(kwnames).saturating_sub(1) { let arg = PyTuple_GET_ITEM(kwnames, i as Py_ssize_t); if arg == typeref::DEFAULT { @@ -367,15 +367,15 @@ pub unsafe extern "C" fn dumps( } let mut optsbits: i32 = 0; - if let Some(opts) = optsptr { - if opts.as_ptr() == typeref::NONE { - } else if (*opts.as_ptr()).ob_type != typeref::INT_TYPE { - return raise_dumps_exception_fixed("Invalid opts"); - } else { + if unlikely!(optsptr.is_some()) { + let opts = optsptr.unwrap(); + if (*opts.as_ptr()).ob_type == typeref::INT_TYPE { optsbits = PyLong_AsLong(optsptr.unwrap().as_ptr()) as i32; - if !(0..=opt::MAX_OPT).contains(&optsbits) { + if unlikely!(!(0..=opt::MAX_OPT).contains(&optsbits)) { return raise_dumps_exception_fixed("Invalid opts"); } + } else if unlikely!(opts.as_ptr() != typeref::NONE) { + return raise_dumps_exception_fixed("Invalid opts"); } } diff --git a/src/serialize/per_type/dict.rs b/src/serialize/per_type/dict.rs index 6d185ac5..f8bcfec8 100644 --- a/src/serialize/per_type/dict.rs +++ b/src/serialize/per_type/dict.rs @@ -29,7 +29,7 @@ impl ZeroDictSerializer { } impl Serialize for ZeroDictSerializer { - #[inline(never)] + #[inline(always)] fn serialize(&self, serializer: S) -> Result where S: Serializer, diff --git a/src/serialize/per_type/float.rs b/src/serialize/per_type/float.rs index 24d052c9..68f1de92 100644 --- a/src/serialize/per_type/float.rs +++ b/src/serialize/per_type/float.rs @@ -14,7 +14,7 @@ impl FloatSerializer { } impl Serialize for FloatSerializer { - #[inline(never)] + #[inline(always)] fn serialize(&self, serializer: S) -> Result where S: Serializer, diff --git a/src/serialize/writer/json.r b/src/serialize/writer/json.r new file mode 100644 index 00000000..e69de29b diff --git a/src/serialize/writer/json.rs b/src/serialize/writer/json.rs index bcfce2a5..0c7a4257 100644 --- a/src/serialize/writer/json.rs +++ b/src/serialize/writer/json.rs @@ -134,7 +134,7 @@ where .map_err(Error::io) } } - #[inline] + #[inline(never)] fn serialize_f64(self, value: f64) -> Result<()> { if unlikely!(value.is_infinite() || value.is_nan()) { self.serialize_unit() @@ -155,8 +155,9 @@ where Ok(()) } + #[inline(always)] fn serialize_bytes(self, value: &[u8]) -> Result<()> { - self.writer.reserve(value.len()); + self.writer.reserve(value.len() + 32); unsafe { self.writer.write_reserved_fragment(value).unwrap() }; Ok(()) }