Open4

mincore()

zulinx86zulinx86

実験1

mmap() したファイルの中身がメモリ上にあるのかどうかを確認する。

mmap() については、こちらを参照。

実行結果

$ truncate -s 1000K sample.txt
$ sync; echo 3 | sudo tee /proc/sys/vm/drop_caches
$ ./a.out
Opened file sample.txt
File size is 1024000 bytes
Mapped file into memory: 0x7f1e45a52000
Number of pages: 250
Touching page #133...
Page #0 is not resident in memory.
Page #10 is not resident in memory.
Page #20 is not resident in memory.
Page #30 is not resident in memory.
Page #40 is not resident in memory.
Page #50 is not resident in memory.
Page #60 is not resident in memory.
Page #70 is not resident in memory.
Page #80 is not resident in memory.
Page #90 is not resident in memory.
Page #100 is not resident in memory.
Page #110 is not resident in memory.
Page #120 is resident in memory.
Page #130 is resident in memory.
Page #140 is resident in memory.
Page #150 is not resident in memory.
Page #160 is not resident in memory.
Page #170 is not resident in memory.
Page #180 is not resident in memory.
Page #190 is not resident in memory.
Page #200 is not resident in memory.
Page #210 is not resident in memory.
Page #220 is not resident in memory.
Page #230 is not resident in memory.
Page #240 is not resident in memory.

触った Page #133 の前後のページが、メモリに乗ってることがわかる。

コード

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/mman.h>

