Alejandro Colomar wrote: > some experiment shows that s+strlen(s) is faster: > > 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
I'm adding a benchmark (see attached patch). I get similar results as you do: With current glibc, on x86_64: $ gltests/bench-strnul nlc 50 100000000 Test n real 0.307205 user 0.307 sys 0.000 Test l real 0.285057 user 0.285 sys 0.000 Test c real 0.334361 user 0.334 sys 0.000 With current glibc, on i686: $ gltests/bench-strnul nlc 50 100000000 Test n real 0.307825 user 0.308 sys 0.000 Test l real 0.309397 user 0.309 sys 0.000 Test c real 0.500006 user 0.500 sys 0.000 So, I confirm that strchr is consistently slower, and that the transformation done by gcc >= 7 and clang >= 4 is actually an optimization. > > 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 guess that when Ulrich Drepper wrote this text in 1999, the particular combination of - the i586 processors available at the time, - the strlen implementation in glibc at the time, - the strchr implementation in glibc at the time made the strchr expression the fastest one. Lesson learned: Benchmarks need to be repeated every 5 or 10 years. Which is why we keep the benchmarks under version control in Gnulib. Bruno
>From 45bac631c5b12f5bb59353a7806551701a9d3a7d Mon Sep 17 00:00:00 2001 From: Bruno Haible <[email protected]> Date: Sun, 22 Feb 2026 08:58:11 +0100 Subject: [PATCH] strnul-bench-tests: New module. * tests/bench-strnul.c: New file. * modules/strnul-bench-tests: New file. --- ChangeLog | 6 ++ modules/strnul-bench-tests | 16 +++++ tests/bench-strnul.c | 140 +++++++++++++++++++++++++++++++++++++ 3 files changed, 162 insertions(+) create mode 100644 modules/strnul-bench-tests create mode 100644 tests/bench-strnul.c diff --git a/ChangeLog b/ChangeLog index 2c934641da..8eac7b6430 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,9 @@ +2026-02-22 Bruno Haible <[email protected]> + + strnul-bench-tests: New module. + * tests/bench-strnul.c: New file. + * modules/strnul-bench-tests: New file. + 2026-02-21 Paul Eggert <[email protected]> cdefs: omit ungrammatical (and unnecessary) #error diff --git a/modules/strnul-bench-tests b/modules/strnul-bench-tests new file mode 100644 index 0000000000..816528d675 --- /dev/null +++ b/modules/strnul-bench-tests @@ -0,0 +1,16 @@ +Files: +tests/bench-strnul.c +tests/bench-multibyte.h +tests/bench.h + +Depends-on: +strnul +striconv +getrusage +gettimeofday + +configure.ac: + +Makefile.am: +noinst_PROGRAMS += bench-strnul +bench_strnul_LDADD = $(LDADD) $(LIBICONV) diff --git a/tests/bench-strnul.c b/tests/bench-strnul.c new file mode 100644 index 0000000000..539c371682 --- /dev/null +++ b/tests/bench-strnul.c @@ -0,0 +1,140 @@ +/* Benchmark for strnul(). + Copyright (C) 2026 Free Software Foundation, Inc. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <https://www.gnu.org/licenses/>. */ + +#include <config.h> + +#include <string.h> + +#include "bench.h" +#include "bench-multibyte.h" + +const char * volatile input; +const char * volatile output; + +static _GL_ATTRIBUTE_NOINLINE void +do_strnul_test (char test, int repeat, const char *text) +{ + printf ("Test %c\n", test); + + struct timings_state ts; + timing_start (&ts); + + for (int count = 0; count < repeat; count++) + { + input = text; + output = strnul (input); + } + + timing_end (&ts); + timing_output (&ts); + printf ("\n"); +} + +static _GL_ATTRIBUTE_NOINLINE void +do_strlen_test (char test, int repeat, const char *text) +{ + printf ("Test %c\n", test); + + size_t (* volatile p_strlen) (const char *) = strlen; + + struct timings_state ts; + timing_start (&ts); + + for (int count = 0; count < repeat; count++) + { + input = text; + output = input + p_strlen (input); + } + + timing_end (&ts); + timing_output (&ts); + printf ("\n"); +} + +static _GL_ATTRIBUTE_NOINLINE void +do_strchr_test (char test, int repeat, const char *text) +{ + printf ("Test %c\n", test); + + char * (* volatile p_strchr) (const char *, int) = strchr; + + struct timings_state ts; + timing_start (&ts); + + for (int count = 0; count < repeat; count++) + { + input = text; + output = p_strchr (input, '\0'); + } + + timing_end (&ts); + timing_output (&ts); + printf ("\n"); +} + +/* Performs some or all of the following tests: + n - strnul + l - strlen + c - strchr + Pass the tests to be performed as first argument. */ +int +main (int argc, char *argv[]) +{ + if (argc != 4) + { + fprintf (stderr, "Usage: %s TESTS LENGTH REPETITIONS\n", argv[0]); + fprintf (stderr, "Example: %s nlc 100 1000000\n", argv[0]); + exit (1); + } + + const char *tests = argv[1]; + int length = atoi (argv[2]); + int repeat = atoi (argv[3]); + + text_init (); + if (length > strlen (text_french_iso8859)) + { + fprintf (stderr, "LENGTH too large, should be <= %zu\n", + strlen (text_french_iso8859)); + exit (1); + } + const char *text = + text_french_iso8859 + strlen (text_french_iso8859) - length; + + /* Execute each test. */ + for (size_t i = 0; i < strlen (tests); i++) + { + char test = tests[i]; + + switch (test) + { + case 'n': + do_strnul_test (test, repeat, text); + break; + case 'l': + do_strlen_test (test, repeat, text); + break; + case 'c': + do_strchr_test (test, repeat, text); + break; + default: + /* Ignore. */ + ; + } + } + + return 0; +} -- 2.52.0
