#include #include #include #include #include #include #include #include #include #include using namespace std; static const size_t kPageSize = sysconf(_SC_PAGESIZE); static constexpr char kZramBlkdevPath[] = "/dev/block/zram0"; static constexpr size_t kPatternSize = 4; static constexpr size_t kSectorSize = 512; void fillPageRand(uint32_t *page) { uint32_t start = rand(); for (int i = 0; i < kPageSize / sizeof(start); i++) { page[i] = start+i; } } void fillPageCompressible(void* page) { uint32_t val = rand() & 0xfff; auto page_ptr = reinterpret_cast(page); std::vector pattern(kPatternSize, 0); for (auto i = 0u; i < kPatternSize; i++) { pattern[i] = val + i; } // fill in ABCD... pattern for (int i = 0; i < kPageSize / sizeof(val); i += kPatternSize) { std::copy_n(pattern.data(), kPatternSize, (page_ptr + i)); } } class AlignedAlloc { void *m_ptr; public: AlignedAlloc(size_t size, size_t align) { posix_memalign(&m_ptr, align, size); } ~AlignedAlloc() { free(m_ptr); } void *ptr() { return m_ptr; } }; class BlockFd { int m_fd = -1; public: BlockFd(const char *path, bool direct) { m_fd = open(path, O_RDWR | (direct ? O_DIRECT : 0)); } size_t getSize() { size_t blockSize = 0; int result = ioctl(m_fd, BLKGETSIZE, &blockSize); if (result < 0) { cout << "ioctl block size failed" << endl; } return blockSize * kSectorSize; } ~BlockFd() { if (m_fd >= 0) { close(m_fd); } } void fillWithCompressible() { size_t devSize = getSize(); AlignedAlloc page(kPageSize, kPageSize); for (uint64_t offset = 0; offset < devSize; offset += kPageSize) { fillPageCompressible((uint32_t*)page.ptr()); ssize_t ret = write(m_fd, page.ptr(), kPageSize); if (ret != kPageSize) { cout << "write() failed" << endl; } } } void benchSequentialRead() { chrono::time_point start, end; size_t devSize = getSize(); size_t passes = 4; AlignedAlloc page(kPageSize, kPageSize); start = chrono::high_resolution_clock::now(); for (int i = 0; i < passes; i++) { for (uint64_t offset = 0; offset < devSize; offset += kPageSize) { if (offset == 0) lseek(m_fd, offset, SEEK_SET); ssize_t ret = read(m_fd, page.ptr(), kPageSize); if (ret != kPageSize) { cout << "read() failed" << endl; } } } end = chrono::high_resolution_clock::now(); size_t duration = chrono::duration_cast(end - start).count(); cout << "read: " << (double)devSize * passes / 1024.0 / 1024.0 / (duration / 1000.0 / 1000.0) << "MB/s" << endl; } void benchSequentialWrite() { chrono::time_point start, end; size_t devSize = getSize(); size_t passes = 4; AlignedAlloc page(kPageSize, kPageSize); start = chrono::high_resolution_clock::now(); for (int i = 0; i < passes; i++) { for (uint64_t offset = 0; offset < devSize; offset += kPageSize) { fillPageCompressible((uint32_t*)page.ptr()); if (offset == 0) lseek(m_fd, offset, SEEK_SET); ssize_t ret = write(m_fd, page.ptr(), kPageSize); if (ret != kPageSize) { cout << "write() failed" << endl; } } } end = chrono::high_resolution_clock::now(); size_t duration = chrono::duration_cast(end - start).count(); cout << "write: " << (double)devSize * passes / 1024.0 / 1024.0 / (duration / 1000.0 / 1000.0) << "MB/s" << endl; } }; int bench(bool direct) { BlockFd zramDev{kZramBlkdevPath, direct}; zramDev.fillWithCompressible(); zramDev.benchSequentialRead(); zramDev.benchSequentialWrite(); return 0; } int main(int argc, char *argv[]) { int result = swapoff(kZramBlkdevPath); if (result < 0) { cout << "swapoff failed: " << strerror(errno) << endl; } bench(1); result = system((string("mkswap ") + string(kZramBlkdevPath)).c_str()); if (result < 0) { cout << "mkswap failed: " << strerror(errno) << endl; return -1; } result = swapon(kZramBlkdevPath, 0); if (result < 0) { cout << "swapon failed: " << strerror(errno) << endl; return -1; } return 0; }