prelim code to test sys_read loop replace - #24521
Conversation
khwilliamson
commented
Jul 1, 2026
- This set of changes does not require a perldelta entry.
|
sorry, should be fixed now |
|
Just to be clear. I'm not saying this patch should be used instead of your proposal; I'm saying this should be used regardless of your proposal for those cases where yours would not kick in. The 12 where this kicks in was determined with just a bit of experimentation; it could be tweaked. I did try a quick and dirty check of changing it to 1, and the entire test suite seems to run 1% faster, but that could be noise. I ran a few trials, and the changed version never ran slower. |
|
sys_read() when it gets a partial character retries asking for just enough bytes to complete that character. Then it loops to ask for any remaining length the caller has asked for. The code could be simplified if it just always asked for the entire remaining desired length. @Leont, Is there as reason it works this way? |
|
I see that S_sv_gets_read_record() in sv.c has similar logic. I don't know if it gets long enough data to justify changing it. @tonycoz any opinions? |
I can't imagine any good reason for it working that way TBH. |
|
@khwilliamson, I hadn't seen your comments here until now. I've updated #24511 with the benchmark results from this PR. |
The partial-completion request and the main request stop on different conditions, so they can't be merged into one "ask for everything remaining" call. The main loop uses Requesting exactly the missing continuation bytes also keeps every read character-aligned, which is what makes the On seekable files this is redundant (you could over-read and rewind). It matters on pipes/sockets/ttys, where over-reading either blocks waiting for bytes the caller never asked for, or consumes bytes belonging to the next logical read that can't be pushed back. |
|
I'm not convinced that there isn't a way around these. Yes a partial can never be returned and hence has to have special case code that is byte-oriented,, but that doesn't mean we can't ask for more data from the read than just enough to complete the partial. I currently have some puzzlements.
For one this comment is the only place I found where the symbol |
|
@chansen I did get it to work to request more at a time than the rest of the continuation bytes for the partial character. This should cut down on the number of read calls significantly. If it is convenient, could you test the new version to see how much difference in time it makes |
Thanks, I'll benchmark it. First though, my sanity check caught a correctness regression with the new version: a plain ar.txt is a valid file of exactly 14308 code points, so this is dropping the last characters rather than looping to fulfil the count. corpus/ar.txt |
|
There seems to be an issue with the recent changes. I had to disable all sanity length checks because they were reporting incorrect lengths, and after that I encountered a segmentation fault (core dumped). That said, the numbers before the crash looked promising. |
|
Sorry. I was just finishing up fixes. This now passes the arabic test script. I think the changes will correct your other issues as well. |
|
Also 756c3a0 reproduces some of the cachegrind numbers that I got when I added the per-word looping in 2019 |
|
The throughput is higher than in the previous results. |
I rewrote this function in 2019 via 71d63d0 to do advancing per-word versus by character for long enough strings, thus saving significant amounts of execution time. FYI, at the bottom is a copy of some of the data presented in that commit message. The function traditionally did just a bit of syntax error checking of the parsed string. I chose to not add any, since the goal was speed, but also chose to not take any away, so that it would behave the same as always. That caused a tiny bit of slowdown than if checking were eliminated. This commit adds a new check at the beginning of the function in the form of an assert (active only in DEBUG builds) that the input string is positioned at the beginning of a character. This should have been a no-brainer, adding essentially nothing and verifying an important sanity criterium ------------------------- Data from 2019 commit Very long strings run an order of magnitude fewer instructions than blead. Here are worst case scenarios (7 bytes after word boundary). The values are in terms of percent, with blead (at the time) always set to 100. Numbers above 100 are good; below bad string length 10000000 characters; 1 bytes per character blead patch ------ ------- Ir 100.00 814.53 Dr 100.00 1069.58 Dw 100.00 3296.55 COND 100.00 1575.83 IND 100.00 100.00 string length 5000000 characters; 2 bytes per character blead patch ------ ------- Ir 100.00 408.86 Dr 100.00 536.32 Dw 100.00 1698.31 COND 100.00 788.72 IND 100.00 100.00 string length 3333333 characters; 3 bytes per character blead patch ------ ------- Ir 100.00 273.64 Dr 100.00 358.56 Dw 100.00 1165.55 COND 100.00 526.35 IND 100.00 100.00 string length 2500000 characters; 4 bytes per character blead patch ------ ------- Ir 100.00 206.03 Dr 100.00 269.68 Dw 100.00 899.17 COND 100.00 395.17 IND 100.00 100.00
'len' is more associated with number of bytes; this is the number of characters, so use 'count'
…ormed input The code works correctly if the input is well-formed. But it was returning a count of the bytes instead of characters for malformed input. It raised a 'utf8' warning, if enabled, before the bad return.
A future commit will surround the indented lines with a block, and will remove a block from the outdented ones. This commit leaves things in an apparent inconsistent state, but this will be fixed the commit after the next
The next commit would otherwise potentially cause malformed input to read off the end of the buffer.
This is in preparation for the next commit to simplify things
When nearing the end of the input string when counting continuations per-word, we switch to counting per-character, using UTF8SKIP. Prior to this commit, we counted continuations based on UTF8SKIP and later converted to characters. But it is simpler to convert the continuations count so far to a character count, and simply use that from then on
Instead of breaking out of the loop when we find an error go directly to the error-handling code. This skips an extra conditional after the loop for this case.
This is in preparation for the next commit
Previous commits in this series have made these two areas identical except for comments, and have repositioned things so that they can easily be consolidated.
This is in preparation for more possible types of warning messages
Reading this code, I don't think the condition tested for by the 'if' can happen, but I added an assert in case I'm wrong.
It is illegal to call this function with the start pointer beyond the end pointer. This is asserted against in PERL_ARGS_ASSERT_UTF8_LENGTH. Therefore no additional code is needed to check against that.
This is to enable this to still work on C++ after a future commit would otherwise fail to compile because of goto crossing initialization
This is in preparation for future commits
This is in preparation for it being needed again.
This function is like utf8_length, but has hooks for code that is reading from a non-rewindable byte stream to more easily handle input stopping in the middle of multi-byte characters. The function is marked API, but experimental.
This code is used for 3 different ops. In the non-pp_read ones, the values are in bytes; in pp_read, they can be in characters as well. I found the names confusing given this intermixing. Changing the names of the two variables that always refer to character values helps lessen the confusion Also add a few clarifying comments
This results in significant speed improvements. See Perl#24511
This is when $/ is set to an integer reference. This speeds up the process greatly, and simplifies the code here.
|
I have polished this up, so I think it would be ready for merging if not for needing some APItests, which I'm holding off on until that gets split. @tonycoz, this now has changed sv_gets_read_record to use the new utf8_length_maybe_partial |
|
@khwilliamson Nice work! |