Created
January 29, 2020 12:56
-
-
Save kevgs/52b9c733cb41a26e4dcbe1c313c635e5 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <sys/types.h> | |
#include <sys/mman.h> | |
#include <sys/stat.h> | |
#include <fcntl.h> | |
#include <unistd.h> | |
#include <cerrno> | |
#include <cassert> | |
#include <cstdlib> | |
#include <cstring> | |
#include <cstdio> | |
#include <array> | |
#include <chrono> | |
#include <string> | |
#include <iostream> | |
static const size_t kWriteTotal = 4 * 1024 * 1024; | |
static const size_t kFileSize = 1 * 1024 * 1024; | |
static const std::array<unsigned char, 4096> kBuf = { 33 }; | |
static const std::array<unsigned char, 4096> kAlignedBuf alignas(512) = { 33 }; | |
static_assert(kFileSize % kBuf.size() == 0, ""); | |
static const std::string kPath = "test_file"; | |
void ShowErrnoAndExit(std::string function_name) { | |
std::cerr << function_name << " returned errno " << strerror(errno) << "\n"; | |
std::exit(EXIT_FAILURE); | |
} | |
struct File { | |
File(std::string path, int additional_flags = 0) : path_(std::move(path)) { | |
fd_ = open(path_.c_str(), | |
O_CREAT | O_TRUNC | O_RDWR | additional_flags, | |
S_IRUSR | S_IWUSR); | |
if (fd_ == -1) | |
ShowErrnoAndExit("open()"); | |
} | |
void Resize(off_t length) { | |
assert(fd_ != -1); | |
if (int ret = posix_fallocate(fd_, 0, length)) { | |
errno = ret; | |
ShowErrnoAndExit("posix_fallocate()"); | |
} | |
} | |
void LinuxFallocate(int mode, off_t offset, off_t len) { | |
assert(fd_ != -1); | |
if (fallocate(fd_, mode, offset, len) == -1) | |
ShowErrnoAndExit("fallocate()"); | |
} | |
void Write(const void *buf, size_t count) { | |
assert(fd_ != -1); | |
ssize_t ret = write(fd_, buf, count); | |
if (ret == -1) | |
ShowErrnoAndExit("write()"); | |
if (static_cast<size_t>(ret) != count) { | |
std::cerr << "write() partial write\n"; | |
std::exit(EXIT_FAILURE); | |
} | |
} | |
void Seek(off_t offset) { | |
if (lseek(fd_, offset, SEEK_SET) == (off_t) -1) | |
ShowErrnoAndExit("lseek"); | |
} | |
void PWrite(const void *buf, size_t count, off_t offset) { | |
assert(fd_ != -1); | |
ssize_t ret = pwrite(fd_, buf, count, offset); | |
if (ret == -1) | |
ShowErrnoAndExit("pwrite()"); | |
if (static_cast<size_t>(ret) != count) { | |
std::cerr << "pwrite() partial write\n"; | |
std::exit(EXIT_FAILURE); | |
} | |
} | |
void Fsync() { | |
assert(fd_ != -1); | |
if (fsync(fd_) == -1) | |
ShowErrnoAndExit("fsync()"); | |
} | |
void Fdatasync() { | |
assert(fd_ != -1); | |
if (fdatasync(fd_) == -1) | |
ShowErrnoAndExit("fdatasync()"); | |
} | |
size_t Size() { | |
assert(fd_ != 1); | |
struct stat s; | |
if (fstat(fd_, &s) == -1) | |
ShowErrnoAndExit("fstat()"); | |
return s.st_size; | |
} | |
void Mmap() { | |
assert(fd_ != -1); | |
size_t size = Size(); | |
mapped_ = mmap(nullptr, size, | |
PROT_READ | PROT_WRITE, | |
MAP_SHARED | MAP_POPULATE, | |
fd_, 0); | |
if (mapped_ == MAP_FAILED) | |
ShowErrnoAndExit("mmap()"); | |
} | |
unsigned char *GetMmappedRegion() { | |
assert(fd_ != -1); | |
assert(mapped_); | |
return static_cast<unsigned char *>(mapped_); | |
} | |
void Msync(void *addr, size_t len) { | |
if (msync(addr, len, MS_SYNC) == -1) { | |
ShowErrnoAndExit("msync()"); | |
} | |
} | |
void Munmap() { | |
assert(fd_ != -1); | |
assert(mapped_ != nullptr); | |
if (munmap(mapped_, Size()) == -1) | |
ShowErrnoAndExit("munmap()"); | |
mapped_ = nullptr; | |
} | |
~File() { | |
if (mapped_) | |
Munmap(); | |
if (fd_ != -1 && close(fd_) == -1) | |
ShowErrnoAndExit("close()"); | |
if (unlink(path_.c_str()) == -1) | |
ShowErrnoAndExit("unlink()"); | |
} | |
int fd_{-1}; | |
void *mapped_{nullptr}; | |
std::string path_; | |
}; | |
struct SimpleCyclicWriter { | |
SimpleCyclicWriter(std::string path) : file_(path) { | |
file_.Resize(kFileSize); | |
file_.Fsync(); | |
} | |
static std::string Name() { return "Simple cyclic file"; } | |
void Write() { | |
const off_t offset = offset_ % kFileSize; | |
file_.PWrite(kBuf.data(), kBuf.size(), offset); | |
offset_ += kBuf.size(); | |
} | |
void Flush() { file_.Fdatasync(); } | |
File file_; | |
off_t offset_{0}; | |
}; | |
struct DSyncCyclicWriter { | |
DSyncCyclicWriter(std::string path) : file_(path, O_DSYNC) { | |
file_.Resize(kFileSize); | |
file_.Fsync(); | |
} | |
static std::string Name() { return "O_DSYNC cyclic file"; } | |
void Write() { | |
const off_t offset = offset_ % kFileSize; | |
file_.PWrite(kBuf.data(), kBuf.size(), offset); | |
offset_ += kBuf.size(); | |
} | |
void Flush() { } | |
File file_; | |
off_t offset_{0}; | |
}; | |
struct ODirectODsyncCyclicWriter { | |
ODirectODsyncCyclicWriter(std::string path) : | |
file_(path, O_DIRECT | O_DSYNC) { | |
file_.Resize(kFileSize); | |
file_.Fsync(); | |
} | |
static std::string Name() { return "O_DIRECT|O_DSYNC cyclic file"; } | |
void Write() { | |
const off_t offset = offset_ % kFileSize; | |
file_.PWrite(kAlignedBuf.data(), kAlignedBuf.size(), offset); | |
offset_ += kAlignedBuf.size(); | |
} | |
void Flush() { } | |
File file_; | |
off_t offset_{0}; | |
}; | |
struct MmappedCyclicWriter { | |
MmappedCyclicWriter(std::string path) : file_(path) { | |
file_.Resize(kFileSize); | |
file_.Fsync(); | |
file_.Mmap(); | |
} | |
static std::string Name() { return "Mmapped cyclic file"; } | |
void Write() { | |
const off_t offset = offset_ % kFileSize; | |
memcpy(file_.GetMmappedRegion() + offset, kBuf.data(), kBuf.size()); | |
prev_offset_ = offset; | |
offset_ += kBuf.size(); | |
} | |
void Flush() { | |
auto *start = file_.GetMmappedRegion() + prev_offset_; | |
auto *end = start + kBuf.size(); | |
start = start - reinterpret_cast<std::uintptr_t>(start) % page_size_; | |
assert(start >= file_.GetMmappedRegion()); | |
assert(end <= file_.GetMmappedRegion() + file_.Size()); | |
file_.Msync(start, end - start); | |
} | |
File file_; | |
uintptr_t page_size_{static_cast<uintptr_t>(sysconf(_SC_PAGE_SIZE))}; | |
off_t prev_offset_{0}; | |
off_t offset_{0}; | |
}; | |
struct OAppendAppendWriter { | |
OAppendAppendWriter(std::string path) : file_(path, O_APPEND) {} | |
static std::string Name() { return "O_APPEND append file"; } | |
void Write() { file_.Write(kBuf.data(), kBuf.size()); } | |
void Flush() { file_.Fsync(); } | |
File file_; | |
}; | |
struct SimpleAppendWriter { | |
SimpleAppendWriter(std::string path) : file_(path) {} | |
static std::string Name() { return "Simple append file"; } | |
void Write() { file_.Write(kBuf.data(), kBuf.size()); } | |
void Flush() { file_.Fsync(); } | |
File file_; | |
}; | |
struct ODsyncAppendWriter { | |
ODsyncAppendWriter(std::string path) : file_(path, O_DSYNC) {} | |
static std::string Name() { return "O_DSYNC append file"; } | |
void Write() { file_.Write(kBuf.data(), kBuf.size()); } | |
void Flush() { file_.Fsync(); } | |
File file_; | |
}; | |
struct ZeroFallocAppendWriter { | |
ZeroFallocAppendWriter(std::string path) : file_(path, O_DIRECT | O_DSYNC) { | |
file_.LinuxFallocate(0, 0, kWriteTotal); | |
std::array<char, 4096> buf alignas(512); | |
assert(kFileSize % buf.size() == 0); | |
file_.Seek(0); | |
for (size_t written = 0; written < kFileSize; written += buf.size()) { | |
file_.Write(buf.data(), buf.size()); | |
} | |
file_.Seek(0); | |
file_.Fsync(); | |
} | |
static std::string Name() { | |
return "fallocate() + zero fill + O_DSYNC append file"; | |
} | |
void Write() { | |
const auto &buf = kAlignedBuf; | |
file_.Write(buf.data(), buf.size()); | |
} | |
void Flush() { } | |
File file_; | |
}; | |
struct Timer { | |
using Clock = std::chrono::steady_clock; | |
~Timer() { | |
auto duration = Clock::now() - now_; | |
auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(duration); | |
auto s = std::chrono::duration_cast<std::chrono::seconds>(ms); | |
ms = ms - s; | |
std::cout << "Took " << s.count() << " seconds " << ms.count() << | |
" milliseconds\n"; | |
} | |
Clock::time_point now_ = Clock::now(); | |
}; | |
template <class Writer> | |
void Test() { | |
std::cout << Writer::Name() << "\n"; | |
Writer file_(kPath); | |
{ | |
Timer timer; | |
size_t write_total = kWriteTotal; | |
while (write_total) { | |
file_.Write(); | |
file_.Flush(); | |
write_total -= kBuf.size(); | |
} | |
} | |
std::cout << "\n"; | |
} | |
int main() { | |
std::cout << "File size " << kFileSize / 1024 << " Kb\n"; | |
std::cout << "Writing " << kWriteTotal / 1024 << " Kb to it\n"; | |
std::cout << "\n"; | |
Test<ZeroFallocAppendWriter>(); | |
Test<SimpleCyclicWriter>(); | |
Test<DSyncCyclicWriter>(); | |
Test<MmappedCyclicWriter>(); | |
Test<ODirectODsyncCyclicWriter>(); | |
Test<OAppendAppendWriter>(); | |
Test<SimpleAppendWriter>(); | |
Test<ODsyncAppendWriter>(); | |
return EXIT_SUCCESS; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment