Skip to content

Commit e76a327

Browse files
Add the possibility to compile filewriter in single-thread mode
Now we can also measure the perf impact of multi-threading when turning off the compression thread, the write-out threa, or both. Also adjust sleep times of the write helper threads as our own unit tests ran much faster without threads until they were lowered.
1 parent ad58e9d commit e76a327

File tree

3 files changed

+79
-36
lines changed

3 files changed

+79
-36
lines changed

src/filereader.cpp

+2-2
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ file_reader::file_reader(const std::string& filename, unsigned mytid) : tid(myti
2323
done_reading = false;
2424
readahead_chunks.exchange(2);
2525
decompressor_thread = std::thread(&file_reader::decompressor, this);
26-
DLOG("%s opened for reading (size %lu) and decompressor thread launched!", filename.c_str(), (unsigned long)total_left);
26+
DLOG("%s opened for reading (size %lu) and decompressor thread %u launched!", filename.c_str(), (unsigned long)total_left, tid);
2727
}
2828

2929
file_reader::file_reader(packed pf, unsigned mytid) : tid(mytid), mFilename(pf.inside)
@@ -51,7 +51,7 @@ file_reader::~file_reader()
5151
fclose(fp);
5252
if (times_caught_decompressor) // this would be bad
5353
{
54-
ELOG("We caught up with the decompressor thread %u times!", (unsigned)times_caught_decompressor);
54+
ELOG("We caught up with the decompressor thread %u times on thread %u!", (unsigned)times_caught_decompressor, tid);
5555
}
5656
}
5757

src/filewriter.cpp

+63-34
Original file line numberDiff line numberDiff line change
@@ -98,8 +98,19 @@ void file_writer::finalize()
9898
}
9999
// whatever is left in our current buffer, move to work list
100100
chunk_mutex.lock();
101+
printf("Filewriter finalizing thread %u: %lu total bytes, %lu in last chunk, %d uncompressed chunks, and %d compressed chunks to be written out\n",
102+
mTid, (unsigned long)uncompressed_bytes, (unsigned long)uidx, (int)uncompressed_chunks.size(), (int)compressed_chunks.size());
101103
chunk.shrink(uidx);
104+
#ifndef MULTITHREADED_COMPRESS
105+
chunk = compress_chunk(chunk);
106+
#ifdef MULTITHREADED_WRITE
107+
compressed_chunks.push_front(chunk);
108+
#else
109+
write_chunk(chunk);
110+
#endif
111+
#else
102112
uncompressed_chunks.push_front(chunk);
113+
#endif
103114
chunk = buffer(uncompressed_chunk_size); // ready to go again
104115
chunk_mutex.unlock();
105116
// wrap up work in work lists
@@ -127,10 +138,31 @@ file_writer::~file_writer()
127138
chunk.release();
128139
}
129140

141+
void file_writer::write_chunk(buffer& active)
142+
{
143+
size_t written = 0;
144+
size_t size = active.size();
145+
char* ptr = active.data();
146+
int err = 0;
147+
do {
148+
written = fwrite(ptr, 1, size, fp);
149+
ptr += written;
150+
size -= written;
151+
err = ferror(fp);
152+
} while (size > 0 && (err == EAGAIN || err == EWOULDBLOCK || err == EINTR));
153+
if (size > 0)
154+
{
155+
ELOG("Failed to write out file (%u bytes left): %s", (unsigned)size, strerror(ferror(fp)));
156+
}
157+
DLOG3("Filewriter thread %d wrote out compressed buffer of %lu size\n", mTid, (unsigned long)active.size());
158+
active.release();
159+
}
160+
130161
void file_writer::serializer()
131162
{
132163
// lock, steal compressed buffer, unlock, store to disk, sleep, repeat
133164
set_thread_name("serializer");
165+
#ifdef MULTITHREADED_WRITE
134166
while (1)
135167
{
136168
buffer active;
@@ -151,36 +183,42 @@ void file_writer::serializer()
151183
// save compressed buffer
152184
if (active.size() > 0)
153185
{
154-
size_t written = 0;
155-
size_t size = active.size();
156-
char* ptr = active.data();
157-
int err = 0;
158-
DLOG3("\thunk=%u", (unsigned)size);
159-
do {
160-
written = fwrite(ptr, 1, size, fp);
161-
DLOG3("\t\twritten=%u / %u", (unsigned)written, (unsigned)size);
162-
ptr += written;
163-
size -= written;
164-
err = ferror(fp);
165-
} while (size > 0 && (err == EAGAIN || err == EWOULDBLOCK || err == EINTR));
166-
if (size > 0)
167-
{
168-
ELOG("Failed to write out file (%u bytes left): %s", (unsigned)size, strerror(ferror(fp)));
169-
}
170-
active.release();
186+
write_chunk(active);
171187
}
172188
// if not done and no work done, wait a bit
173189
else if (!done_compressing)
174190
{
175-
usleep(10000);
191+
usleep(2000);
176192
}
177193
}
194+
#endif
195+
}
196+
197+
buffer file_writer::compress_chunk(buffer& uncompressed)
198+
{
199+
const uint64_t header_size = sizeof(uint64_t) * 2;
200+
uint64_t compressed_size = density_compress_safe_size(uncompressed.size()) + header_size;
201+
buffer compressed(compressed_size);
202+
density_processing_result result = density_compress((const uint8_t *)uncompressed.data(), uncompressed.size(),
203+
(uint8_t *)compressed.data() + header_size, compressed.size(),
204+
DENSITY_ALGORITHM_CHEETAH);
205+
uncompressed.release();
206+
if (result.state != DENSITY_STATE_OK)
207+
{
208+
ABORT("Failed to compress buffer - aborting from compression thread");
209+
}
210+
uint64_t header[2] = { result.bytesWritten, result.bytesRead }; // store compressed and uncompressed sizes
211+
memcpy(compressed.data(), header, header_size); // use memcpy to avoid aliasing issues
212+
compressed.shrink(result.bytesWritten + header_size);
213+
DLOG3("Filewriter thread %d handing over compressed buffer of %lu bytes, was %lu bytes uncompressed", mTid, (unsigned long)(result.bytesWritten + header_size), (unsigned long)result.bytesRead);
214+
return compressed;
178215
}
179216

