Skip to content

Instantly share code, notes, and snippets.

@kghose
Last active June 1, 2020 01:46
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save kghose/04f7a23599d06ffce1ddc626011d341a to your computer and use it in GitHub Desktop.
Save kghose/04f7a23599d06ffce1ddc626011d341a to your computer and use it in GitHub Desktop.
File writing buffering experiment
/*
g++ --std=c++17 -O3 doublebuffer.cpp -odoublebuffer
*/
#include <chrono>
#include <ctype.h>
#include <fstream>
#include <string>
#include <thread>
const size_t buf_size = 50000;
class DoubleBuffer {
enum BufferId { one = 0, two = 1, neither = 3 };
public:
DoubleBuffer(std::string fname)
{
_buffer[0] = new size_t[buf_size];
_buffer[1] = new size_t[buf_size];
_buffer_to_write_to = BufferId::one;
_buffer_being_flushed = BufferId::neither;
file.open(fname, std::ios::binary | std::ios::out);
}
void _write_out_buffer()
{
if (_writer.joinable()) {
_writer.join();
}
_buffer_being_flushed = _buffer_to_write_to;
_writer = std::thread(
&std::ofstream::write,
&file,
(char*)_buffer[_buffer_being_flushed],
sizeof(size_t) * _buf_idx);
_buffer_to_write_to = _buffer_to_write_to == BufferId::one
? BufferId::two
: BufferId::one;
_buf_idx = 0;
}
~DoubleBuffer()
{
_write_out_buffer();
_writer.join();
delete[] _buffer[0];
delete[] _buffer[1];
}
void write(const size_t k)
{
if (_buf_idx == buf_size) {
_write_out_buffer();
}
_buffer[_buffer_to_write_to][_buf_idx] = k;
_buf_idx++;
}
private:
std::ofstream file;
size_t* _buffer[2];
BufferId _buffer_to_write_to;
BufferId _buffer_being_flushed;
size_t _buf_idx = 0;
std::thread _writer;
};
int main(int argc, char* argv[])
{
DoubleBuffer buffer("test.bin");
for (size_t j = 0; j < (1 << 27); j++) {
size_t k = (size_t)(j * 1.5 - j * 1.1 + j / 3.2);
buffer.write(k);
}
}
/*
g++ --std=c++17 -O3 doublebuffer2.cpp -odoublebuffer2
*/
#include <fstream>
#include <mutex>
#include <thread>
const size_t buf_size = 50000;
template <typename T> class ThreadedBuffer {
public:
ThreadedBuffer(std::string fname)
{
buffer[0] = new T[buf_size];
buffer[1] = new T[buf_size];
idx[0] = idx[1] = 0;
write_b = 0;
file.open(fname, std::ios::binary | std::ios::out);
saver = std::thread(&ThreadedBuffer::save_buffer_to_disk, this);
}
~ThreadedBuffer()
{
wait_for(saved);
run = false;
set(go);
saver.join();
delete[] buffer[0];
delete[] buffer[1];
}
void write(const T& k)
{
buffer[write_b][idx[write_b]] = k;
idx[write_b]++;
if (idx[write_b] == buf_size) {
wait_for(saved);
write_b = 1 - write_b;
idx[write_b] = 0;
set(go);
}
}
private:
std::ofstream file;
T* buffer[2];
size_t idx[2];
size_t write_b = 0;
bool go;
bool saved;
bool run = true;
std::mutex m;
std::condition_variable cv;
std::thread saver;
void save_buffer_to_disk()
{
set(saved);
size_t save_b = 0;
do {
wait_for(go);
file.write((char*)buffer[save_b], sizeof(T) * idx[save_b]);
save_b = 1 - save_b;
set(saved);
} while (run);
}
void set(bool& v)
{
{
std::unique_lock<std::mutex> lk(m);
v = true;
}
cv.notify_one();
}
void wait_for(bool& v)
{
std::unique_lock<std::mutex> lk(m);
cv.wait(lk, [&] { return v; });
v = false;
}
};
int main(int argc, char* argv[])
{
ThreadedBuffer<size_t> buffer("test.bin");
for (size_t j = 0; j < (1 << 27); j++) {
size_t k = (size_t)(j * 1.5 - j * 1.1 + j / 3.2);
buffer.write(k);
}
}
/*
g++ --std=c++17 -O3 nobuffer.cpp -onobuffer
*/
#include <ctype.h>
#include <fstream>
int main(int argc, char* argv[])
{
std::ofstream buffer("test.bin", std::ios::binary | std::ios::out);
for (size_t j = 0; j < (1 << 27); j++) {
size_t k = (size_t)(j * 1.5 - j * 1.1 + j / 3.2);
buffer.write((char*)&k, sizeof(size_t));
}
}
/*
g++ --std=c++17 -O3 singlebuffer.cpp -osinglebuffer
*/
#include <ctype.h>
#include <fstream>
#include <string>
const size_t buf_size = 2048;
class Buffer {
public:
Buffer(std::string fname)
{
file.open(fname, std::ios::binary | std::ios::out);
}
void _write() { file.write((char*)_buffer, sizeof(size_t) * _buf_idx); }
~Buffer() { _write(); }
void write(const size_t k)
{
if (_buf_idx == buf_size) {
_write();
_buf_idx = 0;
}
_buffer[_buf_idx] = k;
_buf_idx++;
}
private:
std::ofstream file;
size_t _buffer[buf_size];
size_t _buf_idx = 0;
};
int main(int argc, char* argv[])
{
Buffer buffer("test.bin");
for (size_t j = 0; j < (1 << 27); j++) {
size_t k = (size_t)(j * 1.5 - j * 1.1 + j / 3.2);
buffer.write(k);
}
}
@startuml nobuffer
concise "Main Thread" as MT
@0
MT is compute
@2
MT is save
@5
MT is compute
@7
MT is save
@enduml
@startuml buffer
concise "Main Thread" as MT
@0
MT is "compute x N"
@4
MT is save
@10
MT is "compute x N"
@14
MT is save
@enduml
@startuml doublebuffer
concise "Main Thread" as MT
concise "Writer Thread" as WT
@0
MT is "compute x N"
@4
MT -> WT : Write
WT is init
MT is "compute x N"
@5
WT is save
@8
MT is {-}
@11
WT -> MT : Done
MT -> WT : Write
WT is init
MT is "compute x N"
@12
WT is save
@15
MT is {-}
@18
WT -> MT : Done
WT is init
MT is "compute x N"
@18
WT is save
@enduml
@startuml persistentdoublebuffer
concise "Main Thread" as MT
concise "Writer Thread" as WT
@0
MT is "compute x N"
WT is init
@1
WT is {-}
@4
MT -> WT : Write
WT is save
MT is "compute x N"
@8
MT is {-}
@10
WT -> MT : Done
MT -> WT : Write
WT is save
MT is "compute x N"
@14
MT is {-}
@16
WT -> MT : Done
WT is save
MT is "compute x N"
@enduml
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment