Created
September 18, 2014 04:53
-
-
Save aktau/8f2a18ef4eb1e4f3c0a8 to your computer and use it in GitHub Desktop.
benchmark fread/read/mmap/std::streams
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
// @aktau: this is not my file, I tried to find the original article this was from but couldn't, I'm pasting it here for posterity | |
#include <fcntl.h> | |
#include <stdio.h> | |
#include <errno.h> | |
#include <string.h> | |
#include <stdlib.h> | |
#include <sys/resource.h> | |
#include <sys/types.h> | |
#include <sys/time.h> | |
#include <sys/uio.h> | |
#include <sys/mman.h> | |
#include <unistd.h> | |
#include <iostream> | |
#include <fstream> | |
#include <vector> | |
#include <cassert> | |
using namespace std; | |
// write N * 512 integers | |
void fillFile(int N, char * name) { | |
FILE * fd = ::fopen(name, "wb"); | |
if (fd == NULL) { | |
cerr << "problem" << endl; | |
return; | |
} | |
int numbers[512]; | |
for(int k = 0; k < 512; ++k) | |
numbers[k] = k; // whatever | |
for(int t = 0; t < N; ++t) { | |
int size = 512; | |
size_t realsize; | |
int err, error; | |
if((realsize = fwrite(&size, sizeof(int),1,fd))!=1) { | |
err = errno; | |
error = ferror(fd); | |
if(error) { | |
cout<< "We written only " << realsize << " bytes" << endl; | |
cout<< "[ERROR] " << strerror(err) <<endl; | |
exit (EXIT_FAILURE); | |
} | |
return; | |
} | |
if((realsize = fwrite(&numbers[0], sizeof(int),512,fd))!=512) { | |
err = errno; | |
error = ferror(fd); | |
if(error) { | |
cout<< "We written only" << realsize << "bytes" << endl; | |
cout<< "[ERROR] " << strerror(err) <<endl; | |
exit (EXIT_FAILURE); | |
} | |
return; | |
} | |
} | |
::fclose(fd); | |
} | |
int doSomeComputation(int * numbers, size_t size) { | |
int answer = 0; | |
// totally arbitrary | |
for(size_t k = 0; k < size; k+= 2) { | |
answer += numbers[k]; | |
} | |
for(size_t k = 1; k < size; k+= 2) { | |
answer += numbers[k] * 2; | |
} | |
return answer; | |
} | |
int testfread(char * name, int N) { | |
int answer = 0; | |
FILE * fd = ::fopen(name, "rb"); | |
if (fd == NULL) { | |
cerr << "problem" << endl; | |
return -1; | |
} | |
vector<int> numbers(512); | |
for(int t = 0; t < N; ++t) { | |
int size = 0; | |
if(fread(&size, sizeof(int),1,fd)!=1) { | |
cout<<"Data can't be read???"<<endl; | |
return -1; | |
} | |
numbers.resize(size); | |
if(fread(&numbers[0], sizeof(int),numbers.size(),fd)!=numbers.size()) { | |
cout<<"Data can't be read???"<<endl; | |
return -1; | |
} | |
answer += doSomeComputation(&numbers[0],numbers.size()); | |
} | |
::fclose(fd); | |
return answer; | |
} | |
int testwithCpp(char * name, int N) { | |
int answer = 0; | |
ifstream in(name,ios::binary); | |
vector<int> numbers(512); | |
for(int t = 0; t < N; ++t) { | |
int size = 0; | |
in.read(reinterpret_cast<char *>(&size),sizeof(int)); | |
numbers.resize(size); | |
in.read(reinterpret_cast<char *>(&numbers[0]),sizeof(int)*size); | |
answer += doSomeComputation(&numbers[0],numbers.size()); | |
} | |
in.close(); | |
return answer; | |
} | |
int testfreadwithsetbuffer(char * name, int N) { | |
int answer = 0; | |
FILE * fd = ::fopen(name, "rb"); | |
setvbuf (fd , NULL , _IOFBF , 1024*4 ); // large buffer | |
if (fd == NULL) { | |
cerr << "problem" << endl; | |
return -1; | |
} | |
vector<int> numbers(512); | |
for(int t = 0; t < N; ++t) { | |
int size = 0; | |
if(fread(&size, sizeof(int),1,fd)!=1) { | |
cout<<"Data can't be read???"<<endl; | |
return -1; | |
} | |
numbers.resize(size); | |
if(fread(&numbers[0], sizeof(int),numbers.size(),fd)!=numbers.size()) { | |
cout<<"Data can't be read???"<<endl; | |
return -1; | |
} | |
answer += doSomeComputation(&numbers[0],numbers.size()); | |
} | |
::fclose(fd); | |
return answer; | |
} | |
int testfreadwithlargebuffer(char * name, int N) { | |
int answer = 0; | |
FILE * fd = ::fopen(name, "rb"); | |
setvbuf (fd , NULL , _IOFBF , 1024*1024*32 ); // large buffer | |
if (fd == NULL) { | |
cerr << "problem" << endl; | |
return -1; | |
} | |
vector<int> numbers(512); | |
for(int t = 0; t < N; ++t) { | |
int size = 0; | |
if(fread(&size, sizeof(int),1,fd)!=1) { | |
cout<<"Data can't be read???"<<endl; | |
return -1; | |
} | |
numbers.resize(size); | |
if(fread(&numbers[0], sizeof(int),numbers.size(),fd)!=numbers.size()) { | |
cout<<"Data can't be read???"<<endl; | |
return -1; | |
} | |
answer += doSomeComputation(&numbers[0],numbers.size()); | |
} | |
::fclose(fd); | |
return answer; | |
} | |
int testwmmap(char * name, int N, bool advise, bool shared) { | |
int answer = 0; | |
int fd = ::open(name, O_RDONLY); | |
size_t length = N * (512 + 1) * 4; | |
// for Linux: | |
#ifdef __linux__ | |
int * addr = reinterpret_cast<int *>(mmap(NULL, length, PROT_READ, MAP_FILE | (shared?MAP_SHARED:MAP_PRIVATE) | MAP_POPULATE , fd, 0)); | |
#else | |
int * addr = reinterpret_cast<int *>(mmap(NULL, length, PROT_READ, MAP_FILE | (shared?MAP_SHARED:MAP_PRIVATE), fd, 0)); | |
#endif | |
int * initaddr = addr; | |
if (addr == MAP_FAILED) { | |
cout<<"Data can't be mapped???"<<endl; | |
return -1; | |
} | |
if(advise) | |
if(madvise(addr,length,MADV_SEQUENTIAL|MADV_WILLNEED)!=0) | |
cerr<<" Couldn't set hints"<<endl; | |
close(fd); | |
for(int t = 0; t < N; ++t) { | |
int size = *addr++; | |
answer += doSomeComputation(addr,size); | |
addr+=size; | |
} | |
munmap(initaddr,length); | |
return answer; | |
} | |
int testread(char * name, int N) { | |
int answer = 0; | |
int fd = ::open(name, O_RDONLY); | |
if (fd < 0) { | |
cerr << "problem" << endl; | |
return -1; | |
} | |
vector<int> numbers(512); | |
for(int t = 0; t < N; ++t) { | |
int size = 0; | |
if(read(fd,&size, sizeof(int))!=sizeof(int)) { | |
cout<<"Data can't be read???"<<endl; | |
return -1; | |
} | |
numbers.resize(size); | |
if(read(fd,&numbers[0], sizeof(int)*numbers.size())!=sizeof(int)*numbers.size()) { | |
cout<<"Data can't be read???"<<endl; | |
return -1; | |
} | |
answer += doSomeComputation(&numbers[0],numbers.size()); | |
} | |
::close(fd); | |
return answer; | |
} | |
class WallClockTimer { | |
public: | |
struct timeval t1, t2; | |
WallClockTimer() : | |
t1(), t2() { | |
gettimeofday(&t1, 0); | |
t2 = t1; | |
} | |
void reset() { | |
gettimeofday(&t1, 0); | |
t2 = t1; | |
} | |
unsigned long long elapsed() { | |
return ((t2.tv_sec - t1.tv_sec) * 1000ULL * 1000ULL) + ((t2.tv_usec - t1. tv_usec)); | |
} | |
unsigned long long split() { | |
gettimeofday(&t2, 0); | |
return elapsed(); | |
} | |
}; | |
class CPUTimer { | |
public: | |
//clock_t t1, t2; | |
struct rusage t1,t2; | |
CPUTimer() : | |
t1(), t2() { | |
getrusage(RUSAGE_SELF, &t1); | |
//t1 = clock(); | |
t2 = t1; | |
} | |
void reset() { | |
getrusage(RUSAGE_SELF, &t1); | |
//t1 = clock(); | |
t2 = t1; | |
} | |
// proxy for userelapsed | |
unsigned long long elapsed() { | |
return totalelapsed();//userelapsed(); | |
} | |
unsigned long long totalelapsed() { | |
return userelapsed() + systemelapsed(); | |
} | |
// returns the *user* CPU time in micro seconds (mu s) | |
unsigned long long userelapsed() { | |
return ((t2.ru_utime.tv_sec - t1.ru_utime.tv_sec) * 1000ULL * 1000ULL) + ((t2.ru_utime.tv_usec - t1.ru_utime.tv_usec) | |
); | |
} | |
// returns the *system* CPU time in micro seconds (mu s) | |
unsigned long long systemelapsed() { | |
return ((t2.ru_stime.tv_sec - t1.ru_stime.tv_sec) * 1000ULL * 1000ULL) + ((t2.ru_stime.tv_usec - t1.ru_stime.tv_usec) | |
); | |
} | |
unsigned long long split() { | |
getrusage(RUSAGE_SELF, &t2); | |
return elapsed(); | |
} | |
}; | |
int main() { | |
printf("starting benchmark"); | |
const int N = 16384*32; | |
int tot = 0; | |
CPUTimer cput; | |
WallClockTimer wct; | |
for (int T = 0; T < 2; ++T) { | |
char * name = tmpnam(NULL);// unsafe but for these purposes, ok | |
fillFile(N,name); | |
printf("filled file"); | |
cout<<endl; | |
// don't report times | |
tot += testread(name,N); | |
// fread | |
cput.reset();wct.reset(); | |
for(int x = 0; x<10; ++x) tot += testfread(name,N); | |
cout<<"fread\t\t\t"<<512*N*1.0/cput.split()<<" "<<512*N*1.0/wct.split()<<endl; | |
// fread with set buffer | |
cput.reset();wct.reset(); | |
for(int x = 0; x<10; ++x) tot += testfreadwithsetbuffer(name,N); | |
cout<<"fread w sbuffer\t\t"<<512*N*1.0/cput.split()<<" "<<512*N*1.0/wct.split()<<endl; | |
// fread with large buffer | |
cput.reset();wct.reset(); | |
for(int x = 0; x<10; ++x) tot += testfreadwithlargebuffer(name,N); | |
cout<<"fread w lbuffer\t\t"<<512*N*1.0/cput.split()<<" "<<512*N*1.0/wct.split()<<endl; | |
// read | |
cput.reset();wct.reset(); | |
for(int x = 0; x<10; ++x) tot += testread(name,N); | |
cout<<"read2 \t\t\t"<<512*N*1.0/cput.split()<<" "<<512*N*1.0/wct.split()<<endl; | |
// mmap | |
cput.reset();wct.reset(); | |
for(int x = 0; x<10; ++x) tot += testwmmap(name,N,false,false); | |
cout<<"mmap \t\t\t"<<512*N*1.0/cput.split()<<" "<<512*N*1.0/wct.split()<<endl; | |
// fancy mmap | |
cput.reset();wct.reset(); | |
for(int x = 0; x<10; ++x) tot += testwmmap(name,N,true,false); | |
cout<<"fancy mmap \t\t"<<512*N*1.0/cput.split()<<" "<<512*N*1.0/wct.split()<<endl; | |
// mmap | |
cput.reset();wct.reset(); | |
for(int x = 0; x<10; ++x) tot += testwmmap(name,N,false,true); | |
cout<<"mmap (shared) \t\t"<<512*N*1.0/cput.split()<<" "<<512*N*1.0/wct.split()<<endl; | |
// fancy mmap | |
cput.reset();wct.reset(); | |
for(int x = 0; x<10; ++x) tot += testwmmap(name,N,true,true); | |
cout<<"fancy mmap (shared) \t"<<512*N*1.0/cput.split()<<" "<<512*N*1.0/wct.split()<<endl; | |
// C++ | |
cput.reset();wct.reset(); | |
for(int x = 0; x<10; ++x) tot += testwithCpp(name,N); | |
cout<<"Cpp\t\t\t"<<512*N*1.0/cput.split()<<" "<<512*N*1.0/wct.split()<<endl; | |
::remove(name); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Output on my system: