1 #include <fcntl.h>
2 #include <linux/fs.h>
3 #include <sys/stat.h>
4 #include <sys/swap.h>
5 #include <sys/types.h>
6 #include <unistd.h>
7 #include <chrono>
8 #include <iostream>
9 #include <numeric>
10 #include <vector>
11
12 using namespace std;
13
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;
18
fillPageRand(uint32_t * page)19 void fillPageRand(uint32_t *page) {
20 uint32_t start = rand();
21 for (int i = 0; i < kPageSize / sizeof(start); i++) {
22 page[i] = start+i;
23 }
24 }
fillPageCompressible(void * page)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));
36 }
37 }
38
39 class AlignedAlloc {
40 void *m_ptr;
41 public:
AlignedAlloc(size_t size,size_t align)42 AlignedAlloc(size_t size, size_t align) {
43 posix_memalign(&m_ptr, align, size);
44 }
~AlignedAlloc()45 ~AlignedAlloc() {
46 free(m_ptr);
47 }
ptr()48 void *ptr() {
49 return m_ptr;
50 }
51 };
52
53 class BlockFd {
54 int m_fd = -1;
55 public:
BlockFd(const char * path,bool direct)56 BlockFd(const char *path, bool direct) {
57 m_fd = open(path, O_RDWR | (direct ? O_DIRECT : 0));
58 }
getSize()59 size_t getSize() {
60 size_t blockSize = 0;
61 int result = ioctl(m_fd, BLKGETSIZE, &blockSize);
62 if (result < 0) {
63 cout << "ioctl block size failed" << endl;
64 }
65 return blockSize * kSectorSize;
66 }
~BlockFd()67 ~BlockFd() {
68 if (m_fd >= 0) {
69 close(m_fd);
70 }
71 }
fillWithCompressible()72 void fillWithCompressible() {
73 size_t devSize = getSize();
74 AlignedAlloc page(kPageSize, kPageSize);
75 for (uint64_t offset = 0; offset < devSize; offset += kPageSize) {
76 fillPageCompressible((uint32_t*)page.ptr());
77 ssize_t ret = write(m_fd, page.ptr(), kPageSize);
78 if (ret != kPageSize) {
79 cout << "write() failed" << endl;
80 }
81 }
82 }
benchSequentialRead()83 void benchSequentialRead() {
84 chrono::time_point<chrono::high_resolution_clock> start, end;
85 size_t devSize = getSize();
86 size_t passes = 4;
87 AlignedAlloc page(kPageSize, kPageSize);
88
89 start = chrono::high_resolution_clock::now();
90 for (int i = 0; i < passes; i++) {
91 for (uint64_t offset = 0; offset < devSize; offset += kPageSize) {
92 if (offset == 0)
93 lseek(m_fd, offset, SEEK_SET);
94 ssize_t ret = read(m_fd, page.ptr(), kPageSize);
95 if (ret != kPageSize) {
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 }
benchSequentialWrite()104 void benchSequentialWrite() {
105 chrono::time_point<chrono::high_resolution_clock> start, end;
106 size_t devSize = getSize();
107 size_t passes = 4;
108 AlignedAlloc page(kPageSize, kPageSize);
109
110 start = chrono::high_resolution_clock::now();
111 for (int i = 0; i < passes; i++) {
112 for (uint64_t offset = 0; offset < devSize; offset += kPageSize) {
113 fillPageCompressible((uint32_t*)page.ptr());
114 if (offset == 0)
115 lseek(m_fd, offset, SEEK_SET);
116 ssize_t ret = write(m_fd, page.ptr(), kPageSize);
117 if (ret != kPageSize) {
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
bench(bool direct)129 int bench(bool direct)
130 {
131 BlockFd zramDev{kZramBlkdevPath, direct};
132
133 zramDev.fillWithCompressible();
134 zramDev.benchSequentialRead();
135 zramDev.benchSequentialWrite();
136 return 0;
137 }
138
main(int argc,char * argv[])139 int main(int argc, char *argv[])
140 {
141 int result = swapoff(kZramBlkdevPath);
142 if (result < 0) {
143 cout << "swapoff failed: " << strerror(errno) << endl;
144 }
145
146 bench(1);
147
148 result = system((string("mkswap ") + string(kZramBlkdevPath)).c_str());
149 if (result < 0) {
150 cout << "mkswap failed: " << strerror(errno) << endl;
151 return -1;
152 }
153
154 result = swapon(kZramBlkdevPath, 0);
155 if (result < 0) {
156 cout << "swapon failed: " << strerror(errno) << endl;
157 return -1;
158 }
159 return 0;
160 }
161