180217
void file_writer::compressor()
181218
{
182219
// lock, grab pointer to uncompressed, make new compressed, unlock, compress, sleep, repeat
183220
set_thread_name("compressor");
221+
#ifdef MULTITHREADED_COMPRESS
184222
while (1)
185223
{
186224
buffer uncompressed;
@@ -202,29 +240,20 @@ void file_writer::compressor()
202240
// compress it
203241
if (uncompressed.size() > 0)
204242
{
205-
const uint64_t header_size = sizeof(uint64_t) * 2;
206-
uint64_t compressed_size = density_compress_safe_size(uncompressed.size()) + header_size;
207-
buffer compressed(compressed_size);
208-
density_processing_result result = density_compress((const uint8_t *)uncompressed.data(), uncompressed.size(),
209-
(uint8_t *)compressed.data() + header_size, compressed.size(),
210-
DENSITY_ALGORITHM_CHEETAH);
211-
uncompressed.release();
212-
if (result.state != DENSITY_STATE_OK)
213-
{
214-
ELOG("Failed to compress buffer - aborting compression thread");
215-
break;
216-
}
217-
uint64_t header[2] = { result.bytesWritten, result.bytesRead }; // store compressed and uncompressed sizes
218-
memcpy(compressed.data(), header, header_size); // use memcpy to avoid aliasing issues
219-
compressed.shrink(result.bytesWritten + header_size);
243+
buffer compressed = compress_chunk(uncompressed);
244+
#ifdef MULTITHREADED_WRITE
220245
chunk_mutex.lock();
221246
compressed_chunks.push_front(compressed);
222247
chunk_mutex.unlock();
248+
#else
249+
write_chunk(compressed);
250+
#endif
223251
}
224252
// if not done and no work done, wait a bit
225253
else if (!done_feeding)
226254
{
227-
usleep(100000);
255+
usleep(2000);
228256
}
229257
}
258+
#endif
230259
}

src/filewriter.h

+14
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@
1111
#include "lavamutex.h"
1212
#include "util.h"
1313

14+
#define MULTITHREADED_COMPRESS
15+
#define MULTITHREADED_WRITE
16+
1417
class file_writer
1518
{
1619
file_writer(const file_writer&) = delete;
@@ -21,9 +24,18 @@ class file_writer
2124
// shrink existing chunk to actually used size
2225
chunk.shrink(uidx);
2326
// move chunk into list of chunks to compress
27+
#ifdef MULTITHREADED_COMPRESS
2428
chunk_mutex.lock();
2529
uncompressed_chunks.push_front(chunk);
2630
chunk_mutex.unlock();
31+
#else
32+
buffer compressed = compress_chunk(chunk);
33+
#ifdef MULTITHREADED_WRITE
34+
compressed_chunks.push_front(compressed);
35+
#else
36+
write_chunk(compressed);
37+
#endif
38+
#endif
2739
// create a new chunk for writing into (we could employ a free list here as a possible optimization)
2840
if (size > uncompressed_chunk_size) // make sure our new chunk is big enough
2941
{
@@ -141,6 +153,8 @@ class file_writer
141153
private:
142154
void compressor(); // runs in separate thread, moves chunks from uncompressed to compressed
143155
void serializer(); // runs in separate thread, moves chunks from compressed to disk
156+
buffer compress_chunk(buffer& uncompressed); // returns compressed buffer
157+
void write_chunk(buffer& active);
144158

145159
lava::mutex chunk_mutex;
146160
FILE* fp = nullptr;

0 commit comments

Comments
 (0)