Serenity Operating System
at master 147 lines 4.8 kB view raw
1/* 2 * Copyright (c) 2018-2021, Andreas Kling <kling@serenityos.org> 3 * Copyright (c) 2022, Kenneth Myhra <kennethmyhra@serenityos.org> 4 * 5 * SPDX-License-Identifier: BSD-2-Clause 6 */ 7 8#include <AK/ByteBuffer.h> 9#include <AK/DeprecatedString.h> 10#include <AK/ScopeGuard.h> 11#include <AK/Types.h> 12#include <AK/Vector.h> 13#include <LibCore/ArgsParser.h> 14#include <LibCore/ElapsedTimer.h> 15#include <LibCore/System.h> 16#include <LibMain/Main.h> 17#include <fcntl.h> 18#include <stdio.h> 19#include <sys/stat.h> 20#include <unistd.h> 21 22struct Result { 23 u64 write_bps {}; 24 u64 read_bps {}; 25}; 26 27static Result average_result(Vector<Result> const& results) 28{ 29 Result average; 30 31 for (auto& res : results) { 32 average.write_bps += res.write_bps; 33 average.read_bps += res.read_bps; 34 } 35 36 average.write_bps /= results.size(); 37 average.read_bps /= results.size(); 38 39 return average; 40} 41 42static ErrorOr<Result> benchmark(DeprecatedString const& filename, int file_size, ByteBuffer& buffer, bool allow_cache); 43 44ErrorOr<int> serenity_main(Main::Arguments arguments) 45{ 46 using namespace AK::TimeLiterals; 47 48 DeprecatedString directory = "."; 49 i64 time_per_benchmark_sec = 10; 50 Vector<size_t> file_sizes; 51 Vector<size_t> block_sizes; 52 bool allow_cache = false; 53 54 Core::ArgsParser args_parser; 55 args_parser.add_option(allow_cache, "Allow using disk cache", "cache", 'c'); 56 args_parser.add_option(directory, "Path to a directory where we can store the disk benchmark temp file", "directory", 'd', "directory"); 57 args_parser.add_option(time_per_benchmark_sec, "Time elapsed per benchmark (seconds)", "time-per-benchmark", 't', "time-per-benchmark"); 58 args_parser.add_option(file_sizes, "A comma-separated list of file sizes", "file-size", 'f', "file-size"); 59 args_parser.add_option(block_sizes, "A comma-separated list of block sizes", "block-size", 'b', "block-size"); 60 args_parser.parse(arguments); 61 62 Time const time_per_benchmark = Time::from_seconds(time_per_benchmark_sec); 63 64 if (file_sizes.size() == 0) { 65 file_sizes = { 131072, 262144, 524288, 1048576, 5242880 }; 66 } 67 if (block_sizes.size() == 0) { 68 block_sizes = { 8192, 32768, 65536 }; 69 } 70 71 umask(0644); 72 73 auto filename = DeprecatedString::formatted("{}/disk_benchmark.tmp", directory); 74 75 for (auto file_size : file_sizes) { 76 for (auto block_size : block_sizes) { 77 if (block_size > file_size) 78 continue; 79 80 auto buffer_result = ByteBuffer::create_uninitialized(block_size); 81 if (buffer_result.is_error()) { 82 warnln("Not enough memory to allocate space for block size = {}", block_size); 83 continue; 84 } 85 Vector<Result> results; 86 87 outln("Running: file_size={} block_size={}", file_size, block_size); 88 auto timer = Core::ElapsedTimer::start_new(); 89 while (timer.elapsed_time() < time_per_benchmark) { 90 out("."); 91 fflush(stdout); 92 auto result = TRY(benchmark(filename, file_size, buffer_result.value(), allow_cache)); 93 results.append(result); 94 usleep(100); 95 } 96 auto average = average_result(results); 97 outln("Finished: runs={} time={}ms write_bps={} read_bps={}", results.size(), timer.elapsed(), average.write_bps, average.read_bps); 98 99 sleep(1); 100 } 101 } 102 103 return 0; 104} 105 106ErrorOr<Result> benchmark(DeprecatedString const& filename, int file_size, ByteBuffer& buffer, bool allow_cache) 107{ 108 int flags = O_CREAT | O_TRUNC | O_RDWR; 109 if (!allow_cache) 110 flags |= O_DIRECT; 111 112 int fd = TRY(Core::System::open(filename, flags, 0644)); 113 114 auto fd_cleanup = ScopeGuard([fd, filename] { 115 auto void_or_error = Core::System::close(fd); 116 if (void_or_error.is_error()) 117 warnln("{}", void_or_error.release_error()); 118 119 void_or_error = Core::System::unlink(filename); 120 if (void_or_error.is_error()) 121 warnln("{}", void_or_error.release_error()); 122 }); 123 124 Result result; 125 126 auto timer = Core::ElapsedTimer::start_new(); 127 128 ssize_t total_written = 0; 129 while (total_written < file_size) { 130 auto nwritten = TRY(Core::System::write(fd, buffer)); 131 total_written += nwritten; 132 } 133 134 result.write_bps = (u64)(timer.elapsed() ? (file_size / timer.elapsed()) : file_size) * 1000; 135 136 TRY(Core::System::lseek(fd, 0, SEEK_SET)); 137 138 timer.start(); 139 ssize_t total_read = 0; 140 while (total_read < file_size) { 141 auto nread = TRY(Core::System::read(fd, buffer)); 142 total_read += nread; 143 } 144 145 result.read_bps = (u64)(timer.elapsed() ? (file_size / timer.elapsed()) : file_size) * 1000; 146 return result; 147}