-
Notifications
You must be signed in to change notification settings - Fork 4
/
main.c
155 lines (131 loc) · 3.72 KB
/
main.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
/* _GNU_SOURCE is required for CPU_SET macros in sched.h */
#define _GNU_SOURCE
#include <argp.h>
#include <sched.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <unistd.h>
#include "decls.h"
#define xstr(s) str(s)
#define str(s) #s
#define DEFAULT_CPU -1
#define DEFAULT_MEASUREMENTS 1
#define DEFAULT_ITERS 100000
#define DEFAULT_WARMUP_ITERS 1000
static struct argp_option options[] = {
{"child-cpu", 'c', "CPUID", 0,
"CPU to run the child on; default is to let the scheduler do as it will",
0},
{"parent-cpu", 'p', "CPUID", 0,
"CPU to run the parent on; default is to let the scheduler do as it will",
0},
{"repeat", 'r', "COUNT", 0,
"number of times to repeat measurement; default: " xstr(
DEFAULT_MEASUREMENTS),
0},
{"iters", 'i', "COUNT", 0,
"number of iterations to measure; default: " xstr(DEFAULT_ITERS), 0},
{0, 'n', "COUNT", OPTION_ALIAS, 0, 0},
{"warmup-iters", 'w', "COUNT", 0,
"number of iterations before measurement; default: " xstr(
DEFAULT_WARMUP_ITERS),
0},
{0}};
typedef struct {
int child_cpu;
int parent_cpu;
int iters;
int repeats;
int warmup_iters;
} args;
static error_t parse_opt(int key, char *arg, struct argp_state *state) {
args *args = state->input;
switch (key) {
case 'c':
args->child_cpu = atoi(arg);
break;
case 'p':
args->parent_cpu = atoi(arg);
break;
case 'r':
args->repeats = atoi(arg);
break;
case 'i':
case 'n':
args->iters = atoi(arg);
break;
case 'w':
args->warmup_iters = atoi(arg);
break;
default:
return ARGP_ERR_UNKNOWN;
}
return 0;
}
long long elapsed_nsec(struct timespec start, struct timespec end) {
return (end.tv_sec - start.tv_sec) * 1000000000 +
(end.tv_nsec - start.tv_nsec);
}
static struct argp argp = {options, parse_opt, 0, 0, 0, 0, 0};
void sched_setaffinity_or_die(int cpu_id) {
if (cpu_id == DEFAULT_CPU) {
return;
}
cpu_set_t cpu_set;
CPU_ZERO(&cpu_set);
CPU_SET(cpu_id, &cpu_set);
if (sched_setaffinity(getpid(), sizeof(cpu_set_t), &cpu_set) == -1) {
perror("could not set CPU affinity");
exit(EXIT_FAILURE);
}
}
int main(int argc, char **argv) {
args args;
state *state = new_state();
args.child_cpu = DEFAULT_CPU;
args.parent_cpu = DEFAULT_CPU;
args.iters = DEFAULT_ITERS;
args.repeats = DEFAULT_MEASUREMENTS;
args.warmup_iters = DEFAULT_WARMUP_ITERS;
argp_parse(&argp, argc, argv, 0, 0, &args);
pre_fork_setup(state);
pid_t child = fork();
if (child == -1) {
perror("could not fork");
exit(EXIT_FAILURE);
} else if (child) {
long long *data = calloc(sizeof(long long), args.repeats);
sched_setaffinity_or_die(args.parent_cpu);
parent_post_fork_setup(state);
parent_warmup(args.warmup_iters, state);
struct timespec start, end;
for (int i = 0; i < args.repeats; ++i) {
clock_gettime(CLOCK_MONOTONIC, &start);
parent_loop(args.iters, state);
clock_gettime(CLOCK_MONOTONIC, &end);
data[i] = elapsed_nsec(start, end);
}
if (args.repeats == 1) {
long long elapsed = elapsed_nsec(start, end);
fprintf(stderr, "%d iters in %lld ns\n %f ns/iter\n", args.iters, data[0],
(double)elapsed / args.iters);
} else {
for (int i = 0; i < args.repeats; ++i) {
printf("%d\t%lld\n", args.iters, data[i]);
}
}
parent_cleanup(state);
kill(child, SIGTERM);
} else {
sched_setaffinity_or_die(args.child_cpu);
child_post_fork_setup(state);
child_warmup(args.warmup_iters, state);
child_loop(args.iters, state);
child_cleanup(state);
}
cleanup(state);
free_state(state);
return 0;
}