1 | #include <fcntl.h> |
2 | #include <port/unistd.h> |
3 | #include <stdlib.h> |
4 | #include <time.h> |
5 | #include <iostream> |
6 | #include <iomanip> |
7 | #include <vector> |
8 | #include <random> |
9 | #include <pcg_random.hpp> |
10 | #include <IO/ReadHelpers.h> |
11 | #include <Poco/Exception.h> |
12 | #include <Common/Exception.h> |
13 | #include <Common/randomSeed.h> |
14 | #include <Common/ThreadPool.h> |
15 | #include <Common/Stopwatch.h> |
16 | #include <IO/BufferWithOwnMemory.h> |
17 | #include <cstdlib> |
18 | #include <port/clock.h> |
19 | |
20 | namespace DB |
21 | { |
22 | namespace ErrorCodes |
23 | { |
24 | extern const int CANNOT_OPEN_FILE; |
25 | extern const int CANNOT_CLOSE_FILE; |
26 | extern const int CANNOT_READ_FROM_FILE_DESCRIPTOR; |
27 | extern const int CANNOT_WRITE_TO_FILE_DESCRIPTOR; |
28 | } |
29 | } |
30 | |
31 | |
32 | enum Mode |
33 | { |
34 | MODE_NONE = 0, |
35 | MODE_READ = 1, |
36 | MODE_WRITE = 2, |
37 | MODE_ALIGNED = 4, |
38 | MODE_DIRECT = 8, |
39 | MODE_SYNC = 16, |
40 | }; |
41 | |
42 | |
43 | void thread(int fd, int mode, size_t min_offset, size_t max_offset, size_t block_size, size_t count) |
44 | { |
45 | using namespace DB; |
46 | |
47 | Memory<> direct_buf(block_size, sysconf(_SC_PAGESIZE)); |
48 | std::vector<char> simple_buf(block_size); |
49 | |
50 | char * buf; |
51 | if ((mode & MODE_DIRECT)) |
52 | buf = direct_buf.data(); |
53 | else |
54 | buf = &simple_buf[0]; |
55 | |
56 | pcg64 rng(randomSeed()); |
57 | |
58 | for (size_t i = 0; i < count; ++i) |
59 | { |
60 | long rand_result1 = rng(); |
61 | long rand_result2 = rng(); |
62 | long rand_result3 = rng(); |
63 | |
64 | size_t rand_result = rand_result1 ^ (rand_result2 << 22) ^ (rand_result3 << 43); |
65 | size_t offset; |
66 | if ((mode & MODE_DIRECT) || (mode & MODE_ALIGNED)) |
67 | offset = min_offset + rand_result % ((max_offset - min_offset) / block_size) * block_size; |
68 | else |
69 | offset = min_offset + rand_result % (max_offset - min_offset - block_size + 1); |
70 | |
71 | if (mode & MODE_READ) |
72 | { |
73 | if (static_cast<int>(block_size) != pread(fd, buf, block_size, offset)) |
74 | throwFromErrno("Cannot read" , ErrorCodes::CANNOT_READ_FROM_FILE_DESCRIPTOR); |
75 | } |
76 | else |
77 | { |
78 | if (static_cast<int>(block_size) != pwrite(fd, buf, block_size, offset)) |
79 | throwFromErrno("Cannot write" , ErrorCodes::CANNOT_WRITE_TO_FILE_DESCRIPTOR); |
80 | } |
81 | } |
82 | } |
83 | |
84 | |
85 | int mainImpl(int argc, char ** argv) |
86 | { |
87 | using namespace DB; |
88 | |
89 | const char * file_name = 0; |
90 | int mode = MODE_NONE; |
91 | UInt64 min_offset = 0; |
92 | UInt64 max_offset = 0; |
93 | UInt64 block_size = 0; |
94 | UInt64 threads = 0; |
95 | UInt64 count = 0; |
96 | |
97 | if (argc != 8) |
98 | { |
99 | std::cerr << "Usage: " << argv[0] << " file_name (r|w)[a][d][s] min_offset max_offset block_size threads count" << std::endl << |
100 | "a - aligned, d - direct, s - sync" << std::endl; |
101 | return 1; |
102 | } |
103 | |
104 | file_name = argv[1]; |
105 | min_offset = parse<UInt64>(argv[3]); |
106 | max_offset = parse<UInt64>(argv[4]); |
107 | block_size = parse<UInt64>(argv[5]); |
108 | threads = parse<UInt64>(argv[6]); |
109 | count = parse<UInt64>(argv[7]); |
110 | |
111 | for (int i = 0; argv[2][i]; ++i) |
112 | { |
113 | char c = argv[2][i]; |
114 | switch(c) |
115 | { |
116 | case 'r': |
117 | mode |= MODE_READ; |
118 | break; |
119 | case 'w': |
120 | mode |= MODE_WRITE; |
121 | break; |
122 | case 'a': |
123 | mode |= MODE_ALIGNED; |
124 | break; |
125 | case 'd': |
126 | mode |= MODE_DIRECT; |
127 | break; |
128 | case 's': |
129 | mode |= MODE_SYNC; |
130 | break; |
131 | default: |
132 | throw Poco::Exception("Invalid mode" ); |
133 | } |
134 | } |
135 | |
136 | ThreadPool pool(threads); |
137 | |
138 | #ifndef __APPLE__ |
139 | int fd = open(file_name, ((mode & MODE_READ) ? O_RDONLY : O_WRONLY) | ((mode & MODE_DIRECT) ? O_DIRECT : 0) | ((mode & MODE_SYNC) ? O_SYNC : 0)); |
140 | #else |
141 | int fd = open(file_name, ((mode & MODE_READ) ? O_RDONLY : O_WRONLY) | ((mode & MODE_SYNC) ? O_SYNC : 0)); |
142 | #endif |
143 | if (-1 == fd) |
144 | throwFromErrno("Cannot open file" , ErrorCodes::CANNOT_OPEN_FILE); |
145 | #ifdef __APPLE__ |
146 | if (mode & MODE_DIRECT) |
147 | if (fcntl(fd, F_NOCACHE, 1) == -1) |
148 | throwFromErrno("Cannot open file" , ErrorCodes::CANNOT_CLOSE_FILE); |
149 | #endif |
150 | Stopwatch watch; |
151 | |
152 | for (size_t i = 0; i < threads; ++i) |
153 | pool.scheduleOrThrowOnError(std::bind(thread, fd, mode, min_offset, max_offset, block_size, count)); |
154 | pool.wait(); |
155 | |
156 | fsync(fd); |
157 | |
158 | watch.stop(); |
159 | |
160 | if (0 != close(fd)) |
161 | throwFromErrno("Cannot close file" , ErrorCodes::CANNOT_CLOSE_FILE); |
162 | |
163 | std::cout << std::fixed << std::setprecision(2) |
164 | << "Done " << count << " * " << threads << " ops" ; |
165 | if (mode & MODE_ALIGNED) |
166 | std::cout << " (aligned)" ; |
167 | if (mode & MODE_DIRECT) |
168 | std::cout << " (direct)" ; |
169 | if (mode & MODE_SYNC) |
170 | std::cout << " (sync)" ; |
171 | std::cout << " in " << watch.elapsedSeconds() << " sec." |
172 | << ", " << count * threads / watch.elapsedSeconds() << " ops/sec." |
173 | << ", " << count * threads * block_size / watch.elapsedSeconds() / 1000000 << " MB/sec." |
174 | << std::endl; |
175 | |
176 | return 0; |
177 | } |
178 | |
179 | |
180 | int main(int argc, char ** argv) |
181 | { |
182 | try |
183 | { |
184 | return mainImpl(argc, argv); |
185 | } |
186 | catch (const Poco::Exception & e) |
187 | { |
188 | std::cerr << e.what() << ", " << e.message() << std::endl; |
189 | return 1; |
190 | } |
191 | } |
192 | |