2020-07-15 02:11:59 +03:00
|
|
|
/*
|
2022-01-05 01:58:22 +03:00
|
|
|
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
2020-07-15 02:11:59 +03:00
|
|
|
*
|
|
|
|
* This software may be used and distributed according to the terms of the
|
|
|
|
* GNU General Public License version 2.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <folly/Exception.h>
|
|
|
|
#include <folly/File.h>
|
|
|
|
#include <folly/FileUtil.h>
|
2023-01-19 07:46:42 +03:00
|
|
|
#include <folly/String.h>
|
2020-07-15 02:11:59 +03:00
|
|
|
#include <folly/logging/xlog.h>
|
2022-06-16 23:43:57 +03:00
|
|
|
#include <folly/portability/GFlags.h>
|
2023-01-19 07:46:42 +03:00
|
|
|
#include <algorithm>
|
2020-07-15 02:11:59 +03:00
|
|
|
#include <random>
|
2022-04-04 21:37:38 +03:00
|
|
|
#include "eden/common/utils/benchharness/Bench.h"
|
2020-07-15 02:11:59 +03:00
|
|
|
|
|
|
|
namespace {
|
|
|
|
constexpr size_t kPageSize = 4096;
|
2023-01-19 07:46:42 +03:00
|
|
|
constexpr size_t kDefaultFileSize = 16 * 1024 * 1024;
|
2020-07-15 02:11:59 +03:00
|
|
|
|
|
|
|
DEFINE_string(
|
|
|
|
filename,
|
|
|
|
"random_writes.tmp",
|
|
|
|
"Path to which writes should be issued");
|
2023-01-19 07:46:42 +03:00
|
|
|
DEFINE_uint64(filesize, kDefaultFileSize, "File size in bytes");
|
2020-07-15 02:11:59 +03:00
|
|
|
|
|
|
|
struct TemporaryFile {
|
|
|
|
TemporaryFile()
|
|
|
|
: file{FLAGS_filename, O_CREAT | O_EXCL | O_WRONLY | O_CLOEXEC} {
|
|
|
|
if (FLAGS_filesize == 0 || (FLAGS_filesize % kPageSize)) {
|
|
|
|
throw std::invalid_argument{"file size must be multiple of page size"};
|
|
|
|
}
|
|
|
|
folly::checkUnixError(ftruncate(file.fd(), FLAGS_filesize), "ftruncate");
|
|
|
|
}
|
|
|
|
|
|
|
|
~TemporaryFile() {
|
2023-01-19 07:46:42 +03:00
|
|
|
file.close();
|
|
|
|
if (-1 == unlink(FLAGS_filename.c_str())) {
|
|
|
|
int err = errno;
|
|
|
|
fmt::print(
|
|
|
|
stderr,
|
|
|
|
"error unlinking {}: {}",
|
|
|
|
FLAGS_filename,
|
|
|
|
folly::errnoStr(err));
|
|
|
|
}
|
2020-07-15 02:11:59 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
folly::File file;
|
|
|
|
};
|
|
|
|
|
|
|
|
int getTemporaryFD() {
|
|
|
|
static TemporaryFile tf;
|
|
|
|
return tf.file.fd();
|
|
|
|
}
|
|
|
|
|
2023-01-19 07:46:42 +03:00
|
|
|
using random_bytes_engine = std::independent_bits_engine<
|
|
|
|
std::default_random_engine,
|
|
|
|
CHAR_BIT,
|
|
|
|
unsigned short>;
|
|
|
|
|
2020-07-15 02:11:59 +03:00
|
|
|
void random_writes(benchmark::State& state) {
|
|
|
|
int fd = getTemporaryFD();
|
|
|
|
off_t pageCount = FLAGS_filesize / kPageSize;
|
|
|
|
|
2023-01-19 07:46:42 +03:00
|
|
|
uint8_t pagebuf[kPageSize];
|
|
|
|
std::generate(std::begin(pagebuf), std::end(pagebuf), random_bytes_engine{});
|
2020-07-15 02:11:59 +03:00
|
|
|
|
|
|
|
std::default_random_engine gen{std::random_device{}()};
|
|
|
|
|
|
|
|
// std::uniform_int_distribution has as much userspace CPU cost as
|
|
|
|
// __libc_pwrite64 so pregenerate some offsets.
|
2023-01-19 07:46:42 +03:00
|
|
|
std::vector<off_t> offsets(pageCount);
|
|
|
|
std::generate(offsets.begin(), offsets.end(), [offset = 0ull]() mutable {
|
|
|
|
return (offset++) * kPageSize;
|
2020-07-15 02:11:59 +03:00
|
|
|
});
|
2023-01-19 07:46:42 +03:00
|
|
|
std::shuffle(offsets.begin(), offsets.end(), gen);
|
2020-07-15 02:11:59 +03:00
|
|
|
|
2023-01-19 07:46:42 +03:00
|
|
|
size_t total_written = 0;
|
|
|
|
size_t total_pages = 0;
|
2020-07-15 02:11:59 +03:00
|
|
|
size_t offset_index = 0;
|
|
|
|
for (auto _ : state) {
|
2023-01-19 07:46:42 +03:00
|
|
|
off_t offset = offsets[offset_index];
|
|
|
|
if (offset_index++ == offsets.size()) {
|
|
|
|
// Redoing the offsets is okay
|
|
|
|
total_pages += offsets.size();
|
|
|
|
offset_index = 0;
|
|
|
|
}
|
|
|
|
int result = pwrite(fd, pagebuf, kPageSize, offset);
|
|
|
|
folly::checkUnixError(result);
|
|
|
|
if (result != kPageSize) {
|
|
|
|
fmt::print(stderr, "write was not complete: {} != {}", result, kPageSize);
|
|
|
|
}
|
|
|
|
total_written += result;
|
2020-07-15 02:11:59 +03:00
|
|
|
}
|
2023-01-19 07:46:42 +03:00
|
|
|
total_pages += offset_index;
|
|
|
|
|
|
|
|
state.SetItemsProcessed(total_pages);
|
|
|
|
state.SetBytesProcessed(total_written);
|
2020-07-15 02:11:59 +03:00
|
|
|
}
|
|
|
|
|
2023-01-19 07:46:42 +03:00
|
|
|
BENCHMARK(random_writes)
|
|
|
|
// By default, google benchmark shows throughput numbers in bytes per CPU
|
|
|
|
// second. That's not useful, so tell it we care about wall clock time.
|
|
|
|
->UseRealTime()
|
|
|
|
->Threads(1)
|
|
|
|
->Threads(2)
|
|
|
|
->Threads(4)
|
|
|
|
->Threads(8)
|
|
|
|
->Threads(16);
|
|
|
|
|
|
|
|
#ifdef __GLIBC__
|
|
|
|
|
2020-07-15 02:11:59 +03:00
|
|
|
void random_writes_no_cancellation(benchmark::State& state) {
|
|
|
|
int oldstate;
|
|
|
|
XCHECK_EQ(0, pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldstate));
|
|
|
|
SCOPE_EXIT {
|
|
|
|
pthread_setcancelstate(oldstate, &oldstate);
|
|
|
|
};
|
|
|
|
|
|
|
|
int oldtype;
|
|
|
|
XCHECK_EQ(0, pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &oldtype));
|
|
|
|
SCOPE_EXIT {
|
|
|
|
pthread_setcanceltype(oldtype, &oldtype);
|
|
|
|
};
|
|
|
|
|
|
|
|
random_writes(state);
|
|
|
|
}
|
|
|
|
|
|
|
|
BENCHMARK(random_writes_no_cancellation)
|
2023-01-19 07:46:42 +03:00
|
|
|
// By default, google benchmark shows throughput numbers in bytes per CPU
|
|
|
|
// second. That's not useful, so tell it we care about wall clock time.
|
|
|
|
->UseRealTime()
|
2020-07-15 02:11:59 +03:00
|
|
|
->Threads(1)
|
|
|
|
->Threads(2)
|
|
|
|
->Threads(4)
|
|
|
|
->Threads(8)
|
|
|
|
->Threads(16);
|
2023-01-19 07:46:42 +03:00
|
|
|
|
|
|
|
#endif
|
|
|
|
|
2020-07-15 02:11:59 +03:00
|
|
|
} // namespace
|
|
|
|
|
|
|
|
EDEN_BENCHMARK_MAIN();
|