Re: [PATCH] libcpp: Optimize AArch64 search_line_fast

Wilco Dijkstra <[email protected]>
Newsgroups gmane.comp.gcc.patches
Message-ID <PAWPR08MB898244A774001A1F2CF4A92683DA2@PAWPR08MB8982.eurprd08.prod.outlook.com>
Hi Alexander,

> On Wed, 12 Aug 2026, Wilco Dijkstra wrote:
>
>> Performance is significantly better as a result: 48% faster on Neoverse N1,
>> 67% on Neoverse V1 and 69% on Neoverse V2.
>
> It is a bit strange to see exact numbers without a mention of the workload.
> It is not very surprising that the new loop runs 2x-3x faster on modern CPUs,
> but if you're showing exact percentages, can you say how you measured them,
> please?

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.

> 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?

> I don't see why you want to switch to an aligned load at the end. x86 relies
> on sufficient padding to make unaligned loads all the way. One more issue with
> that:

I didn't know there is guaranteed padding at the end, so trying to avoid overreading
is not really needed...

>> +  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;

> 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;  

Cheers,
Wilco
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.