Skip to content

perf: remove unnecessary clones in a hot path - #3580

Merged
ellie merged 2 commits into
atuinsh:mainfrom
markovejnovic:perf/avoid-clone
Jul 8, 2026
Merged

perf: remove unnecessary clones in a hot path#3580
ellie merged 2 commits into
atuinsh:mainfrom
markovejnovic:perf/avoid-clone

Conversation

@markovejnovic

@markovejnovic markovejnovic commented Jul 7, 2026

Copy link
Copy Markdown
Member

This PR adds two commits:

  • The first commit adds another benchmark to crates/atuin-client.
  • The second commit adds a tiny performance optimization to reorder_fuzzy which avoids an unnecessary clone.

Hand-rolled PR (I hated claude's output).

Benchmarking

I did a couple more changes to benchmarking.

  • In perf: add synchronous(Normal) + optimize_on_close to record store SQLite #3577, I naively used rand, but I realized this is pretty bad for benchmark reproducibility, so I created a new BenchCtx utility which is a more stable random number generator (and a dumping ground for similar things where we want stability, another example in this PR being the "now" timestamp).
  • I reduced the minimum time per iteration of a benchmark to be 1s from 5s.

Optimization

Pretty bog-standard removal of a potentially large clone.

Results

Tested on an M4 mac.

Before

benchmarks                 fastest       │ slowest       │ median        │ mean          │ samples │ iters
├─ ordering                              │               │               │               │         │
│  ╰─ reorder_fuzzy_bench                │               │               │               │         │
│     ├─ 10                2.124 µs      │ 31.91 µs      │ 2.499 µs      │ 2.593 µs      │ 47246   │ 47246
│     ├─ 200               43.62 µs      │ 81.62 µs      │ 49.2 µs       │ 49.58 µs      │ 2488    │ 2488
│     ╰─ 1000              230.2 µs      │ 305.4 µs      │ 252.4 µs      │ 256.5 µs      │ 495     │ 495
╰─ record                                │               │               │               │         │
   ╰─ sqlite_store                       │               │               │               │         │
      ╰─ push_batch                      │               │               │               │         │
         ├─ 1              99.45 µs      │ 1.326 ms      │ 187.6 µs      │ 189.9 µs      │ 945     │ 945
         ├─ 10             155.5 µs      │ 5.674 ms      │ 173.4 µs      │ 191.3 µs      │ 957     │ 957
         ╰─ 100            582.2 µs      │ 6.717 ms      │ 659.4 µs      │ 697.9 µs      │ 568     │ 568

After

benchmarks                 fastest       │ slowest       │ median        │ mean          │ samples │ iters
├─ ordering                              │               │               │               │         │
│  ╰─ reorder_fuzzy_bench                │               │               │               │         │
│     ├─ 10                1.249 µs      │ 30.54 µs      │ 1.582 µs      │ 1.602 µs      │ 49473   │ 49473
│     ├─ 200               25.62 µs      │ 59.7 µs       │ 29.16 µs      │ 29.55 µs      │ 2616    │ 2616
│     ╰─ 1000              135.4 µs      │ 190.3 µs      │ 152.9 µs      │ 155.6 µs      │ 519     │ 519
╰─ record                                │               │               │               │         │
   ╰─ sqlite_store                       │               │               │               │         │
      ╰─ push_batch                      │               │               │               │         │
         ├─ 1              97.95 µs      │ 1.344 ms      │ 178.8 µs      │ 179.3 µs      │ 970     │ 970
         ├─ 10             155.9 µs      │ 4.397 ms      │ 171.3 µs      │ 181.5 µs      │ 994     │ 994
         ╰─ 100            578.9 µs      │ 8.866 ms      │ 653.4 µs      │ 711.1 µs      │ 565     │ 565

Comparison

  ┌────────────────────┬─────────────────┬────────────────┬────────────────────┐
  │     Benchmark      │ Before (median) │ After (median) │      Speedup       │
  ├────────────────────┼─────────────────┼────────────────┼────────────────────┤
  │ reorder_fuzzy 10   │ 2.499 µs        │ 1.582 µs       │ 1.58× (37% faster) │
  ├────────────────────┼─────────────────┼────────────────┼────────────────────┤
  │ reorder_fuzzy 200  │ 49.20 µs        │ 29.16 µs       │ 1.69× (41% faster) │
  ├────────────────────┼─────────────────┼────────────────┼────────────────────┤
  │ reorder_fuzzy 1000 │ 252.4 µs        │ 152.9 µs       │ 1.65× (39% faster) │
  └────────────────────┼─────────────────┼────────────────┼────────────────────┘

Checks

  • I am happy for maintainers to push small adjustments to this PR, to speed up the review cycle
  • I have checked that there are no existing pull requests for the same thing

@github-actions

github-actions Bot commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

Fossier: Manual Review Requested

@markovejnovic is a new contributor. A maintainer should review this PR before merging.

Score Breakdown

Total Score: 51.3/100 | Confidence: 100% | Outcome: REVIEW

Signal Value Score Weight
account_age 3914 1.00 0.09
public_repos 85 1.00 0.05
contribution_history 96 0.48 0.05
follower_ratio 1.39 0.70 0.05
bot_signals False 0.50 0.04
open_prs_elsewhere 34 0.00 0.09
closed_prs_elsewhere 49 0.00 0.10
merged_prs_elsewhere 304 1.00 0.08
prior_interaction 0 0.00 0.08
activity_velocity 5 0.17 0.08
pr_content ... 1.00 0.08
commit_email marko@harmont.dev 0.80 0.04
pr_description ... 0.80 0.05
repo_stars 30469 0.30 0.04
org_membership 0 0.20 0.03
commit_verification ... 0.65 0.04
contributor_stars 25 0.50 0.04

@markovejnovic
markovejnovic force-pushed the perf/avoid-clone branch 6 times, most recently from 7f06ce7 to 627e69d Compare July 8, 2026 00:39
Unify all atuin-client benchmarks under a single harness with shared
deterministic utilities (seeded StdRng, fixed timestamp). Add
reorder_fuzzy (100/200/500/1k/10k) benchmarks. Restructure existing
record store benchmarks to use shared _util modules.
reorder() received Vec<A> by value (already owned) but cloned it
before sorting. Remove the clone, capture len before the mutable
sort borrow, and drop the Clone trait bound.
@markovejnovic
markovejnovic marked this pull request as ready for review July 8, 2026 00:46
@markovejnovic markovejnovic changed the title perf: remove unnecessary clones in hot paths perf: remove unnecessary clones in a hot path Jul 8, 2026
@greptile-apps

greptile-apps Bot commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

Removes an unnecessary Vec<History> clone in reorder_fuzzy by consuming res directly and pre-capturing len, yielding ~40% speedup on the hot interactive-search path. Accompanying commits restructure the benchmark suite under a single benchmarks.rs entry point with a new BenchCtx utility for seeded, reproducible inputs.

  • ordering.rs: Drops A: Clone bound; moves res into r instead of cloning, captures len before sorting — semantically identical, no behavioural change.
  • Benchmarks: New ordering bench at sizes 10/200/1000; BenchCtx replaces ad-hoc rand::thread_rng() calls for determinism; min_time reduced to 1s.

Confidence Score: 5/5

Safe to merge — the only production code change is a one-function refactor in ordering.rs with identical semantics and measurable gains.

The ordering change is mechanically correct: length is immutable across sort, so pre-capturing it is equivalent to the previous res.len() call. Benchmark restructuring is additive and isolated to dev-dependencies.

No files require special attention.

Important Files Changed

Filename Overview
crates/atuin-client/src/ordering.rs Removes unnecessary Vec clone by consuming res directly and pre-capturing len; semantically identical, measurably faster
crates/atuin-client/benches/_util/context.rs New BenchCtx with seeded RNG and fixed timestamp for reproducible benchmarks
crates/atuin-client/benches/ordering.rs New benchmark for reorder_fuzzy at sizes 10/200/1000
crates/atuin-client/benches/record/sqlite_store.rs Migrated to BenchCtx for deterministic RNG; min_time reduced to 1s
crates/atuin-client/Cargo.toml Added autobenches = false and renamed bench entry point from record_store to benchmarks

Reviews (1): Last reviewed commit: "perf: remove unnecessary Vec clone in fu..." | Re-trigger Greptile

@ellie ellie self-assigned this Jul 8, 2026

@ellie ellie left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice catch! one clone made a big difference. thank you!

@ellie
ellie merged commit 55a0438 into atuinsh:main Jul 8, 2026
26 checks passed
taylordotfish pushed a commit that referenced this pull request Jul 9, 2026
### Bug Fixes

- *(ai)* Dispatch skills registered in the slash command registry
([#3593](#3593))
- *(ci)* Fossier install in scan workflow
([#3485](#3485))
- *(i18n)* Fix typos in Russian localization
([#3575](#3575))
- *(nu)* Use `char -u 1b` for ESC in OSC 133 sequences
([#3530](#3530))
- *(nu)* Suppress error when `ATUIN_HISTORY_ID` is missing in
`pre_prompt` hook
([#3587](#3587))
- *(pi)* Observe tool events instead of registering a bash tool
([#3557](#3557))
- *(pty-proxy)* Set `$SHELL` to the spawned shell
([#3548](#3548))
- *(search)* Fix terminal clearing with latest Ratatui
([#3578](#3578))
- *(sync)* Skip records that fail to decrypt or decode instead of
failing the whole store
([#3569](#3569))
- Atuin hangs when attempting to spawn daemon from Ctrl+R invocation
([#3502](#3502))
- Capture session ID from stream headers rather than final event
([#3531](#3531))
- Doctor resiliency fo runknown platforms + openbsd warning
([#3551](#3551))
- Double input on arrow keys in AI setup prompt on Windows
([#3552](#3552))
- Exclude AI agent commands from zsh-autosuggestions
([#3567](#3567))
- Silence shellcheck SC2016 on literal `$all-user` author filter
- Respect `store_failed` when using daemon
([#3571](#3571))


### Documentation

- Highlight `Ctrl-r` keybinding on docs page
([#3489](#3489))
- Document store purge workflow
([#3544](#3544))
- Fix command example typo in documentation
([#3536](#3536))
- Make commented-out lines in `config.toml` match real defaults
([#3583](#3583))
- Add fish shell cleanup step to uninstall instructions
([#3582](#3582))


### Features

- *(doctor)* Add whether daemon is enabled to `doctor` output
([#3572](#3572))
- *(pty-proxy)* Add `--shell` flag to override the spawned shell
([#3327](#3327))
- Setup fossier to stop bot slop prs
([#3482](#3482))
- Capture command output + expose to new `atuin_output` tool
([#3510](#3510))
- Cache user contexts on load until `/reload`
([#3525](#3525))
- Create database integration tests for atuin-server
([#3514](#3514))
- Add `/model` slash command for changing models
([#3576](#3576))
- Add mcp server for history tools and expand search filters
([#3581](#3581))
- Add status bar with model and usage information
([#3591](#3591))


### Miscellaneous Tasks

- *(rustdoc)* Fix Rustdoc warnings
([#3585](#3585))
- *(warnings)* Fix compile warnings with latest dependencies
([#3586](#3586))
- Vouch for all existing contributors
([#3486](#3486))
- Update GitHub app token format
- Update to Rust 1.96.1
([#3568](#3568))
- Adopt `derive_more` to reduce boilerplate across the codebase
([#3573](#3573))


### Performance

- *(search)* Scan history by recency until N unique
([#3553](#3553))
- Add `synchronous(Normal)` + `optimize_on_close` to record store SQLite
([#3577](#3577))
- Remove unnecessary clones in a hot path
([#3580](#3580))


### Refactor

- Implement `From<sqlx::Error>` and clean up `fix_error`
([#3484](#3484))
- Pull `fn into_utc` into `atuin-server-database` crate
([#3487](#3487))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants