Итак, в нескольких комментариях я повторил ответы людей, что проблема, вероятно, заключалась в дополнительном копировании, выполненном вашей версией C ++, где она копирует строки в память в виде строки. Но я хотел это проверить.
Сначала я реализовал версии fgetc и getline и рассчитал их время. Я подтвердил, что в режиме отладки версия getline работает медленнее, около 130 мкс против 60 мкс для версии fgetc. Это неудивительно, учитывая общепринятое мнение, что iostreams медленнее, чем использование stdio. Однако в прошлом я знал, что iostreams значительно ускоряются благодаря оптимизации. Это подтвердилось, когда я сравнил время выхода в режим релиза: около 20 мкс с использованием getline и 48 мкс с использованием fgetc.
Тот факт, что использование getline с iostreams быстрее, чем fgetc, по крайней мере в режиме выпуска, противоречит тому, что копирование всех этих данных должно выполняться медленнее, чем не копирование, поэтому я не уверен, на что способна вся оптимизация чтобы избежать, и я действительно не пытался найти какое-либо объяснение, но было бы интересно понять, что оптимизируется. редактировать: когда я смотрел на программы с профилировщиком, было неочевидно, как сравнивать производительность, так как разные методы выглядели настолько отличающимися друг от друга
Кстати, я хотел посмотреть, смогу ли я получить более быструю версию, избегая копирования с использованием метода get()
для объекта fstream и просто точно делая то, что делает C-версия. Когда я сделал это, я был очень удивлен, обнаружив, что использование fstream::get()
было немного медленнее, чем методы fgetc и getline в отладке и выпуске; Около 230 мкс в отладке и 80 мкс в версии.
Чтобы сузить все замедления, я пошел дальше и сделал другую версию, на этот раз используя stream_buf, прикрепленный к объекту fstream, и метод snextc()
для этого. Эта версия, безусловно, самая быстрая; 25 мкс в отладке и 6 мкс в выпуске.
Я предполагаю, что метод, который делает fstream::get()
намного медленнее, заключается в том, что он создает объекты часового для каждого вызова. Хотя я не проверял это, я не вижу, что get()
делает гораздо больше, чем просто получает следующий символ из stream_buf, за исключением этих сторожевых объектов.
В любом случае, мораль этой истории в том, что если вы хотите быстро io, вам, вероятно, лучше использовать функции iostream высокого уровня, а не stdio, и для действительно быстрого доступа к базовому stream_buf. edit: на самом деле эта мораль может относиться только к MSVC, см. Обновление внизу для результатов из другого набора инструментов.
Для справки:
Я использовал VS2010 и Chrono от Boost 1.47 для синхронизации. Я построил 32-битные двоичные файлы (кажется, требуется для Chrono Boost, потому что он не может найти 64-битную версию этой библиотеки). Я не настраивал параметры компиляции, но они могут быть не совсем стандартными, так как я делал это в проекте «нуля против проекта», который я храню.
Файл, который я тестировал, представлял собой открытую текстовую версию Oeuvres Complètes de Frédéric Bastiat объемом 11 000 МБ, том 1 Фредерика Бастиата из Project Gutenberg, http://www.gutenberg.org/ebooks/35390
Время выхода из режима
fgetc time is: 48150 microseconds
snextc time is: 6019 microseconds
get time is: 79600 microseconds
getline time is: 19881 microseconds
Время режима отладки:
fgetc time is: 59593 microseconds
snextc time is: 24915 microseconds
get time is: 228643 microseconds
getline time is: 130807 microseconds
Вот моя 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';
}
Вот моя 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';
}
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';
}
и snextc()
версия
{
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';
}
Обновление:
Я перезапустил тесты, используя clang (trunk) на OS X с libc ++. Результаты для реализаций на основе iostream остались относительно одинаковыми (с включенной оптимизацией); fstream::get()
намного медленнее, чем std::getline()
намного медленнее, чем filebuf::snextc()
. Но производительность fgetc()
улучшилась по сравнению с реализацией getline()
и стала быстрее. Возможно, это связано с тем, что копирование, выполняемое getline()
, становится проблемой для этого набора инструментов, тогда как для MSVC этого не было? Может быть, реализация fgetc () в CRT от Microsoft плохая или что-то в этом роде?
В любом случае, вот время (я использовал гораздо больший файл, 5,3 МБ):
с использованием -Os
fgetc time is: 39004 microseconds
snextc time is: 19374 microseconds
get time is: 145233 microseconds
getline time is: 67316 microseconds
с использованием -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