Skip to content

Instantly share code, notes, and snippets.

@peff
Last active September 10, 2021 17:31
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save peff/433674a829831b88c93965d7519bfafb to your computer and use it in GitHub Desktop.
Save peff/433674a829831b88c93965d7519bfafb to your computer and use it in GitHub Desktop.
timing of a simple git command versus an in-process read
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <time.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <spawn.h>
static void die_sys(const char *msg)
{
perror(msg);
exit(1);
}
static void doit_read(const char *repo)
{
char fn[128];
char buf[128];
FILE *fh;
snprintf(fn, sizeof(fn), "%s/HEAD", repo);
fh = fopen(fn, "r");
if (!fh)
die_sys("fopen");
if (!fgets(buf, sizeof(buf), fh))
die_sys("fgets");
/* in the real world, actually check for "ref: " */
printf("%s", buf + 5);
fclose(fh);
}
static void copy_from_pipe(int fd)
{
char buf[128];
size_t ret;
ret = read(fd, buf, sizeof(buf));
if (ret < 0)
die_sys("read");
close(fd);
fwrite(buf, 1, ret, stdout);
}
static void doit_forkexecv(const char **argv)
{
int fd[2];
pid_t pid;
if (pipe(fd) < 0)
die_sys("pipe");
pid = fork();
if (pid < 0)
die_sys("fork");
if (pid == 0) {
close(fd[0]);
if (dup2(fd[1], 1) < 0)
die_sys("dup2");
execvp(argv[0], (char **)argv);
die_sys("exec");
}
close(fd[1]);
copy_from_pipe(fd[0]);
waitpid(pid, NULL, 0);
}
static void doit_spawnv(const char **argv)
{
pid_t pid;
posix_spawn_file_actions_t fops;
int fd[2];
if (pipe(fd) < 0)
die_sys("pipe");
if (posix_spawn_file_actions_init(&fops) ||
posix_spawn_file_actions_addclose(&fops, fd[0]) ||
posix_spawn_file_actions_adddup2(&fops, fd[1], 1))
die_sys("fops");
if (posix_spawnp(&pid, argv[0], &fops, NULL, (char * const*)argv, NULL))
die_sys("posix_spawnp");
posix_spawn_file_actions_destroy(&fops);
close(fd[1]);
copy_from_pipe(fd[0]);
waitpid(pid, NULL, 0);
}
static const char *git_argv[] = {
"git",
"--git-dir", NULL,
"symbolic-ref",
"HEAD",
NULL
};
static const char *trivial_argv[] = {
"echo",
"foo",
NULL
};
static void doit_forkexec(const char *repo)
{
git_argv[2] = repo;
doit_forkexecv(git_argv);
}
static void doit_forkexec_trivial(const char *repo)
{
doit_forkexecv(trivial_argv);
}
static void doit_spawn(const char *repo)
{
git_argv[2] = repo;
doit_spawnv(git_argv);
}
static void doit_spawn_trivial(const char *repo)
{
doit_spawnv(trivial_argv);
}
typedef void (*doit_func)(const char *);
static void usage(void)
{
fprintf(stderr, "usage: prog <method> <iterations> <repo>\n");
exit(1);
}
static uint64_t current_nanos(void)
{
struct timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts);
return (uint64_t) ts.tv_sec * 1000000000 + ts.tv_nsec;
}
int main(int argc, const char **argv)
{
const char *method;
int i, n;
const char *repo;
uint64_t before, after;
doit_func doit;
method = *++argv;
if (!method)
usage();
if (!strcmp(method, "read"))
doit = doit_read;
else if (!strcmp(method, "forkexec"))
doit = doit_forkexec;
else if (!strcmp(method, "spawn"))
doit = doit_spawn;
else if (!strcmp(method, "forkexec-trivial"))
doit = doit_forkexec_trivial;
else if (!strcmp(method, "spawn-trivial"))
doit = doit_spawn_trivial;
else
usage();
if (!*++argv)
usage();
n = atoi(*argv);
repo = *++argv;
if (!repo)
usage();
before = current_nanos();
for (i = 0; i < n; i++)
doit(repo);
after = current_nanos();
fprintf(stderr, "%d runs of %s in %lu nanoseconds: %lu ns/run\n",
n, method, (unsigned long)(after - before),
(unsigned long)(after - before) / n);
return 0;
}
CFLAGS = -Wall -Werror -O2
all: foo
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment