Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Extend io::copy buffer reuse to BufReader too #112330

Merged
merged 1 commit into from
Jun 17, 2023

Conversation

the8472
Copy link
Member

@the8472 the8472 commented Jun 5, 2023

previously it was only able to use BufWriter. This was due to a limitation in the BufReader generics that prevented specialization. This change works around the issue by using BufReader where Self: Read instead of BufReader<I> where I: Read. This limits our options, e.g. we can't access the inner reader, but it happens to work out if we rely on some implementation details.

Copying 1MiB from /dev/zero to /dev/null through a 256KiB BufReader yields following improvements

OLD:
    io::copy::tests::bench_copy_buf_reader  51.44µs/iter +/- 703.00ns
NEW:
    io::copy::tests::bench_copy_buf_reader  18.55µs/iter +/- 237.00ns

Previously this would read 256KiB into the reader but then copy 8KiB chunks to the writer through an additional intermediate buffer inside io::copy. Since those devices don't do much work most of the speedup should come from fewer syscalls and avoided memcopies.

The b3sum crate notes that the default buffer size in io::copy is too small. With this optimization they could achieve the desired performance by wrapping the reader in a BufReader instead of handrolling it.

Currently the optimization doesn't apply to things like StdinLock, but this can be addressed with an additional AsMutBufReader specialization trait.

@rustbot
Copy link
Collaborator

rustbot commented Jun 5, 2023

r? @thomcc

(rustbot has picked a reviewer for you, use r? to override)

@rustbot rustbot added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. T-libs Relevant to the library team, which will review and decide on the PR/issue. labels Jun 5, 2023
@rust-log-analyzer

This comment has been minimized.

@the8472 the8472 force-pushed the use-buf-reader-buffer branch from 81c1d01 to 9c20c89 Compare June 6, 2023 19:39
Comment on lines +123 to +128
// Hack: this relies on `impl Read for BufReader` always calling fill_buf
// if the buffer is empty, even for empty slices.
// It can't be called directly here since specialization prevents us
// from adding I: Read
match self.read(&mut []) {
Copy link
Member Author

Choose a reason for hiding this comment

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

Ah, I could maybe get everything I want by adding a bunch of hidden helper functions to the Read trait and only implement them on BufReader (and provide stub default impls for everything else) but imo that would be an even bigger hack than this. And that would require combining associated type defaults with specialization which is uncharted territory.

Copy link
Member

@thomcc thomcc Jun 17, 2023

Choose a reason for hiding this comment

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

I don't think this is worth doing right now.

@bors
Copy link
Contributor

bors commented Jun 17, 2023

☔ The latest upstream changes (presumably #112716) made this pull request unmergeable. Please resolve the merge conflicts.

@thomcc
Copy link
Member

thomcc commented Jun 17, 2023

Looks good to me. r=me after the rebase.

previously it was only able to use BufWriter. This was due to a limitation in the
BufReader generics that prevented specialization. This change works around the issue
by using `where Self: Read` instead of `where I: Read`. This limits our options, e.g.
we can't access BufRead methods, but it happens to work out if we rely on some
implementation details.
@the8472 the8472 force-pushed the use-buf-reader-buffer branch from 9c20c89 to 3738785 Compare June 17, 2023 09:07
@the8472
Copy link
Member Author

the8472 commented Jun 17, 2023

@bors r=thomcc

@bors
Copy link
Contributor

bors commented Jun 17, 2023

📌 Commit 3738785 has been approved by thomcc

It is now in the queue for this repository.

@bors bors added S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Jun 17, 2023
@bors
Copy link
Contributor

bors commented Jun 17, 2023

⌛ Testing commit 3738785 with merge 7513407...

@bors
Copy link
Contributor

bors commented Jun 17, 2023

☀️ Test successful - checks-actions
Approved by: thomcc
Pushing 7513407 to master...

@bors bors added the merged-by-bors This PR was explicitly merged by bors. label Jun 17, 2023
@bors bors merged commit 7513407 into rust-lang:master Jun 17, 2023
@rustbot rustbot added this to the 1.72.0 milestone Jun 17, 2023
@rust-timer
Copy link
Collaborator

Finished benchmarking commit (7513407): comparison URL.

Overall result: ✅ improvements - no action needed

@rustbot label: -perf-regression

Instruction count

This is a highly reliable metric that was used to determine the overall result at the top of this comment.

mean range count
Regressions ❌
(primary)
- - 0
Regressions ❌
(secondary)
- - 0
Improvements ✅
(primary)
-1.9% [-1.9%, -1.9%] 1
Improvements ✅
(secondary)
- - 0
All ❌✅ (primary) -1.9% [-1.9%, -1.9%] 1

Max RSS (memory usage)

Results

This is a less reliable metric that may be of interest but was not used to determine the overall result at the top of this comment.

mean range count
Regressions ❌
(primary)
- - 0
Regressions ❌
(secondary)
3.1% [3.1%, 3.1%] 1
Improvements ✅
(primary)
- - 0
Improvements ✅
(secondary)
- - 0
All ❌✅ (primary) - - 0

Cycles

Results

This is a less reliable metric that may be of interest but was not used to determine the overall result at the top of this comment.

mean range count
Regressions ❌
(primary)
- - 0
Regressions ❌
(secondary)
2.8% [2.8%, 2.8%] 1
Improvements ✅
(primary)
- - 0
Improvements ✅
(secondary)
- - 0
All ❌✅ (primary) - - 0

Binary size

Results

This is a less reliable metric that may be of interest but was not used to determine the overall result at the top of this comment.

mean range count
Regressions ❌
(primary)
0.1% [0.1%, 0.1%] 4
Regressions ❌
(secondary)
- - 0
Improvements ✅
(primary)
-0.1% [-0.1%, -0.1%] 1
Improvements ✅
(secondary)
- - 0
All ❌✅ (primary) 0.0% [-0.1%, 0.1%] 5

Bootstrap: 656.303s -> 655.196s (-0.17%)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
merged-by-bors This PR was explicitly merged by bors. S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. T-libs Relevant to the library team, which will review and decide on the PR/issue.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants