On Fri, 14 Aug 2026, Wilco Dijkstra wrote:

> It's just measuring the throughput of the search_line_fast function for 
> different sizes.
> Then calculate the average from the speedup of each size times the frequency 
> of the
> distribution. As it happens, the old code was doing a lot of unnecessary 
> initialization,
> checks etc, so the speedup goes from ~85% for 1-16 chars to ~50% at 80 chars.

Thanks. It's good to know that new code is that much faster for short lines,
but I suspect averaging in this manner is not ideal if measurements are taken
as if final branch is perfectly predictable.

> > Yeah. When working on x86 SSSE3 implementation I noticed that at 16 
> > characters
> > per iteration, the branch that exits the loop tends to be pretty 
> > unpredictable
> > on "typical" C++ sources, so moving to 32 characters per iteration helped 
> > with
> > that a bit, even with 16-byte vectors (so two vector loads per iteration, 
> > etc)
> >
> > I wonder if you looked at something like that? Doing two 16-character 
> > matches
> > per one iteration, then combining results into one GPR for the final CBZ?
> > Not sure if there's a way to make the epilogue cheap enough, though.
> 
> It's certainly possible to do so, but it would be hard to measure whether it 
> ends up
> faster. It probably means saving a trace and then replaying it. How did you 
> measure
> the effect of branch prediction on overall compile time?

With 'perf stat -r 9 cc1plus -fsyntax-only -quiet t-rawstr.cc' where t-rawstr.cc
is a big C++ file created by preprocessing '#include <regex>' (see Makefile in
the attachment). It was posted with my SSSE3-based search_line_fast at
https://inbox.sourceware.org/gcc-patches/[email protected]/
(attaching it again to this message)
and my measurements can be seen at
https://inbox.sourceware.org/gcc-patches/[email protected]/

> >> +  const uchar *limit = (const uchar*) ((uintptr_t)end & ~15);
> >
> > This is not safe in absence of padding: when 's' itself is not aligned,
> > and 'end' is close to it, this may move 'limit' to before 's', and then
> > the load from *limit will pick up bytes prior to 's'.
> 
> Yes that's why I read from limit to get a full vector and then just shift out 
> any
> chars before 's' - this magic removes 0-15 nibbles from the result mask based
> on the alignment of 's':
> 
> +  mask >>= (uintptr_t)s * 4;

Sorry, I missed that. Would have been worth a comment, if it wasn't going!

> > Now, libcpp provides sufficient tail padding (at least 16 bytes, with '\n'
> > at the beginning of padding area) so in context this is safe, but then
> > the final aligned load should never be reached, and computing 'limit' is
> > pointless.
> 
> If we can safely read 16 bytes beyond 'end' then yes, the loop condition 
> could be:
> 
> while (s < end)
>   ... loop
> // no tail code, just return end since it is known that *end == '\n'.
> return end;  

No, I meant you can just have an infinite loop because the test in the loop
will work properly when *end is reached.

Alexander

PS: unfortunately I didn't get a copy of your response to my inbox and had to
fish it out of the mailing list archive

Attachment: search-line-bench.tgz
Description: application/gzip

Reply via email to