Skip to content

Instantly share code, notes, and snippets.

@kevgs
Created January 29, 2020 12:56
Show Gist options
  • Save kevgs/52b9c733cb41a26e4dcbe1c313c635e5 to your computer and use it in GitHub Desktop.
Save kevgs/52b9c733cb41a26e4dcbe1c313c635e5 to your computer and use it in GitHub Desktop.
#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