| ... | @@ -1000,6 +1000,18 @@ pub fn fill(r: *Reader, n: usize) Error!void { | ... | @@ -1000,6 +1000,18 @@ pub fn fill(r: *Reader, n: usize) Error!void { |
| 1000 | @branchHint(.likely); | 1000 | @branchHint(.likely); |
| 1001 | return; | 1001 | return; |
| 1002 | } | 1002 | } |
| | 1003 | return fillUnbuffered(r, n); |
| | 1004 | } |
| | 1005 | |
| | 1006 | /// This internal function is separated from `fill` to encourage optimizers to inline `fill`, hence |
| | 1007 | /// propagating its `@branchHint` to usage sites. If these functions are combined, `fill` is large |
| | 1008 | /// enough that LLVM is reluctant to inline it, forcing usages of APIs like `takeInt` to go through |
| | 1009 | /// an expensive runtime function call just to figure out that the data is, in fact, already in the |
| | 1010 | /// buffer. |
| | 1011 | /// |
| | 1012 | /// Missing this optimization can result in wall-clock time for the most affected benchmarks |
| | 1013 | /// increasing by a factor of 5 or more. |
| | 1014 | fn fillUnbuffered(r: *Reader, n: usize) Error!void { |
| 1003 | if (r.seek + n <= r.buffer.len) while (true) { | 1015 | if (r.seek + n <= r.buffer.len) while (true) { |
| 1004 | const end_cap = r.buffer[r.end..]; | 1016 | const end_cap = r.buffer[r.end..]; |
| 1005 | var writer: Writer = .fixed(end_cap); | 1017 | var writer: Writer = .fixed(end_cap); |