summaryrefslogtreecommitdiffstats
path: root/paging_speed.c
blob: 4ad56e295cd842054fb3d911cc6ed93f6450f1d9 (plain) (blame)
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
#define _GNU_SOURCE

#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdint.h>
#include <time.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h> // strlen()

#define GiB 1024l*1024l*1024l
#define s2ns(s) ((s)*1000l*1000l*1000l)


int seq_walk(char* mem, int len, char to_find) {
	int num_42 = 0;
	// Stride of 4096 bytes (one 4k page)
	for (int i = 4096; i < len; i += 4096)
		if (mem[i] == to_find)
			num_42++;
	return num_42;
}

long time_diff_ns(struct timespec start, struct timespec stop) {
	return (s2ns(stop.tv_sec) + stop.tv_nsec) - (s2ns(start.tv_sec) + start.tv_nsec);
}
// TODO: take *num_42, return time

//#define PAGED_FILE "/home/jbakita/1gib_random_f"
#define PAGED_FILE "/dev/nvme0n1"

int main(int argc, char **argv) {
	int iters = 1;
	int no_seq = 0;
	if (argc > 1)
		iters = atoi(argv[1]);
	if (argc > 2) {
		no_seq = strncmp(argv[2], "--no-seq", strlen(argv[2])) ? 1 : 0;
		fprintf(stderr, "Skipping seq, but using no-seq emulation with demand paging\n");
	}
	struct timespec start, stop, seq_stop;
	int clear_fd = open("/proc/sys/vm/drop_caches", O_WRONLY);
	if (clear_fd == -1) {
		perror("Unable to open /proc/sys/vm/drop_caches");
		return 1;
	}

	char clear_cmd = '3';
	for (int i = 0; i < iters; i++) {
		int fd = open(PAGED_FILE, O_RDWR);
		if (fd == -1) {
			perror("Unable to open " PAGED_FILE);
			return 1;
		}
		// Clear page cache
		write(clear_fd, &clear_cmd, 1);
		// VIA MMAP
		clock_gettime(CLOCK_MONOTONIC_RAW, &start);
		char* mem = mmap(NULL, GiB, PROT_READ, MAP_PRIVATE, fd, 0);
		if (mem == MAP_FAILED) {
			perror("Unable to mmap " PAGED_FILE);
			return 1;
		}
		// Fault on all the pages via a sequential walk
		int num_42 = seq_walk(mem, GiB, 42);
		clock_gettime(CLOCK_MONOTONIC_RAW, &stop);
		int num_52 = 0;
		if (no_seq)
			num_52 = seq_walk(mem, GiB, 52);
		clock_gettime(CLOCK_MONOTONIC_RAW, &seq_stop);
		if (num_52)
			fprintf(stderr, "Something is seriously wrong! Found a 52 in a buffer that should be only 42s\n");
		long duration = (s2ns(stop.tv_sec) + stop.tv_nsec) - (s2ns(start.tv_sec) + start.tv_nsec);
		// Emulate the time demand paging would take if we didn't have to walk
		if (no_seq) {
			long seq_time = time_diff_ns(stop, seq_stop);
			duration -= seq_time;
		}
		if (iters == 1) {
			printf("Took %ldus via mmap\n", duration / 1000);
			printf("Read %d 42s of %ld expected\n", num_42, GiB/4096);
		} else {
			printf("%ld, ", duration / 1000);
		}
		munmap(mem, GiB);
		close(fd);
	}
	if (iters > 1)
		printf("\n");

	for (int i = 0; i < iters; i++) {
		char* mem;
		int fd = open(PAGED_FILE, O_RDWR | O_DIRECT);
		if (fd == -1) {
			perror("Unable to open " PAGED_FILE);
			return 1;
		}
		// Clear page cache
		write(clear_fd, &clear_cmd, 1);
		// VIA READ
		clock_gettime(CLOCK_MONOTONIC_RAW, &start);
		// Aligned malloc(GiB) basicially
		int res = posix_memalign((void**)&mem, 4096, GiB);
		if (res) {
			fprintf(stderr, "posix_memalign() failure. Error %d.", res);
			return 1;
		}
		res = read(fd, mem, GiB);
		if (res == -1) {
			perror("Unable to read 1GiB from /dev/nvme0n1");
			return 1;
		}
		if (res < GiB) {
			fprintf(stderr, "Unable to read the buffer all at once!");
			return 2;
		}
		int num_42 = 0;
		if (!no_seq)
			num_42 = seq_walk(mem, GiB, 42); // Not strictly necessary, but to match mmap path overheads
		clock_gettime(CLOCK_MONOTONIC_RAW, &stop);
		if (iters == 1) {
			printf("Took %ldus via read\n", ((s2ns(stop.tv_sec) + stop.tv_nsec) - (s2ns(start.tv_sec) + start.tv_nsec)) / 1000);
			if (!no_seq)
				printf("Read %d 42s of %ld expected\n", num_42, GiB/4096);
		} else {
			printf("%ld, ", ((s2ns(stop.tv_sec) + stop.tv_nsec) - (s2ns(start.tv_sec) + start.tv_nsec)) / 1000);
		}
		close(fd);
		free(mem);
	}
	if (iters > 1)
		printf("\n");
	return 0;
}