1df7cd654309bb90fe83fb823ff5c4f83c2a4afd0Julien Desprez#include "benchmark/benchmark_api.h"
26e61ea96f3d9765dd9cbf4fa248d90b856c5412aRiley Andrews#include <string>
36e61ea96f3d9765dd9cbf4fa248d90b856c5412aRiley Andrews#include <cstring>
46e61ea96f3d9765dd9cbf4fa248d90b856c5412aRiley Andrews#include <cstdlib>
56e61ea96f3d9765dd9cbf4fa248d90b856c5412aRiley Andrews#include <cstdio>
66e61ea96f3d9765dd9cbf4fa248d90b856c5412aRiley Andrews#include <iostream>
76e61ea96f3d9765dd9cbf4fa248d90b856c5412aRiley Andrews#include <vector>
86e61ea96f3d9765dd9cbf4fa248d90b856c5412aRiley Andrews#include <tuple>
96e61ea96f3d9765dd9cbf4fa248d90b856c5412aRiley Andrews
106e61ea96f3d9765dd9cbf4fa248d90b856c5412aRiley Andrews#include <unistd.h>
116e61ea96f3d9765dd9cbf4fa248d90b856c5412aRiley Andrews#include <sys/stat.h>
126e61ea96f3d9765dd9cbf4fa248d90b856c5412aRiley Andrews#include <fcntl.h>
136e61ea96f3d9765dd9cbf4fa248d90b856c5412aRiley Andrews#include <sys/mman.h>
146e61ea96f3d9765dd9cbf4fa248d90b856c5412aRiley Andrews
156e61ea96f3d9765dd9cbf4fa248d90b856c5412aRiley Andrewsusing namespace std;
16df7cd654309bb90fe83fb823ff5c4f83c2a4afd0Julien Desprezstatic const size_t pageSize = PAGE_SIZE;
17df7cd654309bb90fe83fb823ff5c4f83c2a4afd0Julien Desprezstatic size_t fsize = 1024 * (1ull << 20);
18df7cd654309bb90fe83fb823ff5c4f83c2a4afd0Julien Desprezstatic size_t pagesTotal = fsize / pageSize;
196e61ea96f3d9765dd9cbf4fa248d90b856c5412aRiley Andrews
206e61ea96f3d9765dd9cbf4fa248d90b856c5412aRiley Andrewsclass Fd {
216e61ea96f3d9765dd9cbf4fa248d90b856c5412aRiley Andrews    int m_fd = -1;
226e61ea96f3d9765dd9cbf4fa248d90b856c5412aRiley Andrewspublic:
236e61ea96f3d9765dd9cbf4fa248d90b856c5412aRiley Andrews    int get() { return m_fd; }
246e61ea96f3d9765dd9cbf4fa248d90b856c5412aRiley Andrews    void set(int fd) { m_fd = fd; }
256e61ea96f3d9765dd9cbf4fa248d90b856c5412aRiley Andrews    Fd() {}
266fbd1a930a0f53bd392259ad12dd94c45305d2c8Chih-Hung Hsieh    explicit Fd(int fd) : m_fd{fd} {}
276e61ea96f3d9765dd9cbf4fa248d90b856c5412aRiley Andrews    ~Fd() {
286e61ea96f3d9765dd9cbf4fa248d90b856c5412aRiley Andrews        if (m_fd >= 0)
296e61ea96f3d9765dd9cbf4fa248d90b856c5412aRiley Andrews            close(m_fd);
306e61ea96f3d9765dd9cbf4fa248d90b856c5412aRiley Andrews    }
316e61ea96f3d9765dd9cbf4fa248d90b856c5412aRiley Andrews};
326e61ea96f3d9765dd9cbf4fa248d90b856c5412aRiley Andrews
336e61ea96f3d9765dd9cbf4fa248d90b856c5412aRiley Andrewsint dummy = 0;
346e61ea96f3d9765dd9cbf4fa248d90b856c5412aRiley Andrews
356e61ea96f3d9765dd9cbf4fa248d90b856c5412aRiley Andrewsvoid fillPageJunk(void *ptr)
366e61ea96f3d9765dd9cbf4fa248d90b856c5412aRiley Andrews{
376e61ea96f3d9765dd9cbf4fa248d90b856c5412aRiley Andrews    uint64_t seed = (unsigned long long)rand() | ((unsigned long long)rand() << 32);
386e61ea96f3d9765dd9cbf4fa248d90b856c5412aRiley Andrews    uint64_t *target = (uint64_t*)ptr;
396e61ea96f3d9765dd9cbf4fa248d90b856c5412aRiley Andrews    for (int i = 0; i < pageSize / sizeof(uint64_t); i++) {
406e61ea96f3d9765dd9cbf4fa248d90b856c5412aRiley Andrews        *target = seed ^ (uint64_t)(uintptr_t)target;
416e61ea96f3d9765dd9cbf4fa248d90b856c5412aRiley Andrews        seed = (seed << 1) | ((seed >> 63) & 1);
426e61ea96f3d9765dd9cbf4fa248d90b856c5412aRiley Andrews        target++;
436e61ea96f3d9765dd9cbf4fa248d90b856c5412aRiley Andrews    }
446e61ea96f3d9765dd9cbf4fa248d90b856c5412aRiley Andrews}
456e61ea96f3d9765dd9cbf4fa248d90b856c5412aRiley Andrews
466e61ea96f3d9765dd9cbf4fa248d90b856c5412aRiley Andrewsclass FileMap {
476e61ea96f3d9765dd9cbf4fa248d90b856c5412aRiley Andrews    string m_name;
486e61ea96f3d9765dd9cbf4fa248d90b856c5412aRiley Andrews    size_t m_size;
496e61ea96f3d9765dd9cbf4fa248d90b856c5412aRiley Andrews    void *m_ptr = nullptr;
506e61ea96f3d9765dd9cbf4fa248d90b856c5412aRiley Andrews    Fd m_fileFd;
516e61ea96f3d9765dd9cbf4fa248d90b856c5412aRiley Andrewspublic:
526e61ea96f3d9765dd9cbf4fa248d90b856c5412aRiley Andrews    enum Hint {
536e61ea96f3d9765dd9cbf4fa248d90b856c5412aRiley Andrews       FILE_MAP_HINT_NONE,
546e61ea96f3d9765dd9cbf4fa248d90b856c5412aRiley Andrews       FILE_MAP_HINT_RAND,
556e61ea96f3d9765dd9cbf4fa248d90b856c5412aRiley Andrews       FILE_MAP_HINT_LINEAR,
566e61ea96f3d9765dd9cbf4fa248d90b856c5412aRiley Andrews    };
576e61ea96f3d9765dd9cbf4fa248d90b856c5412aRiley Andrews    FileMap(const string &name, size_t size, Hint hint = FILE_MAP_HINT_NONE) : m_name{name}, m_size{size} {
586e61ea96f3d9765dd9cbf4fa248d90b856c5412aRiley Andrews        int fd = open(name.c_str(), O_CREAT | O_RDWR, S_IRWXU);
596e61ea96f3d9765dd9cbf4fa248d90b856c5412aRiley Andrews        if (fd < 0) {
607d20a66002e9a42face7a331d8a72facaddab0faTodd Kjos            cout << "Error: open failed for " << name << ": " << strerror(errno) << endl;
617d20a66002e9a42face7a331d8a72facaddab0faTodd Kjos            exit(1);
626e61ea96f3d9765dd9cbf4fa248d90b856c5412aRiley Andrews        }
636e61ea96f3d9765dd9cbf4fa248d90b856c5412aRiley Andrews        m_fileFd.set(fd);
646e61ea96f3d9765dd9cbf4fa248d90b856c5412aRiley Andrews        fallocate(m_fileFd.get(), 0, 0, size);
656e61ea96f3d9765dd9cbf4fa248d90b856c5412aRiley Andrews        unlink(name.c_str());
666e61ea96f3d9765dd9cbf4fa248d90b856c5412aRiley Andrews        m_ptr = mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_SHARED, m_fileFd.get(), 0);
676e61ea96f3d9765dd9cbf4fa248d90b856c5412aRiley Andrews        if ((int)(uintptr_t)m_ptr == -1) {
687d20a66002e9a42face7a331d8a72facaddab0faTodd Kjos            cout << "Error: mmap failed: " << (int)(uintptr_t)m_ptr << ": " << strerror(errno) << endl;
697d20a66002e9a42face7a331d8a72facaddab0faTodd Kjos            exit(1);
706e61ea96f3d9765dd9cbf4fa248d90b856c5412aRiley Andrews        }
716e61ea96f3d9765dd9cbf4fa248d90b856c5412aRiley Andrews        switch (hint) {
726e61ea96f3d9765dd9cbf4fa248d90b856c5412aRiley Andrews        case FILE_MAP_HINT_NONE: break;
736e61ea96f3d9765dd9cbf4fa248d90b856c5412aRiley Andrews        case FILE_MAP_HINT_RAND:
746e61ea96f3d9765dd9cbf4fa248d90b856c5412aRiley Andrews            madvise(m_ptr, m_size, MADV_RANDOM);
756e61ea96f3d9765dd9cbf4fa248d90b856c5412aRiley Andrews            break;
766e61ea96f3d9765dd9cbf4fa248d90b856c5412aRiley Andrews        case FILE_MAP_HINT_LINEAR:
776e61ea96f3d9765dd9cbf4fa248d90b856c5412aRiley Andrews            madvise(m_ptr, m_size, MADV_SEQUENTIAL);
786e61ea96f3d9765dd9cbf4fa248d90b856c5412aRiley Andrews            break;
796e61ea96f3d9765dd9cbf4fa248d90b856c5412aRiley Andrews        }
806e61ea96f3d9765dd9cbf4fa248d90b856c5412aRiley Andrews        for (int i = 0; i < m_size / pageSize; i++) {
816e61ea96f3d9765dd9cbf4fa248d90b856c5412aRiley Andrews            uint8_t *targetPtr = (uint8_t*)m_ptr + 4096ull * i;
826e61ea96f3d9765dd9cbf4fa248d90b856c5412aRiley Andrews            fillPageJunk(targetPtr);
836e61ea96f3d9765dd9cbf4fa248d90b856c5412aRiley Andrews        }
846e61ea96f3d9765dd9cbf4fa248d90b856c5412aRiley Andrews    }
85df7cd654309bb90fe83fb823ff5c4f83c2a4afd0Julien Desprez    void benchRandomRead(unsigned int targetPage) {
86df7cd654309bb90fe83fb823ff5c4f83c2a4afd0Julien Desprez        uint8_t *targetPtr = (uint8_t*)m_ptr + pageSize * targetPage;
87df7cd654309bb90fe83fb823ff5c4f83c2a4afd0Julien Desprez        dummy += *targetPtr;
886e61ea96f3d9765dd9cbf4fa248d90b856c5412aRiley Andrews    }
89df7cd654309bb90fe83fb823ff5c4f83c2a4afd0Julien Desprez    void benchRandomWrite(unsigned int targetPage) {
90df7cd654309bb90fe83fb823ff5c4f83c2a4afd0Julien Desprez        uint8_t *targetPtr = (uint8_t*)m_ptr + pageSize * targetPage;
91df7cd654309bb90fe83fb823ff5c4f83c2a4afd0Julien Desprez        *targetPtr = dummy;
92df7cd654309bb90fe83fb823ff5c4f83c2a4afd0Julien Desprez    }
93df7cd654309bb90fe83fb823ff5c4f83c2a4afd0Julien Desprez    void benchLinearRead(unsigned int j) {
94df7cd654309bb90fe83fb823ff5c4f83c2a4afd0Julien Desprez        uint8_t *targetPtr = (uint8_t*)m_ptr + pageSize * j;
95df7cd654309bb90fe83fb823ff5c4f83c2a4afd0Julien Desprez        dummy += *targetPtr;
96df7cd654309bb90fe83fb823ff5c4f83c2a4afd0Julien Desprez    }
97df7cd654309bb90fe83fb823ff5c4f83c2a4afd0Julien Desprez    void benchLinearWrite(unsigned int j) {
98df7cd654309bb90fe83fb823ff5c4f83c2a4afd0Julien Desprez        uint8_t *targetPtr = (uint8_t*)m_ptr + pageSize * j;
99df7cd654309bb90fe83fb823ff5c4f83c2a4afd0Julien Desprez        *targetPtr = dummy;
1006e61ea96f3d9765dd9cbf4fa248d90b856c5412aRiley Andrews    }
1016e61ea96f3d9765dd9cbf4fa248d90b856c5412aRiley Andrews    void dropCache() {
1026e61ea96f3d9765dd9cbf4fa248d90b856c5412aRiley Andrews        int ret1 = msync(m_ptr, m_size, MS_SYNC | MS_INVALIDATE);
1036e61ea96f3d9765dd9cbf4fa248d90b856c5412aRiley Andrews        madvise(m_ptr, m_size, MADV_DONTNEED);
1046e61ea96f3d9765dd9cbf4fa248d90b856c5412aRiley Andrews        (void)ret1;
1056e61ea96f3d9765dd9cbf4fa248d90b856c5412aRiley Andrews    }
1066e61ea96f3d9765dd9cbf4fa248d90b856c5412aRiley Andrews    ~FileMap() {
1076e61ea96f3d9765dd9cbf4fa248d90b856c5412aRiley Andrews        if (m_ptr)
1086e61ea96f3d9765dd9cbf4fa248d90b856c5412aRiley Andrews            munmap(m_ptr, m_size);
1096e61ea96f3d9765dd9cbf4fa248d90b856c5412aRiley Andrews    }
1106e61ea96f3d9765dd9cbf4fa248d90b856c5412aRiley Andrews
1116e61ea96f3d9765dd9cbf4fa248d90b856c5412aRiley Andrews};
1126e61ea96f3d9765dd9cbf4fa248d90b856c5412aRiley Andrews
113df7cd654309bb90fe83fb823ff5c4f83c2a4afd0Julien Desprezstatic void benchRandomRead(benchmark::State& state) {
114df7cd654309bb90fe83fb823ff5c4f83c2a4afd0Julien Desprez    FileMap file{"/data/local/tmp/mmap_test", fsize};
115df7cd654309bb90fe83fb823ff5c4f83c2a4afd0Julien Desprez    while (state.KeepRunning()) {
116df7cd654309bb90fe83fb823ff5c4f83c2a4afd0Julien Desprez        unsigned int targetPage = rand() % pagesTotal;
117df7cd654309bb90fe83fb823ff5c4f83c2a4afd0Julien Desprez        file.benchRandomRead(targetPage);
1187d20a66002e9a42face7a331d8a72facaddab0faTodd Kjos    }
119df7cd654309bb90fe83fb823ff5c4f83c2a4afd0Julien Desprez    state.SetBytesProcessed(state.iterations() * pageSize);
120df7cd654309bb90fe83fb823ff5c4f83c2a4afd0Julien Desprez}
121df7cd654309bb90fe83fb823ff5c4f83c2a4afd0Julien DesprezBENCHMARK(benchRandomRead);
1227d20a66002e9a42face7a331d8a72facaddab0faTodd Kjos
123df7cd654309bb90fe83fb823ff5c4f83c2a4afd0Julien Desprezstatic void benchRandomWrite(benchmark::State& state) {
124df7cd654309bb90fe83fb823ff5c4f83c2a4afd0Julien Desprez    FileMap file{"/data/local/tmp/mmap_test", fsize};
125df7cd654309bb90fe83fb823ff5c4f83c2a4afd0Julien Desprez    while (state.KeepRunning()) {
126df7cd654309bb90fe83fb823ff5c4f83c2a4afd0Julien Desprez        unsigned int targetPage = rand() % pagesTotal;
127df7cd654309bb90fe83fb823ff5c4f83c2a4afd0Julien Desprez        file.benchRandomWrite(targetPage);
1287d20a66002e9a42face7a331d8a72facaddab0faTodd Kjos    }
129df7cd654309bb90fe83fb823ff5c4f83c2a4afd0Julien Desprez    state.SetBytesProcessed(state.iterations() * pageSize);
130df7cd654309bb90fe83fb823ff5c4f83c2a4afd0Julien Desprez}
131df7cd654309bb90fe83fb823ff5c4f83c2a4afd0Julien DesprezBENCHMARK(benchRandomWrite);
1327d20a66002e9a42face7a331d8a72facaddab0faTodd Kjos
133df7cd654309bb90fe83fb823ff5c4f83c2a4afd0Julien Desprezstatic void benchLinearRead(benchmark::State& state) {
134df7cd654309bb90fe83fb823ff5c4f83c2a4afd0Julien Desprez   FileMap file{"/data/local/tmp/mmap_test", fsize};
135df7cd654309bb90fe83fb823ff5c4f83c2a4afd0Julien Desprez   unsigned int j = 0;
136df7cd654309bb90fe83fb823ff5c4f83c2a4afd0Julien Desprez   while (state.KeepRunning()) {
137df7cd654309bb90fe83fb823ff5c4f83c2a4afd0Julien Desprez       file.benchLinearRead(j);
138df7cd654309bb90fe83fb823ff5c4f83c2a4afd0Julien Desprez       j = (j + 1) % pagesTotal;
139df7cd654309bb90fe83fb823ff5c4f83c2a4afd0Julien Desprez   }
140df7cd654309bb90fe83fb823ff5c4f83c2a4afd0Julien Desprez   state.SetBytesProcessed(state.iterations() * pageSize);
141df7cd654309bb90fe83fb823ff5c4f83c2a4afd0Julien Desprez}
142df7cd654309bb90fe83fb823ff5c4f83c2a4afd0Julien DesprezBENCHMARK(benchLinearRead);
143df7cd654309bb90fe83fb823ff5c4f83c2a4afd0Julien Desprez
144df7cd654309bb90fe83fb823ff5c4f83c2a4afd0Julien Desprezstatic void benchLinearWrite(benchmark::State& state) {
145df7cd654309bb90fe83fb823ff5c4f83c2a4afd0Julien Desprez   FileMap file{"/data/local/tmp/mmap_test", fsize};
146df7cd654309bb90fe83fb823ff5c4f83c2a4afd0Julien Desprez   unsigned int j = 0;
147df7cd654309bb90fe83fb823ff5c4f83c2a4afd0Julien Desprez   while (state.KeepRunning()) {
148df7cd654309bb90fe83fb823ff5c4f83c2a4afd0Julien Desprez       file.benchLinearWrite(j);
149df7cd654309bb90fe83fb823ff5c4f83c2a4afd0Julien Desprez       j = (j + 1) % pagesTotal;
150df7cd654309bb90fe83fb823ff5c4f83c2a4afd0Julien Desprez   }
151df7cd654309bb90fe83fb823ff5c4f83c2a4afd0Julien Desprez   state.SetBytesProcessed(state.iterations() * pageSize);
1526e61ea96f3d9765dd9cbf4fa248d90b856c5412aRiley Andrews}
153df7cd654309bb90fe83fb823ff5c4f83c2a4afd0Julien DesprezBENCHMARK(benchLinearWrite);
154df7cd654309bb90fe83fb823ff5c4f83c2a4afd0Julien Desprez
155df7cd654309bb90fe83fb823ff5c4f83c2a4afd0Julien DesprezBENCHMARK_MAIN()
156