Riley Andrews | 1199c51 | 2015-11-20 15:40:09 -0800 | [diff] [blame] | 1 | #include <fcntl.h> |
| 2 | #include <linux/fs.h> |
Martin Liu | 2b076cd | 2018-11-19 15:02:46 +0800 | [diff] [blame] | 3 | #include <sys/stat.h> |
Riley Andrews | 1199c51 | 2015-11-20 15:40:09 -0800 | [diff] [blame] | 4 | #include <sys/swap.h> |
Martin Liu | 2b076cd | 2018-11-19 15:02:46 +0800 | [diff] [blame] | 5 | #include <sys/types.h> |
| 6 | #include <unistd.h> |
| 7 | #include <chrono> |
| 8 | #include <iostream> |
| 9 | #include <numeric> |
| 10 | #include <vector> |
Riley Andrews | 1199c51 | 2015-11-20 15:40:09 -0800 | [diff] [blame] | 11 | |
| 12 | using namespace std; |
| 13 | |
Martin Liu | 2b076cd | 2018-11-19 15:02:46 +0800 | [diff] [blame] | 14 | static const size_t kPageSize = sysconf(_SC_PAGESIZE); |
| 15 | static constexpr char kZramBlkdevPath[] = "/dev/block/zram0"; |
| 16 | static constexpr size_t kPatternSize = 4; |
| 17 | static constexpr size_t kSectorSize = 512; |
Riley Andrews | 1199c51 | 2015-11-20 15:40:09 -0800 | [diff] [blame] | 18 | |
| 19 | void fillPageRand(uint32_t *page) { |
Martin Liu | 2b076cd | 2018-11-19 15:02:46 +0800 | [diff] [blame] | 20 | uint32_t start = rand(); |
| 21 | for (int i = 0; i < kPageSize / sizeof(start); i++) { |
Riley Andrews | 1199c51 | 2015-11-20 15:40:09 -0800 | [diff] [blame] | 22 | page[i] = start+i; |
| 23 | } |
| 24 | } |
Martin Liu | 2b076cd | 2018-11-19 15:02:46 +0800 | [diff] [blame] | 25 | void fillPageCompressible(void* page) { |
| 26 | uint32_t val = rand() & 0xfff; |
| 27 | auto page_ptr = reinterpret_cast<typeof(val)*>(page); |
| 28 | std::vector<typeof(val)> pattern(kPatternSize, 0); |
| 29 | |
| 30 | for (auto i = 0u; i < kPatternSize; i++) { |
| 31 | pattern[i] = val + i; |
| 32 | } |
| 33 | // fill in ABCD... pattern |
| 34 | for (int i = 0; i < kPageSize / sizeof(val); i += kPatternSize) { |
| 35 | std::copy_n(pattern.data(), kPatternSize, (page_ptr + i)); |
Riley Andrews | 1199c51 | 2015-11-20 15:40:09 -0800 | [diff] [blame] | 36 | } |
| 37 | } |
| 38 | |
| 39 | class AlignedAlloc { |
| 40 | void *m_ptr; |
| 41 | public: |
| 42 | AlignedAlloc(size_t size, size_t align) { |
| 43 | posix_memalign(&m_ptr, align, size); |
| 44 | } |
| 45 | ~AlignedAlloc() { |
| 46 | free(m_ptr); |
| 47 | } |
| 48 | void *ptr() { |
| 49 | return m_ptr; |
| 50 | } |
| 51 | }; |
| 52 | |
| 53 | class BlockFd { |
| 54 | int m_fd = -1; |
| 55 | public: |
| 56 | BlockFd(const char *path, bool direct) { |
| 57 | m_fd = open(path, O_RDWR | (direct ? O_DIRECT : 0)); |
| 58 | } |
| 59 | size_t getSize() { |
| 60 | size_t blockSize = 0; |
| 61 | int result = ioctl(m_fd, BLKGETSIZE, &blockSize); |
| 62 | if (result < 0) { |
Martin Liu | 2b076cd | 2018-11-19 15:02:46 +0800 | [diff] [blame] | 63 | cout << "ioctl block size failed" << endl; |
Riley Andrews | 1199c51 | 2015-11-20 15:40:09 -0800 | [diff] [blame] | 64 | } |
Martin Liu | 2b076cd | 2018-11-19 15:02:46 +0800 | [diff] [blame] | 65 | return blockSize * kSectorSize; |
Riley Andrews | 1199c51 | 2015-11-20 15:40:09 -0800 | [diff] [blame] | 66 | } |
| 67 | ~BlockFd() { |
| 68 | if (m_fd >= 0) { |
| 69 | close(m_fd); |
| 70 | } |
| 71 | } |
| 72 | void fillWithCompressible() { |
| 73 | size_t devSize = getSize(); |
Martin Liu | 2b076cd | 2018-11-19 15:02:46 +0800 | [diff] [blame] | 74 | AlignedAlloc page(kPageSize, kPageSize); |
| 75 | for (uint64_t offset = 0; offset < devSize; offset += kPageSize) { |
Riley Andrews | 1199c51 | 2015-11-20 15:40:09 -0800 | [diff] [blame] | 76 | fillPageCompressible((uint32_t*)page.ptr()); |
Martin Liu | 2b076cd | 2018-11-19 15:02:46 +0800 | [diff] [blame] | 77 | ssize_t ret = write(m_fd, page.ptr(), kPageSize); |
| 78 | if (ret != kPageSize) { |
Riley Andrews | 1199c51 | 2015-11-20 15:40:09 -0800 | [diff] [blame] | 79 | cout << "write() failed" << endl; |
| 80 | } |
| 81 | } |
| 82 | } |
| 83 | void benchSequentialRead() { |
| 84 | chrono::time_point<chrono::high_resolution_clock> start, end; |
| 85 | size_t devSize = getSize(); |
| 86 | size_t passes = 4; |
Martin Liu | 2b076cd | 2018-11-19 15:02:46 +0800 | [diff] [blame] | 87 | AlignedAlloc page(kPageSize, kPageSize); |
Riley Andrews | 1199c51 | 2015-11-20 15:40:09 -0800 | [diff] [blame] | 88 | |
| 89 | start = chrono::high_resolution_clock::now(); |
| 90 | for (int i = 0; i < passes; i++) { |
Martin Liu | 2b076cd | 2018-11-19 15:02:46 +0800 | [diff] [blame] | 91 | for (uint64_t offset = 0; offset < devSize; offset += kPageSize) { |
Riley Andrews | 1199c51 | 2015-11-20 15:40:09 -0800 | [diff] [blame] | 92 | if (offset == 0) |
| 93 | lseek(m_fd, offset, SEEK_SET); |
Martin Liu | 2b076cd | 2018-11-19 15:02:46 +0800 | [diff] [blame] | 94 | ssize_t ret = read(m_fd, page.ptr(), kPageSize); |
| 95 | if (ret != kPageSize) { |
Riley Andrews | 1199c51 | 2015-11-20 15:40:09 -0800 | [diff] [blame] | 96 | cout << "read() failed" << endl; |
| 97 | } |
| 98 | } |
| 99 | } |
| 100 | end = chrono::high_resolution_clock::now(); |
| 101 | size_t duration = chrono::duration_cast<chrono::microseconds>(end - start).count(); |
| 102 | cout << "read: " << (double)devSize * passes / 1024.0 / 1024.0 / (duration / 1000.0 / 1000.0) << "MB/s" << endl; |
| 103 | } |
| 104 | void benchSequentialWrite() { |
| 105 | chrono::time_point<chrono::high_resolution_clock> start, end; |
| 106 | size_t devSize = getSize(); |
| 107 | size_t passes = 4; |
Martin Liu | 2b076cd | 2018-11-19 15:02:46 +0800 | [diff] [blame] | 108 | AlignedAlloc page(kPageSize, kPageSize); |
Riley Andrews | 1199c51 | 2015-11-20 15:40:09 -0800 | [diff] [blame] | 109 | |
| 110 | start = chrono::high_resolution_clock::now(); |
| 111 | for (int i = 0; i < passes; i++) { |
Martin Liu | 2b076cd | 2018-11-19 15:02:46 +0800 | [diff] [blame] | 112 | for (uint64_t offset = 0; offset < devSize; offset += kPageSize) { |
Riley Andrews | 1199c51 | 2015-11-20 15:40:09 -0800 | [diff] [blame] | 113 | fillPageCompressible((uint32_t*)page.ptr()); |
| 114 | if (offset == 0) |
| 115 | lseek(m_fd, offset, SEEK_SET); |
Martin Liu | 2b076cd | 2018-11-19 15:02:46 +0800 | [diff] [blame] | 116 | ssize_t ret = write(m_fd, page.ptr(), kPageSize); |
| 117 | if (ret != kPageSize) { |
Riley Andrews | 1199c51 | 2015-11-20 15:40:09 -0800 | [diff] [blame] | 118 | cout << "write() failed" << endl; |
| 119 | } |
| 120 | } |
| 121 | } |
| 122 | end = chrono::high_resolution_clock::now(); |
| 123 | size_t duration = chrono::duration_cast<chrono::microseconds>(end - start).count(); |
| 124 | cout << "write: " << (double)devSize * passes / 1024.0 / 1024.0 / (duration / 1000.0 / 1000.0) << "MB/s" << endl; |
| 125 | |
| 126 | } |
| 127 | }; |
| 128 | |
| 129 | int bench(bool direct) |
| 130 | { |
Martin Liu | 2b076cd | 2018-11-19 15:02:46 +0800 | [diff] [blame] | 131 | BlockFd zramDev{kZramBlkdevPath, direct}; |
Riley Andrews | 1199c51 | 2015-11-20 15:40:09 -0800 | [diff] [blame] | 132 | |
| 133 | zramDev.fillWithCompressible(); |
| 134 | zramDev.benchSequentialRead(); |
| 135 | zramDev.benchSequentialWrite(); |
| 136 | return 0; |
| 137 | } |
| 138 | |
| 139 | int main(int argc, char *argv[]) |
| 140 | { |
Martin Liu | 2b076cd | 2018-11-19 15:02:46 +0800 | [diff] [blame] | 141 | int result = swapoff(kZramBlkdevPath); |
Riley Andrews | 1199c51 | 2015-11-20 15:40:09 -0800 | [diff] [blame] | 142 | if (result < 0) { |
| 143 | cout << "swapoff failed: " << strerror(errno) << endl; |
| 144 | } |
| 145 | |
| 146 | bench(1); |
| 147 | |
Martin Liu | 2b076cd | 2018-11-19 15:02:46 +0800 | [diff] [blame] | 148 | result = system((string("mkswap ") + string(kZramBlkdevPath)).c_str()); |
Riley Andrews | 1199c51 | 2015-11-20 15:40:09 -0800 | [diff] [blame] | 149 | if (result < 0) { |
| 150 | cout << "mkswap failed: " << strerror(errno) << endl; |
| 151 | return -1; |
| 152 | } |
| 153 | |
Martin Liu | 2b076cd | 2018-11-19 15:02:46 +0800 | [diff] [blame] | 154 | result = swapon(kZramBlkdevPath, 0); |
Riley Andrews | 1199c51 | 2015-11-20 15:40:09 -0800 | [diff] [blame] | 155 | if (result < 0) { |
| 156 | cout << "swapon failed: " << strerror(errno) << endl; |
| 157 | return -1; |
| 158 | } |
| 159 | return 0; |
| 160 | } |