int main() {
	// Open a file.
	const char *filepath = "sample.txt";
	int fd = open(filepath, O_RDWR);
	if (fd == -1) {
		perror("open");
		exit(EXIT_FAILURE);
	}
	printf("Opened file %s\n", filepath);

	// Get the file size.
	struct stat st;
	if (fstat(fd, &st) == -1) {
		perror("fstat");
		close(fd);
		exit(EXIT_FAILURE);
	}
	printf("File size is %ld bytes\n", st.st_size);

	// Map the file into memory.
	char *addr = mmap(NULL, st.st_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
	if (addr == MAP_FAILED) {
		perror("mmap");
		close(fd);
		exit(EXIT_FAILURE);
	}
	printf("Mapped file into memory: %p\n", addr);

	// Calculate the number of pages.
	long page_size = sysconf(_SC_PAGESIZE);
	unsigned int num_pages = (st.st_size + page_size - 1) / page_size;
	printf("Number of pages: %d\n", num_pages);

	// Touch a page.
	unsigned int idx = rand() % num_pages;
	printf("Touching page #%d...\n", idx);
	addr[idx * page_size] = 'a';

	// Check the residency of pages.
	unsigned char *vec = calloc(num_pages, sizeof(unsigned char));
	if (vec == NULL) {
		perror("malloc");
		munmap(addr, st.st_size);
		close(fd);
		exit(EXIT_FAILURE);
	}
	if (mincore(addr, st.st_size, vec) == -1) {
		perror("mincore");
		free(vec);
		munmap(addr, st.st_size);
		close(fd);
		exit(EXIT_FAILURE);
	}

	// Print the residency of each page.
	for (unsigned int i = 0; i < num_pages; i += 10) {
		if (vec[i] & 0x1) {
			printf("Page #%d is resident in memory.\n", i);
		} else {
			printf("Page #%d is not resident in memory.\n", i);
		}
	}

	// Clean up.
	free(vec);
	munmap(addr, st.st_size);
	close(fd);
	return 0;
}
zulinx86zulinx86

実験2

ページにタッチする部分を消した別の実行ファイル (b.out) を作って、a.out の終了後もファイルのキャッシュがメモリ上に残っていることを確認する。

$ truncate -s 1000K sample.txt
$ sync; echo 3 | sudo tee /proc/sys/vm/drop_caches

$ ./b.out
Opened file sample.txt
File size is 1024000 bytes
Mapped file into memory: 0x7f72677ef000
Number of pages: 250
Page #0 is not resident in memory.
Page #10 is not resident in memory.
Page #20 is not resident in memory.
Page #30 is not resident in memory.
Page #40 is not resident in memory.
Page #50 is not resident in memory.
Page #60 is not resident in memory.
Page #70 is not resident in memory.
Page #80 is not resident in memory.
Page #90 is not resident in memory.
Page #100 is not resident in memory.
Page #110 is not resident in memory.
Page #120 is not resident in memory.
Page #130 is not resident in memory.
Page #140 is not resident in memory.
Page #150 is not resident in memory.
Page #160 is not resident in memory.
Page #170 is not resident in memory.
Page #180 is not resident in memory.
Page #190 is not resident in memory.
Page #200 is not resident in memory.
Page #210 is not resident in memory.
Page #220 is not resident in memory.
Page #230 is not resident in memory.
Page #240 is not resident in memory.

$ ./a.out
Opened file sample.txt
File size is 1024000 bytes
Mapped file into memory: 0x7f9c5c881000
Number of pages: 250
Touching page #133...
Page #0 is not resident in memory.
Page #10 is not resident in memory.
Page #20 is not resident in memory.
Page #30 is not resident in memory.
Page #40 is not resident in memory.
Page #50 is not resident in memory.
Page #60 is not resident in memory.
Page #70 is not resident in memory.
Page #80 is not resident in memory.
Page #90 is not resident in memory.
Page #100 is not resident in memory.
Page #110 is not resident in memory.
Page #120 is resident in memory.
Page #130 is resident in memory.
Page #140 is resident in memory.
Page #150 is not resident in memory.
Page #160 is not resident in memory.
Page #170 is not resident in memory.
Page #180 is not resident in memory.
Page #190 is not resident in memory.
Page #200 is not resident in memory.
Page #210 is not resident in memory.
Page #220 is not resident in memory.
Page #230 is not resident in memory.
Page #240 is not resident in memory.

$ ./b.out
Opened file sample.txt
File size is 1024000 bytes
Mapped file into memory: 0x7fe3bc88c000
Number of pages: 250
Page #0 is not resident in memory.
Page #10 is not resident in memory.
Page #20 is not resident in memory.
Page #30 is not resident in memory.
Page #40 is not resident in memory.
Page #50 is not resident in memory.
Page #60 is not resident in memory.
Page #70 is not resident in memory.
Page #80 is not resident in memory.
Page #90 is not resident in memory.
Page #100 is not resident in memory.
Page #110 is not resident in memory.
Page #120 is resident in memory.
Page #130 is resident in memory.
Page #140 is resident in memory.
Page #150 is not resident in memory.
Page #160 is not resident in memory.
Page #170 is not resident in memory.
Page #180 is not resident in memory.
Page #190 is not resident in memory.
Page #200 is not resident in memory.
Page #210 is not resident in memory.
Page #220 is not resident in memory.
Page #230 is not resident in memory.
Page #240 is not resident in memory.
zulinx86zulinx86

実験3

アクセス速度がどれくらい違うか、計測してみる。

$ truncate -s 1000K sample.txt
$ sync; echo 3 | sudo tee /proc/sys/vm/drop_caches

$ ./a.out
Opened file sample.txt
File size is 1024000 bytes
Mapped file into memory: 0x7f8dc8c43000
Number of pages: 250
Touching page #133...
Page #0 is not resident in memory.
Page #40 is not resident in memory.
Page #80 is not resident in memory.
Page #120 is resident in memory.
Page #160 is not resident in memory.
Page #200 is not resident in memory.
Page #240 is not resident in memory.
Time taken to access page #0: 30068 ns
Time taken to access page #40: 18474 ns
Time taken to access page #80: 26352 ns
Time taken to access page #120: 988 ns
Time taken to access page #160: 21035 ns
Time taken to access page #200: 26701 ns
Time taken to access page #240: 19469 ns

タッチした page #133 付近の page #120 だけアクセス速度がかなり良いのがわかる。

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <time.h>

int main() {
	// Open a file.
	const char *filepath = "sample.txt";
	int fd = open(filepath, O_RDWR);
	if (fd == -1) {
		perror("open");
		exit(EXIT_FAILURE);
	}
	printf("Opened file %s\n", filepath);

	// Get the file size.
	struct stat st;
	if (fstat(fd, &st) == -1) {
		perror("fstat");
		close(fd);
		exit(EXIT_FAILURE);
	}
	printf("File size is %ld bytes\n", st.st_size);

	// Map the file into memory.
	char *addr = mmap(NULL, st.st_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
	if (addr == MAP_FAILED) {
		perror("mmap");
		close(fd);
		exit(EXIT_FAILURE);
	}
	printf("Mapped file into memory: %p\n", addr);

	// Calculate the number of pages.
	long page_size = sysconf(_SC_PAGESIZE);
	unsigned int num_pages = (st.st_size + page_size - 1) / page_size;
	printf("Number of pages: %d\n", num_pages);

	// Touch a page.
	unsigned int idx = rand() % num_pages;
	printf("Touching page #%d...\n", idx);
	addr[idx * page_size] = 'a';

	// Check the residency of pages.
	unsigned char *vec = calloc(num_pages, sizeof(unsigned char));
	if (vec == NULL) {
		perror("malloc");
		munmap(addr, st.st_size);
		close(fd);
		exit(EXIT_FAILURE);
	}
	if (mincore(addr, st.st_size, vec) == -1) {
		perror("mincore");
		free(vec);
		munmap(addr, st.st_size);
		close(fd);
		exit(EXIT_FAILURE);
	}

	// Check residency of each page.
	for (unsigned int i = 0; i < num_pages; i += 40) {
		if (vec[i] & 0x1) {
			printf("Page #%d is resident in memory.\n", i);
		} else {
			printf("Page #%d is not resident in memory.\n", i);
		}
	}

	// Measure time taken to access data.
	for (unsigned int i = 0; i < num_pages; i += 40) {
		struct timespec start;
		clock_gettime(CLOCK_MONOTONIC, &start);
		char _ = addr[i * page_size];
		struct timespec end;
		clock_gettime(CLOCK_MONOTONIC, &end);
		long long elapsed = (end.tv_sec - start.tv_sec) * 1000000000LL + (end.tv_nsec - start.tv_nsec);
		printf("Time taken to access page #%d: %lld ns\n", i, elapsed);
	}

	// Clean up.
	free(vec);
	munmap(addr, st.st_size);
	close(fd);
	return 0;
}