[PATCH] riscv: avoid vle8ff in vector strcmp loop

daichengrong daichengrong@iscas.ac.cn
Mon Aug 10 08:57:38 GMT 2026



On 6/15/26 21:06, Carlos O'Donell wrote:
> On 6/14/26 6:55 AM, daichengrong wrote:
>> Limit vl by the minimum distance from the input pointers to the next 4K
>> boundary, and use normal vle8.v loads in the main loop.  This keeps the
>> loads page-safe while avoiding the vl update dependency from vle8ff.v.
> 
> Do you have microbenchmark data that supports this change?
> 
> Generally for such changes we would like to see "make bench" showing a difference.

Sorry, my previous reply with the benchmark results was accidentally sent
outside this thread. Reposting the results here to keep the discussion
in the proper thread.

I ran the benchmark on the SpacemiT K3 platform.

The comparison is between the current __strcmp_vector implementation in
the git tree and the new implementation in this patch.

The main purpose of this patch is to avoid using vle8ff.v in the common
strcmp vector loop.  The strcmp implementation itself does not know the
string length in advance.  It only uses the current pointer values to
compute the distance from both input pointers to the next 4K boundary.

The smaller distance is used to limit vl, and then normal vle8.v loads
are used.  This keeps the vector loads from crossing a 4K boundary while
avoiding the vl update dependency from vle8ff.v.

For the benchmark analysis, I grouped the test cases by the benchmark
string length.  This is only for presenting the results; the
implementation itself does not use the length.

Grouped by benchmark string length:

Len bucket  N     Git_t     New_t    Speedup
--------------------------------------------
0-256       4048  42.091    38.438   1.10x
257-511     490   229.183   69.731   3.29x
512-1023    98    292.148   91.323   3.20x
1024-2047   98    564.029   149.398  3.78x
2048-4095   70    1109.365  278.249  3.99x
4096-8191   70    2197.762  474.719  4.63x

Since the patch changes the page-boundary handling, I also grouped the
results by the smaller distance of the two input pointers to the next 4K
boundary:

Page dist  N     Git_t    New_t    Speedup
------------------------------------------
1-16       966   144.690  68.287   2.12x
17-32      1026  52.423   49.389   1.06x
33-64      841   95.930   42.210   2.27x
65-128     682   94.812   31.330   3.03x
129-256    393   112.647  65.460   1.72x
256        966   222.487  69.186   3.22x

