Hi Ben, On 2026-02-21T09:47:12-0800, Ben Pfaff wrote: > On Sat, Feb 21, 2026 at 1:38 AM Bruno Haible via Gnulib discussion > list <[email protected]> wrote: > > strchr is a red herring, because this way to define strnul is compiled > > less efficiently than (s + strlen (s)). See attached example code. > > > > The better way is thus (s + strlen (s)), and since it evaluates s twice, > > it means we need an inline function, not a macro. But we have the machinery > > for inline functions in Gnulib. > > Maybe it would be a good idea to suggest an update to the glibc manual,
Agree.
> because it says that strchr(s, '\0') is faster than s + strlen(s):
Huh; I'm curious about why that's said in the glibc manual.
I would be surprised if that were true, given strchr() must perform two
comparisons per iteration. And indeed, some experiment shows that
s+strlen(s) is faster:
alx@devuan:~/tmp$ diff -U9 strnul?.c
--- strnul1.c 2026-02-21 21:08:11.932024735 +0100
+++ strnul2.c 2026-02-21 21:08:15.718094220 +0100
@@ -2,11 +2,11 @@
#include <string.h>
int
main(int argc, char *argv[])
{
const char *p, *q;
int n = atoi(argv[1]);
p = argv[0];
for (int i = 0; i < n; i++)
- q = p + strlen(p);
+ q = strchr(p, '\0');
}
alx@devuan:~/tmp$ gcc -S -fno-builtin strnul?.c
alx@devuan:~/tmp$ diff -U9 strnul?.s
--- strnul1.s 2026-02-21 21:11:41.185153412 +0100
+++ strnul2.s 2026-02-21 21:11:41.197153516 +0100
@@ -1,10 +1,10 @@
- .file "strnul1.c"
+ .file "strnul2.c"
.text
.globl main
.type main, @function
main:
.LFB6:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
@@ -20,22 +20,21 @@
call atoi@PLT
movl %eax, -8(%rbp)
movq -48(%rbp), %rax
movq (%rax), %rax
movq %rax, -16(%rbp)
movl $0, -4(%rbp)
jmp .L2
.L3:
movq -16(%rbp), %rax
+ movl $0, %esi
movq %rax, %rdi
- call strlen@PLT
- movq -16(%rbp), %rdx
- addq %rdx, %rax
+ call strchr@PLT
movq %rax, -24(%rbp)
addl $1, -4(%rbp)
.L2:
movl -4(%rbp), %eax
cmpl -8(%rbp), %eax
jl .L3
movl $0, %eax
leave
.cfi_def_cfa 7, 8
alx@devuan:~/tmp$ gcc -fno-builtin strnul1.c -o strnul1
alx@devuan:~/tmp$ gcc -fno-builtin strnul2.c -o strnul2
alx@devuan:~/tmp$ time ./strnul1 1000000000
real 0m1.006s
user 0m1.002s
sys 0m0.004s
alx@devuan:~/tmp$ time ./strnul2 1000000000
real 0m1.243s
user 0m1.239s
sys 0m0.004s
The numbers are consistent over many runs.
Have a lovely night!
Alex
>
> > One useful, but unusual, use of the strchr function is when one wants to
> > have a
> > pointer pointing to the null byte terminating a string. This is often
> > written in this way:
> >
> > s += strlen (s);
> >
> > This is almost optimal but the addition operation duplicated a bit of the
> > work
> > already done in the strlen function. A better solution is this:
> >
> > s = strchr (s, '\0');
> >
> > There is no restriction on the second parameter of strchr so it could very
> > well
> > also be zero. Those readers thinking very hard about this might now point
> > out
> > that the strchr function is more expensive than the strlen function since we
> > have two abort criteria. This is right. But in the GNU C Library the
> > implementation of strchr is optimized in a special way so that strchr
> > actually is faster.
>
> (See, e.g.
> https://sourceware.org/glibc/manual/latest/html_node/Search-Functions.html)
--
<https://www.alejandro-colomar.es>
signature.asc
Description: PGP signature
