glibc/string/string-inlines.c
Wilco Dijkstra 5625f666ce This patch cleans up the strsep implementation and improves performance.
Currently strsep calls strpbrk is is now a veneer to strcspn.  Calling
strcspn directly is faster.  Since it handles a delimiter string of size
1 as a special case, this is not needed in strsep itself.  Although this
means there is a slightly higher overhead if the delimiter size is 1,
all other cases are slightly faster.  The overall performance gain is 5-10%
on AArch64.

The string/bits/string2.h header contains optimizations for constant
delimiters of size 1-3.  Benchmarking these showed similar performance for
size 1 (since in all cases strchr/strchrnul is used), while size 2 and 3
can give up to 2x speedup for small input strings.  However if these cases
are common it seems much better to add this optimization to strcspn.
So move these header optimizations to string-inlines.c.

Improve the strsep benchmark so that it actually benchmarks something.
The current version contains a delimiter character at every position in the
input string, so there is very little work to do, and the extremely inefficent
simple_strsep implementation appears fastest in every case.  The new version
has either no match in the input for the fail case and a match halfway in the
input for the success case.  The input is then restored so that each iteration
does exactly the same amount of work.  Reduce the number of testcases since
simple_strsep takes a lot of time now.

	* benchtests/bench-strsep.c (oldstrsep): Add old implementation.
	(do_one_test) Restore original string so iteration works.
	* string/string-inlines.c (do_test): Create better input strings.
	(test_main) Reduce number of testruns.
	* string/string-inlines.c (__old_strsep_1c): New function.
	(__old_strsep_2c): Likewise.
	(__old_strsep_3c): Likewise.
	* string/strsep.c (__strsep): Remove case of small delim string.
	Call strcspn directly rather than strpbrk.
	* string/bits/string2.h (__strsep): Remove define.
	(__strsep_1c): Remove.
	(__strsep_2c): Remove.
	(__strsep_3c): Remove.
	(strsep): Remove.
	* sysdeps/unix/sysv/linux/internal_statvfs.c
	(__statvfs_getflags): Rename to __strsep.
2016-12-21 15:16:29 +00:00

571 lines
14 KiB
C

