Skip to content

Instantly share code, notes, and snippets.

@N-R-K
Last active July 25, 2024 01:20
Show Gist options
  • Save N-R-K/ebf096448c0a7f3fdd8b93d280747550 to your computer and use it in GitHub Desktop.
Save N-R-K/ebf096448c0a7f3fdd8b93d280747550 to your computer and use it in GitHub Desktop.
///////////
// compile with:
// $ gcc -O3 -march=native -fno-builtin -o /tmp/test bench.c glibc.c openbsd.c bespoke.c
//
// the various *_strlcpy functions are placed into their own
// file/translation-unit so that the compiler doesn't do any inlining or
// const-propogation, skewing the benchmark.
// for this reason LTO is also NOT enabled.
//
// -fno-builtin is so that bespoke_strlcpy doesn't get "optimized" down to
// strlen + memcpy call.
#include <time.h>
#include <stdio.h>
#include <string.h>
size_t glibc_strlcpy(char *dst, const char *src, size_t size);
size_t openbsd_strlcpy(char *dst, const char *src, size_t size);
size_t bespoke_strlcpy(char *dst, const char *src, size_t size);
typedef struct timespec T;
static T
now(void)
{
T t;
clock_gettime(CLOCK_BOOTTIME, &t);
return t;
}
static void
timediff_print(T e, T s)
{
long sec = e.tv_sec - s.tv_sec;
long ns = e.tv_nsec - s.tv_nsec;
if (ns < 0) {
--sec;
ns += 1000000000L;
}
long us = sec * 1000000L + ns / 1000L;
fprintf(stderr, "%8ldus\n", us);
}
int main(void)
{
enum { N = 1<<9, ITER = 1024 };
char src[N] = {0};
memset(src, 1, N - 1);
T beg, end;
beg = now();
for (int i = 0; i < ITER; ++i) {
char buf[N];
char *p = buf;
__asm volatile ("" : "+r"(p));
openbsd_strlcpy(p, src, N);
__asm volatile ("" : "+r"(p) :: "memory" );
}
end = now();
fprintf(stderr, "openbsd: ");
timediff_print(end, beg);
beg = now();
for (int i = 0; i < ITER; ++i) {
char buf[N];
char *p = buf;
__asm volatile ("" : "+r"(p));
glibc_strlcpy(p, src, N);
__asm volatile ("" : "+r"(p) :: "memory" );
}
end = now();
fprintf(stderr, "glibc: ");
timediff_print(end, beg);
beg = now();
for (int i = 0; i < ITER; ++i) {
char buf[N];
char *p = buf;
__asm volatile ("" : "+r"(p));
bespoke_strlcpy(p, src, N);
__asm volatile ("" : "+r"(p) :: "memory" );
}
end = now();
fprintf(stderr, "bespoke: ");
timediff_print(end, beg);
}
#include <stddef.h>
#include <string.h>
size_t
bespoke_strlcpy(char *dst, const char *src, size_t size)
{
/* size_t len = strlen(src); */
size_t len = 0;
for (; src[len] != '\0'; ++len) {} // strlen()
if (size > 0) {
size_t to_copy = len < size ? len : size - 1;
for (size_t i = 0; i < to_copy; ++i) // memcpy()
dst[i] = src[i];
dst[to_copy] = '\0';
}
return len;
}
#include <string.h>
size_t
glibc_strlcpy(char *__restrict dest, const char *__restrict src, size_t size)
{
size_t src_length = strlen (src);
if (src_length >= size)
{
if (size > 0)
{
/* Copy the leading portion of the string. The last
character is subsequently overwritten with the NUL
terminator, but the destination size is usually a
multiple of a small power of two, so writing it twice
should be more efficient than copying an odd number of
bytes. */
memcpy (dest, src, size);
dest[size - 1] = '\0';
}
}
else
/* Copy the string and its terminating NUL character. */
memcpy (dest, src, src_length + 1);
return src_length;
}
#include <stddef.h>
size_t
openbsd_strlcpy(char *dst, const char *src, size_t dsize)
{
const char *osrc = src;
size_t nleft = dsize;
/* Copy as many bytes as will fit. */
if (nleft != 0) {
while (--nleft != 0) {
if ((*dst++ = *src++) == '\0')
break;
}
}
/* Not enough room in dst, add NUL and traverse rest of src. */
if (nleft == 0) {
if (dsize != 0)
*dst = '\0'; /* NUL-terminate dst */
while (*src++)
;
}
return(src - osrc - 1); /* count does not include NUL */
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment