head: use std::io::copy() with TakeLines reader - #2712
Conversation
|
Impressive gain, could you please document your bench like in src/uu/cut/BENCHMARKING.md ? |
|
Yes, I will add a document. |
d33eeaa to
524d206
Compare
|
I have added the BENCHMARKING.md document and rebased on |
There was a problem hiding this comment.
AFAIU, this is line buffered whereas GNU head buffers up to 8 kB of output. It might be helpful to wrap stdout within a BufWriter to achieve a similar effect. This could especially beneficial insofar io::copy has a specialisation for BufWriter.
There was a problem hiding this comment.
I also noticed that for splitting lines, GNU wc actually uses a larger 16 kB buffer when AVX support is used. Since the memchr crate will transparently use the widest vectors available and supported, it would probably make sense to create the above writer with a 16 kB buffer (which should determine the buffer size passed to the Read implementation).
There was a problem hiding this comment.
Okay, I can add that.
Concerning the
warnings issued by Hyperfine, the Wikipedia data dumps, e.g. https://dumps.wikimedia.org/wikidatawiki/latest/wikidatawiki-latest-pages-logging.xml.gz, provide text files with on the order of 100M lines which could make for a larger and easier to measure test case? |
Okay, I can run that test and add information on how to run it to the BENCHMARKING.md file. |
|
Here is the result on the XML file you suggested: I'll add Edit: The XML file contains about 130 million lines, and in this test I am reading the first one million lines. |
|
Here's what I saw after wrapping the |
|
Thank you for going the extra mile! I find it notable that the difference to GNU head is mostly in user time, not in system time. I suspect this is due to GNU head not using SIMD to detect newlines AFAICS. |
Replace the custom `split::walk_lines()` function with a call to `std::io::copy()`, using a new `TakeLines` reader as the source and `stdout` as the destination. The `TakeLines` reader is an adaptor that scans the bytes being read for line ending characters and stops the reading after a given number of lines has been read (similar to the `std::io::Take` adaptor). This change * makes the `read_n_lines()` function more concise, * allows it to mirror the implementation of `read_n_bytes()`, * increases the speed of `head -n NUM`.
76ca123 to
858b0a9
Compare
|
I rebased and squashed the commits |
Replace the custom
split::walk_lines()function with a call tostd::io::copy(), using a newTakeLinesreader as the source andstdoutas the destination. TheTakeLinesreader is an adaptor thatscans the bytes being read for line ending characters and stops the
reading after a given number of lines has been read (similar to the
std::io::Takeadaptor).This change
read_n_lines()function more concise,read_n_bytes(),head -n NUM.Here's my speed benchmark. The file
shakespeare.txtcontains about 170,000 lines, each less than 100 characters, comprising the collected works of Shakespeare.headis the GNU version,head-masteris the version from the currentmasterbranch (11ca4be), andhead-readeris the version from this branch. We read the first 100,000 lines from the file with each version ofhead.