/* Copyright (C) 1999-2016 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU C Library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, see
<http://www.gnu.org/licenses/>. */
/* <bits/string.h> and <bits/string2.h> declare some extern inline
functions. These functions are declared additionally here if
inlining is not possible. */
#undef __USE_STRING_INLINES
#define __USE_STRING_INLINES
#define _FORCE_INLINES
#define __STRING_INLINE /* empty */
#define __NO_INLINE__
#include <string.h>
#undef index
#undef rindex
#undef __NO_INLINE__
#include <bits/string.h>
#include <bits/string2.h>
#include "shlib-compat.h"
#if SHLIB_COMPAT (libc, GLIBC_2_1_1, GLIBC_2_25)
/* The inline functions are not used from GLIBC 2.25 and forward, however
they are required to provide the symbols through string-inlines.c
(if inlining is not possible for compatibility reasons). */
char *
__old_strtok_r_1c (char *__s, char __sep, char **__nextp)
{
char *__result;
if (__s == NULL)
__s = *__nextp;
while (*__s == __sep)
++__s;
__result = NULL;
if (*__s != '\0')
{
__result = __s++;
while (*__s != '\0')
if (*__s++ == __sep)
{
__s[-1] = '\0';
break;
}
}
*__nextp = __s;
return __result;
}
compat_symbol (libc, __old_strtok_r_1c, __strtok_r_1c, GLIBC_2_1_1);
char *
__old_strsep_1c (char **__s, char __reject)
{
char *__retval = *__s;
if (__retval != NULL && (*__s = strchr (__retval, __reject)) != NULL)
*(*__s)++ = '\0';
return __retval;
}
compat_symbol (libc, __old_strsep_1c, __strsep_1c, GLIBC_2_1_1);
char *
__old_strsep_2c (char **__s, char __reject1, char __reject2)
{
char *__retval = *__s;
if (__retval != NULL)
{
char *__cp = __retval;
while (1)
{
if (*__cp == '\0')
{
__cp = NULL;
break;
}
if (*__cp == __reject1 || *__cp == __reject2)
{
*__cp++ = '\0';
break;
}
++__cp;
}
*__s = __cp;
}
return __retval;
}
compat_symbol (libc, __old_strsep_2c, __strsep_2c, GLIBC_2_1_1);
char *
__old_strsep_3c (char **__s, char __reject1, char __reject2, char __reject3)
{
char *__retval = *__s;
if (__retval != NULL)
{
char *__cp = __retval;
while (1)
{
if (*__cp == '\0')
{
__cp = NULL;
break;
}
if (*__cp == __reject1 || *__cp == __reject2 || *__cp == __reject3)
{
*__cp++ = '\0';
break;
}
++__cp;
}
*__s = __cp;
}
return __retval;
}
compat_symbol (libc, __old_strsep_3c, __strsep_3c, GLIBC_2_1_1);
#endif
#if SHLIB_COMPAT (libc, GLIBC_2_1_1, GLIBC_2_24)
/* The inline functions are not used from GLIBC 2.24 and forward, however
they are required to provide the symbols through string-inlines.c
(if inlining is not possible for compatibility reasons). */
size_t
__old_strcspn_c1 (const char *__s, int __reject)
{
size_t __result = 0;
while (__s[__result] != '\0' && __s[__result] != __reject)
++__result;
return __result;
}
compat_symbol (libc, __old_strcspn_c1, __strcspn_c1, GLIBC_2_1_1);
size_t
__old_strcspn_c2 (const char *__s, int __reject1, int __reject2)
{
size_t __result = 0;
while (__s[__result] != '\0' && __s[__result] != __reject1
&& __s[__result] != __reject2)
++__result;
return __result;
}
compat_symbol (libc, __old_strcspn_c2, __strcspn_c2, GLIBC_2_1_1);
size_t
__old_strcspn_c3 (const char *__s, int __reject1, int __reject2,
int __reject3)
{
size_t __result = 0;
while (__s[__result] != '\0' && __s[__result] != __reject1
&& __s[__result] != __reject2 && __s[__result] != __reject3)
++__result;
return __result;
}
compat_symbol (libc, __old_strcspn_c3, __strcspn_c3, GLIBC_2_1_1);
size_t
__old_strspn_c1 (const char *__s, int __accept)
{
size_t __result = 0;
/* Please note that __accept never can be '\0'. */
while (__s[__result] == __accept)
++__result;
return __result;
}
compat_symbol (libc, __old_strspn_c1, __strspn_c1, GLIBC_2_1_1);
size_t
__old_strspn_c2 (const char *__s, int __accept1, int __accept2)
{
size_t __result = 0;
/* Please note that __accept1 and __accept2 never can be '\0'. */
while (__s[__result] == __accept1 || __s[__result] == __accept2)
++__result;
return __result;
}
compat_symbol (libc, __old_strspn_c2, __strspn_c2, GLIBC_2_1_1);
size_t
__old_strspn_c3 (const char *__s, int __accept1, int __accept2,
int __accept3)
{
size_t __result = 0;
/* Please note that __accept1 to __accept3 never can be '\0'. */
while (__s[__result] == __accept1 || __s[__result] == __accept2
|| __s[__result] == __accept3)
++__result;
return __result;
}
compat_symbol (libc, __old_strspn_c3, __strspn_c3, GLIBC_2_1_1);
char *
__old_strpbrk_c2 (const char *__s, int __accept1, int __accept2)
{
/* Please note that __accept1 and __accept2 never can be '\0'. */
while (*__s != '\0' && *__s != __accept1 && *__s != __accept2)
++__s;
return *__s == '\0' ? NULL : (char *) (size_t) __s;
}
compat_symbol (libc, __old_strpbrk_c2, __strpbrk_c2, GLIBC_2_1_1);
char *
__old_strpbrk_c3 (const char *__s, int __accept1, int __accept2, int __accept3)
{
/* Please note that __accept1 to __accept3 never can be '\0'. */
while (*__s != '\0' && *__s != __accept1 && *__s != __accept2
&& *__s != __accept3)
++__s;
return *__s == '\0' ? NULL : (char *) (size_t) __s;
}
compat_symbol (libc, __old_strpbrk_c3, __strpbrk_c3, GLIBC_2_1_1);
/* These are a few types we need for the optimizations if we cannot
use unaligned memory accesses. */
# define __STRING2_COPY_TYPE(N) \
typedef struct { unsigned char __arr[N]; } \
__attribute__ ((__packed__)) __STRING2_COPY_ARR##N
__STRING2_COPY_TYPE (2);
__STRING2_COPY_TYPE (3);
__STRING2_COPY_TYPE (4);
__STRING2_COPY_TYPE (5);
__STRING2_COPY_TYPE (6);
__STRING2_COPY_TYPE (7);
__STRING2_COPY_TYPE (8);
# undef __STRING2_COPY_TYPE
# if _STRING_INLINE_unaligned
void *
__old_mempcpy_small (void *__dest1,
char __src0_1, char __src2_1, char __src4_1, char __src6_1,
__uint16_t __src0_2, __uint16_t __src4_2,
__uint32_t __src0_4, __uint32_t __src4_4,
size_t __srclen)
{
union {
__uint32_t __ui;
__uint16_t __usi;
unsigned char __uc;
unsigned char __c;
} *__u = __dest1;
switch ((unsigned int) __srclen)
{
case 1:
__u->__c = __src0_1;
__u = __extension__ ((void *) __u + 1);
break;
case 2:
__u->__usi = __src0_2;
__u = __extension__ ((void *) __u + 2);
break;
case 3:
__u->__usi = __src0_2;
__u = __extension__ ((void *) __u + 2);
__u->__c = __src2_1;
__u = __extension__ ((void *) __u + 1);
break;
case 4:
__u->__ui = __src0_4;
__u = __extension__ ((void *) __u + 4);
break;
case 5:
__u->__ui = __src0_4;
__u = __extension__ ((void *) __u + 4);
__u->__c = __src4_1;
__u = __extension__ ((void *) __u + 1);
break;
case 6:
__u->__ui = __src0_4;
__u = __extension__ ((void *) __u + 4);
__u->__usi = __src4_2;
__u = __extension__ ((void *) __u + 2);
break;
case 7:
__u->__ui = __src0_4;
__u = __extension__ ((void *) __u + 4);
__u->__usi = __src4_2;
__u = __extension__ ((void *) __u + 2);
__u->__c = __src6_1;
__u = __extension__ ((void *) __u + 1);
break;
case 8:
__u->__ui = __src0_4;
__u = __extension__ ((void *) __u + 4);
__u->__ui = __src4_4;
__u = __extension__ ((void *) __u + 4);
break;
}
return (void *) __u;
}
# else
void *
__old_mempcpy_small (void *__dest, char __src1,
__STRING2_COPY_ARR2 __src2, __STRING2_COPY_ARR3 __src3,
__STRING2_COPY_ARR4 __src4, __STRING2_COPY_ARR5 __src5,
__STRING2_COPY_ARR6 __src6, __STRING2_COPY_ARR7 __src7,
__STRING2_COPY_ARR8 __src8, size_t __srclen)
{
union {
char __c;
__STRING2_COPY_ARR2 __sca2;
__STRING2_COPY_ARR3 __sca3;
__STRING2_COPY_ARR4 __sca4;
__STRING2_COPY_ARR5 __sca5;
__STRING2_COPY_ARR6 __sca6;
__STRING2_COPY_ARR7 __sca7;
__STRING2_COPY_ARR8 __sca8;
} *__u = __dest;
switch ((unsigned int) __srclen)
{
case 1:
__u->__c = __src1;
break;
case 2:
__extension__ __u->__sca2 = __src2;
break;
case 3:
__extension__ __u->__sca3 = __src3;
break;
case 4:
__extension__ __u->__sca4 = __src4;
break;
case 5:
__extension__ __u->__sca5 = __src5;
break;
case 6:
__extension__ __u->__sca6 = __src6;
break;
case 7:
__extension__ __u->__sca7 = __src7;
break;
case 8:
__extension__ __u->__sca8 = __src8;
break;
}
return __extension__ ((void *) __u + __srclen);
}
# endif
compat_symbol (libc, __old_mempcpy_small, __mempcpy_small, GLIBC_2_1_1);
# if _STRING_INLINE_unaligned
char *
__old_strcpy_small (char *__dest,
__uint16_t __src0_2, __uint16_t __src4_2,
__uint32_t __src0_4, __uint32_t __src4_4,
size_t __srclen)
{
union {
__uint32_t __ui;
__uint16_t __usi;
unsigned char __uc;
} *__u = (void *) __dest;
switch ((unsigned int) __srclen)
{
case 1:
__u->__uc = '\0';
break;
case 2:
__u->__usi = __src0_2;
break;
case 3:
__u->__usi = __src0_2;
__u = __extension__ ((void *) __u + 2);
__u->__uc = '\0';
break;
case 4:
__u->__ui = __src0_4;
break;
case 5:
__u->__ui = __src0_4;
__u = __extension__ ((void *) __u + 4);
__u->__uc = '\0';
break;
case 6:
__u->__ui = __src0_4;
__u = __extension__ ((void *) __u + 4);
__u->__usi = __src4_2;
break;
case 7:
__u->__ui = __src0_4;
__u = __extension__ ((void *) __u + 4);
__u->__usi = __src4_2;
__u = __extension__ ((void *) __u + 2);
__u->__uc = '\0';
break;
case 8:
__u->__ui = __src0_4;
__u = __extension__ ((void *) __u + 4);
__u->__ui = __src4_4;
break;
}
return __dest;
}
# else
char *
__old_strcpy_small (char *__dest,
__STRING2_COPY_ARR2 __src2, __STRING2_COPY_ARR3 __src3,
__STRING2_COPY_ARR4 __src4, __STRING2_COPY_ARR5 __src5,
__STRING2_COPY_ARR6 __src6, __STRING2_COPY_ARR7 __src7,
__STRING2_COPY_ARR8 __src8, size_t __srclen)
{
union {
char __c;
__STRING2_COPY_ARR2 __sca2;
__STRING2_COPY_ARR3 __sca3;
__STRING2_COPY_ARR4 __sca4;
__STRING2_COPY_ARR5 __sca5;
__STRING2_COPY_ARR6 __sca6;
__STRING2_COPY_ARR7 __sca7;
__STRING2_COPY_ARR8 __sca8;
} *__u = (void *) __dest;
switch ((unsigned int) __srclen)
{
case 1:
__u->__c = '\0';
break;
case 2:
__extension__ __u->__sca2 = __src2;
break;
case 3:
__extension__ __u->__sca3 = __src3;
break;
case 4:
__extension__ __u->__sca4 = __src4;
break;
case 5:
__extension__ __u->__sca5 = __src5;
break;
case 6:
__extension__ __u->__sca6 = __src6;
break;
case 7:
__extension__ __u->__sca7 = __src7;
break;
case 8:
__extension__ __u->__sca8 = __src8;
break;
}
return __dest;
}
# endif
compat_symbol (libc, __old_strcpy_small, __strcpy_small, GLIBC_2_1_1);
# if _STRING_INLINE_unaligned
char *
__old_stpcpy_small (char *__dest,
__uint16_t __src0_2, __uint16_t __src4_2,
__uint32_t __src0_4, __uint32_t __src4_4,
size_t __srclen)
{
union {
unsigned int __ui;
unsigned short int __usi;
unsigned char __uc;
char __c;
} *__u = (void *) __dest;
switch ((unsigned int) __srclen)
{
case 1:
__u->__uc = '\0';
break;
case 2:
__u->__usi = __src0_2;
__u = __extension__ ((void *) __u + 1);
break;
case 3:
__u->__usi = __src0_2;
__u = __extension__ ((void *) __u + 2);
__u->__uc = '\0';
break;
case 4:
__u->__ui = __src0_4;
__u = __extension__ ((void *) __u + 3);
break;
case 5:
__u->__ui = __src0_4;
__u = __extension__ ((void *) __u + 4);
__u->__uc = '\0';
break;
case 6:
__u->__ui = __src0_4;
__u = __extension__ ((void *) __u + 4);
__u->__usi = __src4_2;
__u = __extension__ ((void *) __u + 1);
break;
case 7:
__u->__ui = __src0_4;
__u = __extension__ ((void *) __u + 4);
__u->__usi = __src4_2;
__u = __extension__ ((void *) __u + 2);
__u->__uc = '\0';
break;
case 8:
__u->__ui = __src0_4;
__u = __extension__ ((void *) __u + 4);
__u->__ui = __src4_4;
__u = __extension__ ((void *) __u + 3);
break;
}
return &__u->__c;
}
# else
char *
__old_stpcpy_small (char *__dest,
__STRING2_COPY_ARR2 __src2, __STRING2_COPY_ARR3 __src3,
__STRING2_COPY_ARR4 __src4, __STRING2_COPY_ARR5 __src5,
__STRING2_COPY_ARR6 __src6, __STRING2_COPY_ARR7 __src7,
__STRING2_COPY_ARR8 __src8, size_t __srclen)
{
union {
char __c;
__STRING2_COPY_ARR2 __sca2;
__STRING2_COPY_ARR3 __sca3;
__STRING2_COPY_ARR4 __sca4;
__STRING2_COPY_ARR5 __sca5;
__STRING2_COPY_ARR6 __sca6;
__STRING2_COPY_ARR7 __sca7;
__STRING2_COPY_ARR8 __sca8;
} *__u = (void *) __dest;
switch ((unsigned int) __srclen)
{
case 1:
__u->__c = '\0';
break;
case 2:
__extension__ __u->__sca2 = __src2;
break;
case 3:
__extension__ __u->__sca3 = __src3;
break;
case 4:
__extension__ __u->__sca4 = __src4;
break;
case 5:
__extension__ __u->__sca5 = __src5;
break;
case 6:
__extension__ __u->__sca6 = __src6;
break;
case 7:
__extension__ __u->__sca7 = __src7;
break;
case 8:
__extension__ __u->__sca8 = __src8;
break;
}
return __dest + __srclen - 1;
}
# endif
compat_symbol (libc, __old_stpcpy_small, __stpcpy_small, GLIBC_2_1_1);
#endif