]> git.proxmox.com Git - ceph.git/blame - ceph/src/boost/libs/nowide/test/benchmark_fstream.cpp
import quincy beta 17.1.0
[ceph.git] / ceph / src / boost / libs / nowide / test / benchmark_fstream.cpp
CommitLineData
f67539c2
TL
1//
2// Copyright (c) 2012 Artyom Beilis (Tonkikh)
20effc67 3// Copyright (c) 2019 - 2020 Alexander Grund
f67539c2
TL
4//
5// Distributed under the Boost Software License, Version 1.0. (See
20effc67 6// accompanying file LICENSE or copy at
f67539c2
TL
7// http://www.boost.org/LICENSE_1_0.txt)
8//
9
10#define BOOST_NOWIDE_TEST_NO_MAIN
11
12#include <boost/nowide/convert.hpp>
13#include <boost/nowide/cstdio.hpp>
14#include <boost/nowide/fstream.hpp>
f67539c2 15#include <algorithm>
20effc67 16#include <chrono>
f67539c2
TL
17#include <cstdio>
18#include <fstream>
19#include <iomanip>
20#include <iostream>
21#include <map>
22#include <stdexcept>
23#include <vector>
24
25#include "test.hpp"
26
27template<typename Key, typename Value, typename Key2>
28Value get(const std::map<Key, Value>& map, const Key2& key)
29{
30 typename std::map<Key, Value>::const_iterator it = map.find(key);
31 if(it == map.end())
32 throw std::runtime_error("Key not found");
33 return it->second;
34}
35
36namespace nw = boost::nowide;
37template<typename FStream>
38class io_fstream
39{
40public:
41 explicit io_fstream(const char* file, bool read)
42 {
43 f_.open(file, read ? std::fstream::in : std::fstream::out | std::fstream::trunc);
44 TEST(f_);
45 }
46 // coverity[exn_spec_violation]
47 ~io_fstream()
48 {
49 f_.close();
50 }
51 void write(const char* buf, int size)
52 {
53 TEST(f_.write(buf, size));
54 }
55 void read(char* buf, int size)
56 {
57 TEST(f_.read(buf, size));
58 }
59 void rewind()
60 {
61 f_.seekg(0);
62 f_.seekp(0);
63 }
64 void flush()
65 {
66 f_ << std::flush;
67 }
68
69private:
70 FStream f_;
71};
72
73class io_stdio
74{
75public:
76 io_stdio(const char* file, bool read)
77 {
78 f_ = nw::fopen(file, read ? "r" : "w+");
79 TEST(f_);
80 }
81 ~io_stdio()
82 {
83 std::fclose(f_);
84 f_ = 0;
85 }
86 void write(const char* buf, int size)
87 {
88 TEST(std::fwrite(buf, 1, size, f_) == static_cast<size_t>(size));
89 }
90 void read(char* buf, int size)
91 {
92 TEST(std::fread(buf, 1, size, f_) == static_cast<size_t>(size));
93 }
94 void rewind()
95 {
96 std::rewind(f_);
97 }
98 void flush()
99 {
100 std::fflush(f_);
101 }
102
103private:
104 FILE* f_;
105};
106
107#if defined(_MSC_VER)
108extern "C" void _ReadWriteBarrier(void);
109#pragma intrinsic(_ReadWriteBarrier)
110#define BOOST_NOWIDE_READ_WRITE_BARRIER() _ReadWriteBarrier()
111#elif defined(__GNUC__)
112#if(__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) > 40100
113#define BOOST_NOWIDE_READ_WRITE_BARRIER() __sync_synchronize()
114#else
115#define BOOST_NOWIDE_READ_WRITE_BARRIER() __asm__ __volatile__("" : : : "memory")
116#endif
117#else
118#define BOOST_NOWIDE_READ_WRITE_BARRIER() (void)
119#endif
120
121struct perf_data
122{
123 // Block-size to read/write performance in MB/s
124 std::map<size_t, double> read, write;
125};
126
127char rand_char()
128{
129 // coverity[dont_call]
130 return static_cast<char>(std::rand());
131}
132
133std::vector<char> get_rand_data(int size)
134{
135 std::vector<char> data(size);
136 std::generate(data.begin(), data.end(), rand_char);
137 return data;
138}
139
140static const int MIN_BLOCK_SIZE = 32;
141static const int MAX_BLOCK_SIZE = 8192;
142
143template<typename FStream>
144perf_data test_io(const char* file)
145{
20effc67
TL
146 namespace chrono = std::chrono;
147 using clock = chrono::high_resolution_clock;
148 using milliseconds = chrono::duration<double, std::milli>;
f67539c2
TL
149 perf_data results;
150 // Use vector to force write to memory and avoid possible reordering
151 std::vector<clock::time_point> start_and_end(2);
152 const int data_size = 64 * 1024 * 1024;
153 for(int block_size = MIN_BLOCK_SIZE / 2; block_size <= MAX_BLOCK_SIZE; block_size *= 2)
154 {
155 std::vector<char> buf = get_rand_data(block_size);
156 FStream tmp(file, false);
157 tmp.rewind();
158 start_and_end[0] = clock::now();
159 BOOST_NOWIDE_READ_WRITE_BARRIER();
160 for(int size = 0; size < data_size; size += block_size)
161 {
162 tmp.write(&buf[0], block_size);
163 BOOST_NOWIDE_READ_WRITE_BARRIER();
164 }
165 tmp.flush();
166 start_and_end[1] = clock::now();
167 // heatup
168 if(block_size >= MIN_BLOCK_SIZE)
169 {
170 const milliseconds duration = chrono::duration_cast<milliseconds>(start_and_end[1] - start_and_end[0]);
171 const double speed = data_size / duration.count() / 1024; // MB/s
172 results.write[block_size] = speed;
173 std::cout << " write block size " << std::setw(8) << block_size << " " << std::fixed
174 << std::setprecision(3) << speed << " MB/s" << std::endl;
175 }
176 }
177 for(int block_size = MIN_BLOCK_SIZE; block_size <= MAX_BLOCK_SIZE; block_size *= 2)
178 {
179 std::vector<char> buf = get_rand_data(block_size);
180 FStream tmp(file, true);
181 tmp.rewind();
182 start_and_end[0] = clock::now();
183 BOOST_NOWIDE_READ_WRITE_BARRIER();
184 for(int size = 0; size < data_size; size += block_size)
185 {
186 tmp.read(&buf[0], block_size);
187 BOOST_NOWIDE_READ_WRITE_BARRIER();
188 }
189 start_and_end[1] = clock::now();
190 const milliseconds duration = chrono::duration_cast<milliseconds>(start_and_end[1] - start_and_end[0]);
191 const double speed = data_size / duration.count() / 1024; // MB/s
192 results.read[block_size] = speed;
193 std::cout << " read block size " << std::setw(8) << block_size << " " << std::fixed << std::setprecision(3)
194 << speed << " MB/s" << std::endl;
195 }
196 TEST(std::remove(file) == 0);
197 return results;
198}
199
200template<typename FStream>
201perf_data test_io_driver(const char* file, const char* type)
202{
203 std::cout << "Testing I/O performance for " << type << std::endl;
204 const int repeats = 5;
205 std::vector<perf_data> results(repeats);
206
207 for(int i = 0; i < repeats; i++)
208 results[i] = test_io<FStream>(file);
209 for(int block_size = MIN_BLOCK_SIZE; block_size <= MAX_BLOCK_SIZE; block_size *= 2)
210 {
211 double read_speed = 0, write_speed = 0;
212 for(int i = 0; i < repeats; i++)
213 {
214 read_speed += get(results[i].read, block_size);
215 write_speed += get(results[i].write, block_size);
216 }
217 results[0].read[block_size] = read_speed / repeats;
218 results[0].write[block_size] = write_speed / repeats;
219 }
220 return results[0];
221}
222
223void print_perf_data(const std::map<size_t, double>& stdio_data,
224 const std::map<size_t, double>& std_data,
225 const std::map<size_t, double>& nowide_data)
226{
227 std::cout << "block size"
228 << " stdio "
229 << " std::fstream "
230 << "nowide::fstream" << std::endl;
231 for(int block_size = MIN_BLOCK_SIZE; block_size <= MAX_BLOCK_SIZE; block_size *= 2)
232 {
233 std::cout << std::setw(8) << block_size << " ";
234 std::cout << std::fixed << std::setprecision(3) << std::setw(8) << get(stdio_data, block_size) << " MB/s ";
235 std::cout << std::fixed << std::setprecision(3) << std::setw(8) << get(std_data, block_size) << " MB/s ";
236 std::cout << std::fixed << std::setprecision(3) << std::setw(8) << get(nowide_data, block_size) << " MB/s ";
237 std::cout << std::endl;
238 }
239}
240
241void test_perf(const char* file)
242{
243 perf_data stdio_data = test_io_driver<io_stdio>(file, "stdio");
20effc67
TL
244 perf_data std_data = test_io_driver<io_fstream<std::fstream>>(file, "std::fstream");
245 perf_data nowide_data = test_io_driver<io_fstream<nw::fstream>>(file, "nowide::fstream");
f67539c2
TL
246 std::cout << "================== Read performance ==================" << std::endl;
247 print_perf_data(stdio_data.read, std_data.read, nowide_data.read);
248 std::cout << "================== Write performance =================" << std::endl;
249 print_perf_data(stdio_data.write, std_data.write, nowide_data.write);
250}
251
252int main(int argc, char** argv)
253{
254 std::string filename = "perf_test_file.dat";
255 if(argc == 2)
256 {
257 filename = argv[1];
258 } else if(argc != 1)
259 {
260 std::cerr << "Usage: " << argv[0] << " [test_filepath]" << std::endl;
261 return 1;
262 }
263 try
264 {
265 test_perf(filename.c_str());
266 } catch(const std::runtime_error& err)
267 {
268 std::cerr << "Benchmarking failed: " << err.what() << std::endl;
269 return 1;
270 }
271 return 0;
272}