cut: support multibyte characters in non-UTF-8 locales - #12635
Conversation
36730e9 to
1c826d5
Compare
Merging this PR will degrade performance by 4.67%
|
| Mode | Benchmark | BASE |
HEAD |
Efficiency | |
|---|---|---|---|---|---|
| ❌ | Simulation | cut_characters |
17.1 ms | 23.3 ms | -26.55% |
| ❌ | Simulation | du_deep_tree[(100, 3)] |
2.3 ms | 2.3 ms | -3.67% |
| ⚡ | Simulation | df_with_path |
699.4 µs | 571.2 µs | +22.44% |
Tip
Investigate this regression by commenting @codspeedbot fix this regression on this PR, or directly use the CodSpeed MCP with your agent.
Comparing sylvestre:fix-cut-mb-non-utf8 (e34f4db) with main (a0cb024)
Footnotes
-
46 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports. ↩
|
GNU testsuite comparison: |
1c826d5 to
43545e3
Compare
43545e3 to
b468c15
Compare
|
@codspeedbot fix this regression |
I reproduced the What caused itWith a UTF-8
What changed
CorrectnessOutput is byte-for-byte identical to the current implementation:
Remaining gap
One note: Summary
Was this response helpful? React with 👍 or 👎 to help us improve CodSpeedBot. |
9b73daf to
b7bb157
Compare
a9a6ca1 to
9980d47
Compare
4780f68 to
a3d2145
Compare
wasmtime's argument marshaling requires valid UTF-8, so a raw non-UTF-8 byte delimiter can't be passed through to the WASI binary the way it can natively on Linux.
The character path walked the line one character at a time through the encoding decoder and looked the locale encoding up per character, which dominated the per-line cost on mixed ASCII and multi-byte text. Take runs of ASCII bytes a machine word at a time. They are single-byte characters in every encoding handled here, so byte offset and character position move together across a run, and only the bytes above 0x7F need the decoder. Bundle the parts that are fixed for the whole run -- the ranges, the output delimiter and whether it was given, the position mode and the encoding -- into a CharCut built once by cut_chars, and make the line body a method on it, so the per-line call passes one pointer rather than seven arguments. In uucore, MbEncoding becomes the public Encoding and locale_encoding() returns it by value, so a caller decoding many characters resolves the locale once and keeps it in a register instead of reaching through a OnceLock per character. is_multibyte_locale() had no callers left. Instruction counts against the parent commit (cachegrind, LC_ALL=C.UTF-8): -c 5-30, 100k mixed short lines 30.44M -> 28.95M -c 20-70, 20k long multibyte lines 44.38M -> 41.02M That is roughly 1.1x in wall clock on both shapes; the machine was too loaded to quote a tighter figure. The single-byte path (LC_ALL=C) and field mode are unchanged. Selecting a range of characters still costs more than the same range of bytes, and always will: -c used to be an alias for -b, and characters have to be decoded to be counted. Tests cover advance directly -- character counting for -c, byte counting for -b -n, and the word boundary crossings -- plus a cut -c case over mixed ASCII and multi-byte lines.
a3d2145 to
e34f4db
Compare
No description provided.