Last active
September 10, 2021 17:31
-
-
Save peff/433674a829831b88c93965d7519bfafb to your computer and use it in GitHub Desktop.
timing of a simple git command versus an in-process read
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
CFLAGS = -Wall -Werror -O2 | |
all: foo |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment