Here is my code. It reads a file and returns the sum of all the bytes in the file:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <malloc.h>
#include <iostream>
#include <chrono>
#include <thread>
#include <mutex>
#include <condition_variable>
#define BUFSIZE 134217728
// globals
std::mutex mut;
unsigned char* buffers[12]; // global array of pointers to buffers where file will be read
int bytes_read[12] = {0};
std::condition_variable cv;
// write_head is the shared variable associated with cv
int write_head = 0; // index of buffer currently being written to
void producer_thread()
{
int fd;
const char* fname = "1GB.txt";
if ((fd = open(fname, O_RDONLY|O_DIRECT)) < 0) {
printf("%s: cannot open %s\n", fname);
exit(2);
}
for (int i = 0; i < 12; ++i){
unsigned char* buf = buffers[i];
int n = read(fd,buf,BUFSIZE);
bytes_read[i] = n;
// wake up consumer thread
{
std::lock_guard<std::mutex> lk(mut);
write_head = i + 1;
}
cv.notify_all();
if ( n == 0 ){ // if we have reached end of file
std::cout << "Read to end of file" << std::endl;
std::cout << "Buffers used: " << i << std::endl;
return;
}
}
}
void consumer_thread(){
unsigned long result = 0;
for (int i = 0; i < 12; ++i){
// wait for buffer to become available for reading
{
std::unique_lock<std::mutex> lk(mut);
cv.wait(lk, [&]() { return i < write_head; });
}
int n = bytes_read[i];
if ( n == 0 ) {
std::cout << "Result: " << result;
return ;
}
// now process the data
unsigned char* buf = buffers[i];
for (int j=0; j<n; ++j)
result += buf[j];
}
}
int main (int argc, char* argv[]) {
using std::chrono::high_resolution_clock;
using std::chrono::duration_cast;
using std::chrono::duration;
using std::chrono::milliseconds;
puts("Allocating buffers");
auto start = high_resolution_clock::now();
int alignment = 4096;
// allocate 10 buffers and put them into the global buffers array
for (int i = 0; i < 10; ++i){
unsigned char* buf = (unsigned char*) memalign(alignment, BUFSIZE);
buffers[i] = buf;
}
auto end = high_resolution_clock::now();
/* Getting number of milliseconds as a double. */
duration<double, std::milli> ms_double = end - start;
puts("finished allocating buffers");
std::cout << "time taken: " << ms_double.count() << "ms\n";
// start producer and consumer threads
std::thread t1(producer_thread), t2(consumer_thread);
t1.join();
t2.join();
return 0;
}
(Note: replacing unsigned long
with int
seems to reduce running time by about 0.06
seconds, I'm not sure why. There is no integer overflow going on since the input file is all zeroes.)
This code takes around 0.97 to 1.03 seconds to run on my machine, which is the same as the fread
version.
I thought O_DIRECT would be faster as it bypasses cache, but it seems like there is no difference, maybe because I'm using disk encryption.
Is there any way to make this code faster?
P.S I'm pretty sure there is no race condition but if you notice any please say!
unsigned long
andint
, when very likely the size is the important difference. Trysigned long
orunsigned int
to find out for sure... \$\endgroup\$end
. \$\endgroup\$signed long
performs poorly (~1.03s on average) compared toint
(~0.97s average).unsigned int
seems to perform slightly worse (~1.00s on average) thanint
. There is some variance between the readings but there is definitely a difference on average. I ran the program about 5-7 times each with the different types, the output fromtime
is roughly consistent. \$\endgroup\$-O3
) and are therefore invalid... \$\endgroup\$