1#include "benchmark/benchmark_api.h"
2#include <string>
3#include <cstring>
4#include <cstdlib>
5#include <cstdio>
6#include <iostream>
7#include <vector>
8#include <tuple>
9
10#include <unistd.h>
11#include <sys/stat.h>
12#include <fcntl.h>
13#include <sys/mman.h>
14
15using namespace std;
16static const size_t pageSize = PAGE_SIZE;
17static size_t fsize = 1024 * (1ull << 20);
18static size_t pagesTotal = fsize / pageSize;
19
20class Fd {
21    int m_fd = -1;
22public:
23    int get() { return m_fd; }
24    void set(int fd) { m_fd = fd; }
25    Fd() {}
26    explicit Fd(int fd) : m_fd{fd} {}
27    ~Fd() {
28        if (m_fd >= 0)
29            close(m_fd);
30    }
31};
32
33int dummy = 0;
34
35void fillPageJunk(void *ptr)
36{
37    uint64_t seed = (unsigned long long)rand() | ((unsigned long long)rand() << 32);
38    uint64_t *target = (uint64_t*)ptr;
39    for (int i = 0; i < pageSize / sizeof(uint64_t); i++) {
40        *target = seed ^ (uint64_t)(uintptr_t)target;
41        seed = (seed << 1) | ((seed >> 63) & 1);
42        target++;
43    }
44}
45
46class FileMap {
47    string m_name;
48    size_t m_size;
49    void *m_ptr = nullptr;
50    Fd m_fileFd;
51public:
52    enum Hint {
53       FILE_MAP_HINT_NONE,
54       FILE_MAP_HINT_RAND,
55       FILE_MAP_HINT_LINEAR,
56    };
57    FileMap(const string &name, size_t size, Hint hint = FILE_MAP_HINT_NONE) : m_name{name}, m_size{size} {
58        int fd = open(name.c_str(), O_CREAT | O_RDWR, S_IRWXU);
59        if (fd < 0) {
60            cout << "Error: open failed for " << name << ": " << strerror(errno) << endl;
61            exit(1);
62        }
63        m_fileFd.set(fd);
64        fallocate(m_fileFd.get(), 0, 0, size);
65        unlink(name.c_str());
66        m_ptr = mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_SHARED, m_fileFd.get(), 0);
67        if ((int)(uintptr_t)m_ptr == -1) {
68            cout << "Error: mmap failed: " << (int)(uintptr_t)m_ptr << ": " << strerror(errno) << endl;
69            exit(1);
70        }
71        switch (hint) {
72        case FILE_MAP_HINT_NONE: break;
73        case FILE_MAP_HINT_RAND:
74            madvise(m_ptr, m_size, MADV_RANDOM);
75            break;
76        case FILE_MAP_HINT_LINEAR:
77            madvise(m_ptr, m_size, MADV_SEQUENTIAL);
78            break;
79        }
80        for (int i = 0; i < m_size / pageSize; i++) {
81            uint8_t *targetPtr = (uint8_t*)m_ptr + 4096ull * i;
82            fillPageJunk(targetPtr);
83        }
84    }
85    void benchRandomRead(unsigned int targetPage) {
86        uint8_t *targetPtr = (uint8_t*)m_ptr + pageSize * targetPage;
87        dummy += *targetPtr;
88    }
89    void benchRandomWrite(unsigned int targetPage) {
90        uint8_t *targetPtr = (uint8_t*)m_ptr + pageSize * targetPage;
91        *targetPtr = dummy;
92    }
93    void benchLinearRead(unsigned int j) {
94        uint8_t *targetPtr = (uint8_t*)m_ptr + pageSize * j;
95        dummy += *targetPtr;
96    }
97    void benchLinearWrite(unsigned int j) {
98        uint8_t *targetPtr = (uint8_t*)m_ptr + pageSize * j;
99        *targetPtr = dummy;
100    }
101    void dropCache() {
102        int ret1 = msync(m_ptr, m_size, MS_SYNC | MS_INVALIDATE);
103        madvise(m_ptr, m_size, MADV_DONTNEED);
104        (void)ret1;
105    }
106    ~FileMap() {
107        if (m_ptr)
108            munmap(m_ptr, m_size);
109    }
110
111};
112
113static void benchRandomRead(benchmark::State& state) {
114    FileMap file{"/data/local/tmp/mmap_test", fsize};
115    while (state.KeepRunning()) {
116        unsigned int targetPage = rand() % pagesTotal;
117        file.benchRandomRead(targetPage);
118    }
119    state.SetBytesProcessed(state.iterations() * pageSize);
120}
121BENCHMARK(benchRandomRead);
122
123static void benchRandomWrite(benchmark::State& state) {
124    FileMap file{"/data/local/tmp/mmap_test", fsize};
125    while (state.KeepRunning()) {
126        unsigned int targetPage = rand() % pagesTotal;
127        file.benchRandomWrite(targetPage);
128    }
129    state.SetBytesProcessed(state.iterations() * pageSize);
130}
131BENCHMARK(benchRandomWrite);
132
133static void benchLinearRead(benchmark::State& state) {
134   FileMap file{"/data/local/tmp/mmap_test", fsize};
135   unsigned int j = 0;
136   while (state.KeepRunning()) {
137       file.benchLinearRead(j);
138       j = (j + 1) % pagesTotal;
139   }
140   state.SetBytesProcessed(state.iterations() * pageSize);
141}
142BENCHMARK(benchLinearRead);
143
144static void benchLinearWrite(benchmark::State& state) {
145   FileMap file{"/data/local/tmp/mmap_test", fsize};
146   unsigned int j = 0;
147   while (state.KeepRunning()) {
148       file.benchLinearWrite(j);
149       j = (j + 1) % pagesTotal;
150   }
151   state.SetBytesProcessed(state.iterations() * pageSize);
152}
153BENCHMARK(benchLinearWrite);
154
155BENCHMARK_MAIN()
156