cut: optimizations - #2111
Merged
Merged
Conversation
* Use buffered stdout to reduce write sys calls. This simple change yielded the biggest performace gain. * Use `for_byte_record_with_terminator` from the `bstr` crate. This is to minimize the per line copying needed by `BufReader::read_until`. The `cut_fields` and `cut_fields_delimiter` functions used `read_until` to iterate over lines. That required copying each input line to the line buffer. With `for_byte_record_with_terminator` copying is minimized as it calls our closure with a reference to BufReader's buffer most of the time. It needs to copy (internally) only to process any incomplete lines at the end of the buffer. * Re-write `Searcher` to use `memchr`. Switch from the naive implementation to one that uses `memchr`. * Rewrite `cut_bytes` almost entirely. This was already well optimized. The performance gain in this case is not from avoiding copying. In fact, it needed zero copying whereas new implementation introduces some copying similar to `cut_fields` described above. But the occassional copying cost is more than offset by the use of the very fast `memchr` inside `for_byte_record_with_terminator`. This change also simplifies the code significantly. Removed the `buffer` module.
Contributor
|
Well done for the huge wins! Could you please create a benchmark files like: |
and minor refactoring
Contributor
Author
|
Added benchmarking doc. I think it would be more accessible if instead of adding all details for each utility separately common details can be extracted and kept at project root level. |
Contributor
|
Great doc, thanks |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Use buffered stdout to reduce write sys calls.
This simple change yielded the biggest performance gain.
Use
for_byte_record_with_terminatorfrom thebstrcrate.This is to minimize the per line copying needed by
BufReader::read_until. Thecut_fieldsandcut_fields_delimiterfunctions usedread_untilto iterate over lines. That required copying each input line to the line buffer. Withfor_byte_record_with_terminatorcopying is minimized as it calls our closure with a reference to BufReader's buffer most of the time. It needs to copy (internally) only to process any incomplete lines at the end of the buffer.Re-write
Searcherto usememchr.Switch from the naive implementation to one that uses
memchr.Rewrite
cut_bytesalmost entirely.This was already well optimized. The performance gain in this case is not from avoiding copying. In fact, it needed zero copying whereas new implementation introduces some copying similar to
cut_fieldsdescribed above. But the occasional copying cost is more than offset by the use of the very fastmemchrinsidefor_byte_record_with_terminator. This change also simplifies the code significantly. Removed thebuffermodule.Benchmarks
$ hyperfine -w3 "./target/release/cut -c2-4,8 test-large.txt" "cut -c2-4,8 test-large.txt"Before
After
$ hyperfine -w3 "./target/release/cut -f2-4,8 -d' ' test-large.txt" "cut -f2-4,8 -d' ' test-large.txt"Before
After
hyperfine -w3 "./target/release/cut -f2-4,8 -d' ' --output-delimiter=- test-large.txt" "cut -f2-4,8 -d' ' --output-delimiter=- test-large.txt"Before
After