>  
>> Signed-off-by: daichengrong <daichengrong@iscas.ac.cn>
>> ---
>>   sysdeps/riscv/rvv/strcmp.S | 110 +++++++++++++++++++++----------------
>>   1 file changed, 64 insertions(+), 46 deletions(-)
>>
>> diff --git a/sysdeps/riscv/rvv/strcmp.S b/sysdeps/riscv/rvv/strcmp.S
>> index a2f6865581..d32d8f490a 100644
>> --- a/sysdeps/riscv/rvv/strcmp.S
>> +++ b/sysdeps/riscv/rvv/strcmp.S
>> @@ -35,59 +35,77 @@
>>   #define vstr1 v0
>>   #define vstr2 v8
>>   #define vmask1 v16
>> -#define vmask2 v17
>> +#define vmask2 v24
>>     ENTRY (STRCMP)
>>   .option push
>>   .option arch, +v
>> -    /* lmul=1 */
>> -L(Loop):
>> -    vsetvli ivl, zero, e8, m1, ta, ma
>> -    vle8ff.v vstr1, (str1)
>> -    /* Check if vstr1[i] == 0 */
>> -    vmseq.vx vmask1, vstr1, zero
>> -
>> -    vle8ff.v vstr2, (str2)
>> -    /* Check if vstr1[i] != vstr2[i] */
>> -    vmsne.vv vmask2, vstr1, vstr2
>> -
>> -    /* Find the index x for vstr1[x] == 0 */
>> -    vfirst.m temp1, vmask1
>> -    /* Find the index x for vstr1[x] != vstr2[x] */
>> -    vfirst.m temp2, vmask2
>> -
>> -    bgez temp1, L(check1)
>> -    bgez temp2, L(check2)
>> -
>> -    /* Get the current vl updated by vle8ff. */
>> -    csrr ivl, vl
>> -    add str1, str1, ivl
>> -    add str2, str2, ivl
>> -    j L(Loop)
>> -
>> -    /* temp1 >= 0 */
>> -L(check1):
>> -    bltz temp2, L(return_at_nul)
>> -    blt temp2, temp1, L(check2)
>> -L(return_at_nul):
>> -    /* temp2 < 0 */
>> -    /* temp2 >= 0 && temp1 < temp2 */
>> -    add str1, str1, temp1
>> -    add str2, str2, temp1
>> -    lbu temp1, 0(str1)
>> -    lbu temp2, 0(str2)
>> -    sub result, temp1, temp2
>> +    li          t0, 4095
>> +    li          t3, 4096
>> +L(loop):
>> +    and         t1, str1, t0      /* t1 = str1 offset in 4K page */
>> +    and         t2, str2, t0      /* t2 = str2 offset in 4K page */
>> +    sub         t4, t3, t1        /* t4 = bytes from str1 to page end */
>> +    sub         t5, t3, t2        /* t5 = bytes from str2 to page end */
>> +    /*
>> +     * t3 = min(dist1, dist2)
>> +     */
>> +    bltu        t5, t4, 1f
>> +    mv          t5, t4
>> +1:
>> +    /*
>> +     * Set vl to min(dist, VLMAX).
>> +     *
>> +     * If far from page boundary, vl = VLMAX.
>> +     * If close to page boundary, vl = dist.
>> +     */
>> +
>> +    vsetvli     ivl, t5, e8, m4, ta, ma
>> +    /*
>> +     * This load will not cross a 4K boundary for either pointer.
>> +     */
>> +    vle8.v      vstr1, (str1)
>> +    vle8.v      vstr2, (str2)
>> +
>> +    /*
>> +     * str1 == 0 means string ended.
>> +     * str1 != str2 means strcmp result is decided.
>> +     */
>> +    vmseq.vx    vmask1, vstr1, zero
>> +    vmsne.vv    vmask2, vstr1, vstr2
>> +
>> +    vfirst.m    temp2, vmask2
>> +    vfirst.m    temp1, vmask1
>> +
>> +    bgez        temp2, L(check_nul)
>> +    bgez        temp1, L(ret)
>> +
>> +    /*
>> +     * No NUL and no difference in this chunk.
>> +     * Advance by actual vl.
>> +     *
>> +     * If this was a page-boundary-limited load, one of str1/str2 now
>> +     * reaches the next 4K boundary. The next loop recomputes distance.
>> +     */
>> +    add         str1, str1, ivl
>> +    add         str2, str2, ivl
>> +    j           L(loop)
>> +
>> +L(check_nul):
>> +    bltz        temp1, L(found)
>> +    blt         temp1, temp2, L(ret)
>> +L(found):
>> +    add         str1, str1, temp2
>> +    add         str2, str2, temp2
>> +    lbu         temp1, 0(str1)
>> +    lbu         temp2, 0(str2)
>> +    sub         result, temp1, temp2
>>       ret
>>   -    /* temp1 < 0 */
>> -    /* temp2 >= 0 */
>> -L(check2):
>> -    add str1, str1, temp2
>> -    add str2, str2, temp2
>> -    lbu temp1, 0(str1)
>> -    lbu temp2, 0(str2)
>> -    sub result, temp1, temp2
>> +L(ret):
>> +    li          result, 0
>>       ret
>> +
>>   .option pop
>>   END (STRCMP)
>>   libc_hidden_builtin_def (strcmp)
> 
> 


-- 
Thanks,
Chengrong. 



More information about the Libc-alpha mailing list