diff options
Diffstat (limited to 'paging_speed.c')
-rw-r--r-- | paging_speed.c | 137 |
1 files changed, 137 insertions, 0 deletions
diff --git a/paging_speed.c b/paging_speed.c new file mode 100644 index 0000000..4ad56e2 --- /dev/null +++ b/paging_speed.c | |||
@@ -0,0 +1,137 @@ | |||
1 | #define _GNU_SOURCE | ||
2 | |||
3 | #include <sys/mman.h> | ||
4 | #include <sys/types.h> | ||
5 | #include <sys/stat.h> | ||
6 | #include <fcntl.h> | ||
7 | #include <stdio.h> | ||
8 | #include <stdint.h> | ||
9 | #include <time.h> | ||
10 | #include <unistd.h> | ||
11 | #include <stdlib.h> | ||
12 | #include <string.h> // strlen() | ||
13 | |||
14 | #define GiB 1024l*1024l*1024l | ||
15 | #define s2ns(s) ((s)*1000l*1000l*1000l) | ||
16 | |||
17 | |||
18 | int seq_walk(char* mem, int len, char to_find) { | ||
19 | int num_42 = 0; | ||
20 | // Stride of 4096 bytes (one 4k page) | ||
21 | for (int i = 4096; i < len; i += 4096) | ||
22 | if (mem[i] == to_find) | ||
23 | num_42++; | ||
24 | return num_42; | ||
25 | } | ||
26 | |||
27 | long time_diff_ns(struct timespec start, struct timespec stop) { | ||
28 | return (s2ns(stop.tv_sec) + stop.tv_nsec) - (s2ns(start.tv_sec) + start.tv_nsec); | ||
29 | } | ||
30 | // TODO: take *num_42, return time | ||
31 | |||
32 | //#define PAGED_FILE "/home/jbakita/1gib_random_f" | ||
33 | #define PAGED_FILE "/dev/nvme0n1" | ||
34 | |||
35 | int main(int argc, char **argv) { | ||
36 | int iters = 1; | ||
37 | int no_seq = 0; | ||
38 | if (argc > 1) | ||
39 | iters = atoi(argv[1]); | ||
40 | if (argc > 2) { | ||
41 | no_seq = strncmp(argv[2], "--no-seq", strlen(argv[2])) ? 1 : 0; | ||
42 | fprintf(stderr, "Skipping seq, but using no-seq emulation with demand paging\n"); | ||
43 | } | ||
44 | struct timespec start, stop, seq_stop; | ||
45 | int clear_fd = open("/proc/sys/vm/drop_caches", O_WRONLY); | ||
46 | if (clear_fd == -1) { | ||
47 | perror("Unable to open /proc/sys/vm/drop_caches"); | ||
48 | return 1; | ||
49 | } | ||
50 | |||
51 | char clear_cmd = '3'; | ||
52 | for (int i = 0; i < iters; i++) { | ||
53 | int fd = open(PAGED_FILE, O_RDWR); | ||
54 | if (fd == -1) { | ||
55 | perror("Unable to open " PAGED_FILE); | ||
56 | return 1; | ||
57 | } | ||
58 | // Clear page cache | ||
59 | write(clear_fd, &clear_cmd, 1); | ||
60 | // VIA MMAP | ||
61 | clock_gettime(CLOCK_MONOTONIC_RAW, &start); | ||
62 | char* mem = mmap(NULL, GiB, PROT_READ, MAP_PRIVATE, fd, 0); | ||
63 | if (mem == MAP_FAILED) { | ||
64 | perror("Unable to mmap " PAGED_FILE); | ||
65 | return 1; | ||
66 | } | ||
67 | // Fault on all the pages via a sequential walk | ||
68 | int num_42 = seq_walk(mem, GiB, 42); | ||
69 | clock_gettime(CLOCK_MONOTONIC_RAW, &stop); | ||
70 | int num_52 = 0; | ||
71 | if (no_seq) | ||
72 | num_52 = seq_walk(mem, GiB, 52); | ||
73 | clock_gettime(CLOCK_MONOTONIC_RAW, &seq_stop); | ||
74 | if (num_52) | ||
75 | fprintf(stderr, "Something is seriously wrong! Found a 52 in a buffer that should be only 42s\n"); | ||
76 | long duration = (s2ns(stop.tv_sec) + stop.tv_nsec) - (s2ns(start.tv_sec) + start.tv_nsec); | ||
77 | // Emulate the time demand paging would take if we didn't have to walk | ||
78 | if (no_seq) { | ||
79 | long seq_time = time_diff_ns(stop, seq_stop); | ||
80 | duration -= seq_time; | ||
81 | } | ||
82 | if (iters == 1) { | ||
83 | printf("Took %ldus via mmap\n", duration / 1000); | ||
84 | printf("Read %d 42s of %ld expected\n", num_42, GiB/4096); | ||
85 | } else { | ||
86 | printf("%ld, ", duration / 1000); | ||
87 | } | ||
88 | munmap(mem, GiB); | ||
89 | close(fd); | ||
90 | } | ||
91 | if (iters > 1) | ||
92 | printf("\n"); | ||
93 | |||
94 | for (int i = 0; i < iters; i++) { | ||
95 | char* mem; | ||
96 | int fd = open(PAGED_FILE, O_RDWR | O_DIRECT); | ||
97 | if (fd == -1) { | ||
98 | perror("Unable to open " PAGED_FILE); | ||
99 | return 1; | ||
100 | } | ||
101 | // Clear page cache | ||
102 | write(clear_fd, &clear_cmd, 1); | ||
103 | // VIA READ | ||
104 | clock_gettime(CLOCK_MONOTONIC_RAW, &start); | ||
105 | // Aligned malloc(GiB) basicially | ||
106 | int res = posix_memalign((void**)&mem, 4096, GiB); | ||
107 | if (res) { | ||
108 | fprintf(stderr, "posix_memalign() failure. Error %d.", res); | ||
109 | return 1; | ||
110 | } | ||
111 | res = read(fd, mem, GiB); | ||
112 | if (res == -1) { | ||
113 | perror("Unable to read 1GiB from /dev/nvme0n1"); | ||
114 | return 1; | ||
115 | } | ||
116 | if (res < GiB) { | ||
117 | fprintf(stderr, "Unable to read the buffer all at once!"); | ||
118 | return 2; | ||
119 | } | ||
120 | int num_42 = 0; | ||
121 | if (!no_seq) | ||
122 | num_42 = seq_walk(mem, GiB, 42); // Not strictly necessary, but to match mmap path overheads | ||
123 | clock_gettime(CLOCK_MONOTONIC_RAW, &stop); | ||
124 | if (iters == 1) { | ||
125 | printf("Took %ldus via read\n", ((s2ns(stop.tv_sec) + stop.tv_nsec) - (s2ns(start.tv_sec) + start.tv_nsec)) / 1000); | ||
126 | if (!no_seq) | ||
127 | printf("Read %d 42s of %ld expected\n", num_42, GiB/4096); | ||
128 | } else { | ||
129 | printf("%ld, ", ((s2ns(stop.tv_sec) + stop.tv_nsec) - (s2ns(start.tv_sec) + start.tv_nsec)) / 1000); | ||
130 | } | ||
131 | close(fd); | ||
132 | free(mem); | ||
133 | } | ||
134 | if (iters > 1) | ||
135 | printf("\n"); | ||
136 | return 0; | ||
137 | } | ||