Skip to content

Commit

Permalink
perf: Refactor underlying buffers for branchless access (#229)
Browse files Browse the repository at this point in the history
* branch start, reimplement Repr for branchless string accesses, smaller code, faster in general

* update fuzzing framework

* get rid of our own asserts module, fix clippy

* more clippy and fmt fixes

* fix miri, ignore quickcheck when running miri

* re-enable tests in capacity.rs

* fix issue in test for 32-bit

* amortize growth for HeapBuffer

* cargo fmt

* respond to GitHub feedback, benchmark against compact_str v0.6

* delete the old repr

* cargo fmt and stop using saturating_div

* make the repr module private

* workflow cleanup

* cleanup comments and workflows
  • Loading branch information
ParkMyCar committed Jan 16, 2023
1 parent a510515 commit c95edd5
Show file tree
Hide file tree
Showing 25 changed files with 1,466 additions and 2,156 deletions.
19 changes: 0 additions & 19 deletions .github/workflows/cross_platform.yml
Original file line number Diff line number Diff line change
Expand Up @@ -86,25 +86,6 @@ jobs:
command: miri
args: test --all-features --manifest-path=compact_str/Cargo.toml

# TODO(parkmycar) re-enable ARMv7 builds
# linux_arm7:
# name: Linux ARMv7
# runs-on: [self-hosted, linux, ARM]
# steps:
# - uses: actions/checkout@v2
# name: Checkout Repo
# - uses: actions-rs/toolchain@v1
# name: Install Rust
# with:
# profile: minimal
# toolchain: nightly
# override: true
# - uses: actions-rs/cargo@v1
# name: cargo test
# with:
# command: test
# args: --release --all-features --manifest-path=compact_str/Cargo.toml

linux_32bit:
name: Linux 32-bit
runs-on: ubuntu-latest
Expand Down
92 changes: 0 additions & 92 deletions .github/workflows/fuzz.yml
Original file line number Diff line number Diff line change
Expand Up @@ -70,43 +70,6 @@ jobs:
with:
update_existing: true

# TODO(parkmycar): Fails in CI with "error: failed to run LLVM passes: unknown pass name 'sancov'"
# honggfuzz_x86_64:
# name: honggfuzz [x86_64]
# runs-on: ubuntu-latest
# env:
# HFUZZ_BUILD_ARGS: "--features=honggfuzz"
# HFUZZ_RUN_ARGS: "--run_time 120 --exit_upon_crash"
# RUSTFLAGS: "-Znew-llvm-pass-manager=no"
# steps:
# - uses: actions/checkout@v2
# name: Checkout compact_str
# - uses: actions-rs/toolchain@v1
# name: Install Rust
# with:
# toolchain: nightly
# override: true
# - name: Install Deps
# run: sudo apt-get install --no-install-recommends binutils-dev libunwind8-dev
# - uses: actions-rs/cargo@v1
# name: Install honggfuzz
# with:
# command: install
# toolchain: nightly
# args: honggfuzz
# - uses: actions-rs/cargo@v1
# name: Fuzz!
# with:
# command: hfuzz
# args: run honggfuzz
# - name: File Issue (if failure found)
# if: failure()
# uses: JasonEtco/create-an-issue@v2.6
# env:
# GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# with:
# update_existing: true

afl_x86_64:
name: AFL++ [x86_64]
runs-on: ubuntu-latest
Expand Down Expand Up @@ -177,58 +140,3 @@ jobs:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
update_existing: true

# TODO(parkmycar): ARMv7 runner is no longer online
# afl_armv7:
# name: AFL++ [ARMv7]
# runs-on: [self-hosted, linux, ARM]
# steps:
# - uses: actions/checkout@v2
# name: Checkout compact_str
# - uses: actions-rs/toolchain@v1
# name: Install Rust
# with:
# profile: minimal
# toolchain: nightly-2022-07-01
# override: true
# - name: Install cargo-afl
# run: |
# cargo +nightly install afl --force
# - name: AFL++ Build
# run: |
# cd fuzz
# cargo +nightly afl build --bin afl --release --features=afl
# - name: Set Fuzz Time
# run: |
# if [[ "${{github.event_name}}" == "push" || "${{github.event_name}}" == "pull_request" ]]; then
# echo "fuzz_time=120" >> $GITHUB_ENV
# else
# echo "fuzz_time=1800" >> $GITHUB_ENV
# fi
# echo "${{ env.fuzz_time }}"
# - name: Fuzz!
# run: |
# cd fuzz
# mkdir afl/out
# cargo +nightly afl fuzz -i afl/in -o afl/out -D -V ${{ env.fuzz_time }} ../target/release/afl
# - name: Check for Crashes
# run: |
# if [ "$(ls -1q ./fuzz/afl/out/default/crashes/ | wc -l)" -ne 0 ]; then exit 1; fi
# # AFL generates filenames with ":", which upload-artifact fails on, so we need to "detox" them
# - name: Sanitize Crash Filenames (if present)
# if: failure()
# run: |
# detox -r -v ./fuzz/afl/out/default/crashes/
# - name: Upload Crashes (if present)
# if: failure()
# uses: actions/upload-artifact@v2
# with:
# name: Upload Crashes
# path: ./fuzz/afl/out/default/crashes/
# - name: File Issue (if failure found)
# if: failure()
# uses: JasonEtco/create-an-issue@v2.6
# env:
# GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# with:
# update_existing: true
1 change: 1 addition & 0 deletions bench/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ edition = "2021"

[dependencies]
compact_str = { path = "../compact_str" }
compact_str_6 = { package = "compact_str", version = "0.6" }
criterion = { version = "0.3", features = ["html_reports"] }
iai = "0.1.1"
smartstring = "1.0.1"
Expand Down
67 changes: 65 additions & 2 deletions bench/benches/compact_str.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use compact_str::{
CompactString,
ToCompactString,
};
use compact_str_6::CompactString as CompactString6;
use criterion::{
criterion_group,
criterion_main,
Expand Down Expand Up @@ -31,7 +32,7 @@ fn bench_new(c: &mut Criterion) {
c.bench_with_input(
BenchmarkId::new("CompactString::new", "59 chars"),
&"I am a very long string that will get allocated on the heap",
|b, word| b.iter(|| CompactString::new(word)),
|b, &word| b.iter(|| CompactString::from(word)),
);

c.bench_with_input(
Expand Down Expand Up @@ -91,5 +92,67 @@ fn bench_to_compact_string(c: &mut Criterion) {
);
}

fn bench_repr_creation(c: &mut Criterion) {
let mut group = c.benchmark_group("Creation");

let words: Vec<String> = vec![0, 11, 12, 22, 23, 24, 25, 50]
.into_iter()
.map(|len| (0..len).into_iter().map(|_| 'a').collect())
.collect();

for word in words {
group.bench_with_input(
BenchmarkId::new("CompactString6", word.len()),
&word,
|b, w| b.iter(|| CompactString6::new(w)),
);

group.bench_with_input(
BenchmarkId::new("CompactString", word.len()),
&word,
|b, w| b.iter(|| CompactString::new(w)),
);

group.bench_with_input(
BenchmarkId::new("std::String", word.len()),
&word,
|b, w| b.iter(|| String::from(w)),
);
}
}

fn bench_repr_access(c: &mut Criterion) {
let mut group = c.benchmark_group("Access");

let words: Vec<String> = vec![0, 11, 12, 23, 24, 50]
.into_iter()
.map(|len| (0..len).into_iter().map(|_| 'a').collect())
.collect();

for word in words {
let compact = CompactString6::new(&word);
group.bench_with_input(
BenchmarkId::new("CompactString6", compact.len()),
&compact,
|b, c| b.iter(|| c.as_str()),
);

let compact = CompactString::new(&word);
group.bench_with_input(
BenchmarkId::new("CompactString", compact.len()),
&compact,
|b, c| b.iter(|| c.as_str()),
);

let std_str = String::from(&word);
group.bench_with_input(
BenchmarkId::new("String", std_str.len()),
&std_str,
|b, s| b.iter(|| s.as_str()),
);
}
}
criterion_group!(repr_benches, bench_repr_creation, bench_repr_access);

criterion_group!(compact_str, bench_new, bench_to_compact_string);
criterion_main!(compact_str);
criterion_main!(compact_str, repr_benches);
4 changes: 4 additions & 0 deletions compact_str/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,16 +21,20 @@ rkyv = { version = "0.7", optional = true }
serde = { version = "1", optional = true }

castaway = "0.2.1"
cfg-if = "1"
itoa = "1"
ryu = "1"
static_assertions = "1"

[dev-dependencies]
cfg-if = "1"
proptest = { version = "1", default-features = false, features = ["std"] }
quickcheck = "1"
quickcheck_macros = "1"
rayon = "1"
serde = { version = "1", features = ["derive"] }
serde_json = "1"
test-case = "2"
test-strategy = "0.2"

[package.metadata.docs.rs]
Expand Down
42 changes: 0 additions & 42 deletions compact_str/src/asserts.rs

This file was deleted.

4 changes: 2 additions & 2 deletions compact_str/src/features/bytes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ impl CompactString {
/// ```
#[cfg_attr(docsrs, doc(cfg(feature = "bytes")))]
pub fn from_utf8_buf<B: Buf>(buf: &mut B) -> Result<Self, Utf8Error> {
Repr::from_utf8_buf(buf).map(|repr| CompactString { repr })
Repr::from_utf8_buf(buf).map(CompactString)
}

/// Converts a buffer of bytes to a [`CompactString`], without checking that the provided buffer
Expand All @@ -63,7 +63,7 @@ impl CompactString {
#[cfg_attr(docsrs, doc(cfg(feature = "bytes")))]
pub unsafe fn from_utf8_buf_unchecked<B: Buf>(buf: &mut B) -> Self {
let repr = Repr::from_utf8_buf_unchecked(buf);
CompactString { repr }
CompactString(repr)
}
}

Expand Down
Loading

0 comments on commit c95edd5

Please sign in to comment.