So, in a few comments, I repeated the answers of people that the problem is most likely an additional copy made by your version in C ++, where it copies the lines to memory in line. But I wanted to check it out.
First I implemented the fgetc and getline versions and timed them. I confirmed that in debug mode, the getline version is slower, about 130 μs versus 60 μs for the fgetc version. This is not surprising, given the traditional wisdom that iostreams are slower than using stdio. However, in the past it seemed to me that iostreams significantly accelerated from optimization. This was confirmed when I compared the release time: about 20 μs using getline and 48 μs using fgetc.
The fact that using getline with iostreams is faster than fgetc, at least in release mode, contradicts the argument that copying all this data should be slower than not copying, so I'm not sure that all optimization is to avoid, and I really did not look for any explanation, but it would be interesting to understand what needs to be optimized. edit: when I looked at programs with a profiler, it was unclear how to compare performance, since the different methods looked so different from each other
Anwyay I wanted to see if I could get a faster version by avoiding copying using the get() method in the fstream object and just do exactly what version C does. When I did this, I was very surprised to find that using fstream::get() was rather slow than the fgetc and getline methods both in debugging and in release; About 230 μs for debugging and 80 μs for Release.
To narrow down all the slowdowns, I went ahead and made another version, this time using stream_buf attached to the fstream object and the snextc() method. This version is by far the fastest; 25 μs in debugging and 6 μs in release.
I assume that what the fstream::get() method does is so slow that it creates watchdog objects for every call. Although I have not tested this, I do not see that get() does much more than just get the next character from stream_buf, with the exception of these watchdog objects.
Anyway, the moral of this story is that if you want io fast, you are probably best off using high-level iostream functions, not stdio, but for really quick access to the underlying stream_buf. edit: in fact, this moral can only be applied to MSVC, see the update below to get results from another toolchain.
For reference:
I used VS2010 and chrono from boost 1.47 for synchronization. I built 32-bit binaries (it seems chrono acceleration is required because it cannot find the 64-bit version of this library). I did not configure the compilation options, but they may not have been completely standard, since I did this in the Screenshot Against Me project.
The file I tested with was a version of the text version of Oeuvres Complétes de Frédéric Bastiat in version 1.1 MB of 20,000 lines, volume 1 from Frédéric Bastiat from Project Gutenberg, http://www.gutenberg.org/ebooks/35390
Unlock Time
fgetc time is: 48150 microseconds snextc time is: 6019 microseconds get time is: 79600 microseconds getline time is: 19881 microseconds
Debug time:
fgetc time is: 59593 microseconds snextc time is: 24915 microseconds get time is: 228643 microseconds getline time is: 130807 microseconds
Here is my version of fgetc() :
{ auto begin = boost::chrono::high_resolution_clock::now(); FILE *cin = fopen("D:/bames/automata/pg35390.txt","rb"); assert(cin); unsigned maxLength = 0; unsigned i = 0; int ch; while(1) { ch = fgetc(cin); if(ch == 0x0A || ch == EOF) { maxLength = std::max(i,maxLength); i = 0; if(ch==EOF) break; } else { ++i; } } fclose(cin); auto end = boost::chrono::high_resolution_clock::now(); std::cout << "max line is: " << maxLength << '\n'; std::cout << "fgetc time is: " << boost::chrono::duration_cast<boost::chrono::microseconds>(end-begin) << '\n'; }
Here is my version of getline() :
{ auto begin = boost::chrono::high_resolution_clock::now(); std::ifstream fin("D:/bames/automata/pg35390.txt",std::ios::binary); unsigned maxLength = 0; std::string line; while(std::getline(fin,line)) { maxLength = std::max(line.size(),maxLength); } auto end = boost::chrono::high_resolution_clock::now(); std::cout << "max line is: " << maxLength << '\n'; std::cout << "getline time is: " << boost::chrono::duration_cast<boost::chrono::microseconds>(end-begin) << '\n'; }
version of fstream::get()
{ auto begin = boost::chrono::high_resolution_clock::now(); std::ifstream fin("D:/bames/automata/pg35390.txt",std::ios::binary); unsigned maxLength = 0; unsigned i = 0; while(1) { int ch = fin.get(); if(fin.good() && ch == 0x0A || fin.eof()) { maxLength = std::max(i,maxLength); i = 0; if(fin.eof()) break; } else { ++i; } } auto end = boost::chrono::high_resolution_clock::now(); std::cout << "max line is: " << maxLength << '\n'; std::cout << "get time is: " << boost::chrono::duration_cast<boost::chrono::microseconds>(end-begin) << '\n'; }
and snextc() version
{ auto begin = boost::chrono::high_resolution_clock::now(); std::ifstream fin("D:/bames/automata/pg35390.txt",std::ios::binary); std::filebuf &buf = *fin.rdbuf(); unsigned maxLength = 0; unsigned i = 0; while(1) { int ch = buf.snextc(); if(ch == 0x0A || ch == std::char_traits<char>::eof()) { maxLength = std::max(i,maxLength); i = 0; if(ch == std::char_traits<char>::eof()) break; } else { ++i; } } auto end = boost::chrono::high_resolution_clock::now(); std::cout << "max line is: " << maxLength << '\n'; std::cout << "snextc time is: " << boost::chrono::duration_cast<boost::chrono::microseconds>(end-begin) << '\n'; }
update:
I repeated the tests using clang (trunk) in OS X with libC ++. The results for iostream-based implementations remained relatively the same (with optimization enabled); fstream::get() much slower than std::getline() much slower than filebuf::snextc() . But the performance of fgetc() improved relative to the implementation of getline() and has become faster. Perhaps this is due to the fact that copying performed by getline() becomes a problem of this tool chain, while it is not with MSVC? Perhaps the implementation of fgetc () in Microsoft CRT is bad or something like that?
Anyway, here are the times (I used a much larger file, 5.3 MB):
using -os
fgetc time is: 39004 microseconds snextc time is: 19374 microseconds get time is: 145233 microseconds getline time is: 67316 microseconds
using -O0
fgetc time is: 44061 microseconds snextc time is: 92894 microseconds get time is: 184967 microseconds getline time is: 209529 microseconds
-O2
fgetc time is: 39356 microseconds snextc time is: 21324 microseconds get time is: 149048 microseconds getline time is: 63983 microseconds
-O3
fgetc time is: 37527 microseconds snextc time is: 22863 microseconds get time is: 145176 microseconds getline time is: 67899 microseconds