Должен ли файл C++ читать медленнее, чем Ruby или C#?

Совершенно новый для С++.

Я сравниваю различные аспекты C++, C# и Ruby, чтобы понять, есть ли необходимость в зеркалировании библиотеки. В настоящее время простое чтение файла (после обновления).

Компиляция С++ и С# в VS 2017. С++ находится в режиме выпуска (x64) (или, по крайней мере, компилируется, а затем запускается)

Библиотеки более или менее читают файл и разбивают строки на три, которые составляют элементы объекта, которые затем сохраняются в элементе массива.

Для стресс-тестирования я попробовал большой файл размером 380 МБ (7 млн ​​строк) (после обновления), теперь получая аналогичную производительность с C++ и Ruby,

Чисто читая файл и ничего не делая, производительность выглядит следующим образом:

Ruby: 7s
C#:   2.5s
C++:  500+s (stopped running after awhile, something's clearly wrong)
C++(release build x64): 7.5s

Код:

#Ruby
file = File.open "test_file.txt"
while !file.eof 
    line = file.readline
end

//C#
StreamReader file = new StreamReader("test_file.txt");
file.Open();
while((line = file.ReadLine()) != null){

}



//C++
#include "stdafx.h"
#include "string"
#include "iostream"
#include "ctime"
#include "fstream"
int main()
{
    std::ios::sync_with_stdio(false);
    std::ifstream file;
    file.open("c:/sandboxCPP/test_file.txt");
    std::string line;

    std::clock_t start;
    double duration;
    start = std::clock();
    while (std::getline(file, line)) {

    }
    duration = (std::clock() - start) / (double)CLOCKS_PER_SEC;
    std::cout << "\nDuration: " << duration;
    while (true) 
    {

    }
    return 0;
}

Изменить: следующее работало невероятно хорошо. 0,03 с

vector<string> lines;
string tempString = str.str();
boost::split(lines, tempString, boost::is_any_of("\n"));
start = clock();
cout << "\nCount: " << lines.size();
int count = lines.size();
string s;
for (int i = 0; i < count; i++) {
    s =  lines[i];
} 

s = на вероятность того, что я не знаю, что делает повышение. Изменена производительность.

Протестировано с помощью cout случайной записи в конце цикла.

Спасибо


person CBusBus    schedule 05.01.2018    source источник
comment
Вы запускаете его в режиме выпуска? Вы используете родной С++?   -  person drescherjm    schedule 06.01.2018
comment
@drescherjm Я запускаю его прямо из VS 2017. Используя ctrl+shift+b, затем ctrl+f5.   -  person CBusBus    schedule 06.01.2018
comment
Не могли бы вы опубликовать свой фактический полный исходный код. Предоставленный вами код С++ не должен компилироваться (в нем отсутствует закрывающая скобка).   -  person Jarra McIntyre    schedule 06.01.2018
comment
Я думаю, что вы используете отладочную сборку, переключите свою конфигурацию на выпуск.   -  person drescherjm    schedule 06.01.2018
comment
@JarraMcIntyre Конечно, две минуты   -  person CBusBus    schedule 06.01.2018
comment
@drescherjm Это не может быть всей проблемой или даже большей ее частью. Это не должно привести к такому замедлению.   -  person Daniel H    schedule 06.01.2018
comment
Я видел, как режим отладки Visual Studio занимает в 100 раз больше времени, чем режим выпуска. Однако я не ожидал, что здесь будет так. В моем случае основной проблемой было много аллокаций и проверок кучи.   -  person drescherjm    schedule 06.01.2018
comment
См. это (возможно, дубликат): stackoverflow.com/q/6820765/10077 Вставьте std::ios::sync_with_stdio(false); туда и посмотрите, ускоряется ли он. резко вверх.   -  person Fred Larson    schedule 06.01.2018
comment
Независимо от причины замедления, профилировать нерелизную сборку бессмысленно. Вы буквально просите компилятор производить более легкий для отладки вывод вместо быстрого вывода в режиме отладки, так зачем его измерять?   -  person GManNickG    schedule 06.01.2018
comment
@JarraMcIntyre. Спасибо, я добавил код. Между ними было много закомментированного кода, но в остальном он взят из исходников.   -  person CBusBus    schedule 06.01.2018
comment
@GManNickG. Мне не нужно точно знать, насколько хорошо он работает в сравнении, просто приблизительное представление. Я новичок в С++, поэтому я подумал, что стоит проверить.   -  person CBusBus    schedule 06.01.2018
comment
Также попробуйте его в режиме Release и сообщите о разнице в скорости.   -  person Adrian McCarthy    schedule 06.01.2018
comment
@FredLarson Спасибо за предложение. Это не имело большого значения.   -  person CBusBus    schedule 06.01.2018
comment
См. также stackoverflow.com/q/39309463/10077. может быть убийцей производительности.   -  person Fred Larson    schedule 06.01.2018
comment
Запуск кода из Visual Studio также может быть проблемой. Он прикрепляет ловушки отладки, чтобы перехватывать исключения и другие ошибки. Даже если вы используете сборку Release. Результаты могут быть другими, если вы просто запустите его из командного окна.   -  person Zan Lynx    schedule 06.01.2018
comment
Ваш код c++ неправильно обрабатывает i. Я не вижу увеличения. Также вы должны использовать std::chrono для синхронизации.   -  person drescherjm    schedule 06.01.2018
comment
Хорошо, скомпилировано в режиме релиза и урезано примерно до той же скорости, что и Ruby.   -  person CBusBus    schedule 06.01.2018
comment
Хорошее начало. У вас все еще есть std::ios::sync_with_stdio(false); в коде?   -  person user4581301    schedule 06.01.2018
comment
@user4581301 user4581301 Да, я оставил это.   -  person CBusBus    schedule 06.01.2018
comment
@CBusBus Глядя на полный код, я не вижу причин, по которым это заняло бы так много времени. Описанные вами симптомы согласуются с зависшим процессом. Я бы сначала создал небольшой тестовый файл и проверил, работает ли ваш код. Если он не пытается пройти через отладчик.   -  person Jarra McIntyre    schedule 06.01.2018
comment
@JarraMcIntyre Круто. Не уверен, с чего начать, хотя я впервые действительно прикоснулся к С++.   -  person CBusBus    schedule 06.01.2018
comment
Ваш цикл никогда не увеличивает i; он никогда не будет печатать, даже если он читает файл.   -  person Daniel H    schedule 06.01.2018
comment
@DanielH Извините, я случайно удалил это в предыдущем редактировании. Добавил снова   -  person CBusBus    schedule 06.01.2018
comment
@CBusBus Было бы лучше, если бы у нас был полный файл. Сохраните новый файл, удалив весь закомментированный код, затем попробуйте скомпилировать и запустить его. В нем должны быть такие вещи, как #include <iostream>, int main и т. д.   -  person Daniel H    schedule 06.01.2018
comment
@CBusBus Отладка C++ такая же, как C#. Вы можете установить точку останова в VS, щелкнув поле LHS. Затем нажмите кнопку запуска и пройдите. Есть несколько причин, по которым консольная программа может зависнуть. Например. ожидание ввода (откуда взялась переменная пути?), блокировка на стандартном выводе, если текст выделен в консоли, или если отладчик подключен, ожидая, пока пользователь нажмет «продолжить/далее», или застрял в бесконечном цикле (в код не выложен?). Если вам пришлось принудительно завершить работу через 500 секунд, это признак блокировки/застревания в бесконечном цикле, а не медленного C++. Это просто слишком медленно.   -  person Jarra McIntyre    schedule 06.01.2018
comment
@JarraMcIntyre Я использовал таймер в функции, чтобы убедиться, что это не бесконечный цикл. Я только что протестировал цикл, в котором ничего не было, и часы внутри функции, время для 1,2 млн строк составляет 1,6 с, что примерно на 0,3 с больше, чем у Ruby. На самом деле никогда не используйте точки останова, обычно просто что-то убивает его, как только оно достигает чего-то, или печатает на консоли.   -  person CBusBus    schedule 06.01.2018
comment
@ДэниелХ. Спасибо. Я переместил весь код в автономное консольное приложение. Добавил источник выше.   -  person CBusBus    schedule 06.01.2018
comment
И теперь пришло время очистить весь вопрос, показывая только релевантные вещи вместо серии правок (тем более, что показанный код не делает ничего подобного тому, что вы описываете в первых абзацах). В идеале добавьте краткий рассказ о том, как создать тестовый входной файл. Так что мы смотрим здесь на одно и то же (и не каждый из нас должен искать большой текстовый файл)...   -  person DevSolar    schedule 06.01.2018
comment
И здесь что-то дико не так. С файлом размером 3 МБ (который обрабатывается за ‹1 секунду в реальном времени) я получаю результат 15. Это очевидно неправильно.   -  person DevSolar    schedule 06.01.2018
comment
Ну, это неправильно: duration = std::clock() - start / (double)CLOCKS_PER_SEC; Должно быть duration = (std::clock() - start) / (double)CLOCKS_PER_SEC;.   -  person Fred Larson    schedule 06.01.2018
comment
@DevSolar. Извините, мысль не пришла мне в голову. Я обновил вопрос.   -  person CBusBus    schedule 06.01.2018
comment
@FredLarson: Да, я только что сам туда попал (напечатав start и end clock_t напрямую). Пора ложиться спать, если я сразу не обнаружу проблему с приоритетом операторов. ;-)   -  person DevSolar    schedule 06.01.2018
comment
@FredLarson Моя ошибка. Я написал это снова, когда перешел на версию приложения для одной консоли. Результаты были последовательными, поэтому он никогда не выделялся. Обновил и запустил снова с тем же результатом.   -  person CBusBus    schedule 06.01.2018
comment
@CBusBus: Не могу сравнить, так как у меня нет вашего файла, но я получаю 1.1 с на 390 МБ исходного кода LaTeX (Cygwin64 GCC 6.4.0 по адресу -O3)...   -  person DevSolar    schedule 06.01.2018
comment
@DevSolar Моему ноутбуку несколько лет, так что это может быть как-то связано с этим, но я ожидал, что C ++ будет, по крайней мере, работать так же хорошо, как C #. Независимо от того, что я меняю, это, похоже, не улучшается (кроме более раннего предложения компилировать как релиз (никогда не знал, что это было до сегодняшнего дня)).   -  person CBusBus    schedule 06.01.2018
comment
Удаление бесконечного цикла в конце должно помочь C++ завершить работу быстрее.   -  person Eljay    schedule 06.01.2018


Ответы (2)


Судя по комментариям и первоначально опубликованному коду (теперь он исправлен [теперь удален]), ранее была ошибка кодирования (отсутствует i++), из-за которой программа C++ не могла ничего выводить. Этот плюс цикл while(true) в полном примере кода будет представлять симптомы, соответствующие тем, которые указаны в вопросе (т. Е. Пользователь ждет 500 с, не видит вывода и принудительно завершает программу). Это потому, что он завершит чтение файла, ничего не выводя, и войдет в преднамеренно добавленный бесконечный цикл.

Пересмотренный полный исходный код правильно завершается (согласно комментариям) примерно за 1,6 с для файла размером 1,2 миллиона. Мой совет по улучшению производительности будет следующим:

  1. Убедитесь, что вы компилируете в режиме выпуска (не в режиме отладки). Учитывая, что пользователь указал, что он использует Visual Studio 2017, я бы рекомендовал просмотреть официальную документацию Microsoft (https://msdn.microsoft.com/en-us/library/wx0123s5.aspx) для подробного объяснения.

  2. Чтобы упростить диагностику проблем, не добавляйте бесконечный цикл в конец вашей программы. Вместо этого запустите исполняемый файл из powershell / (cmd) и убедитесь, что он завершается правильно.

РЕДАКТИРОВАТЬ: я бы также добавил:

  1. Для точного тайминга вам также необходимо учитывать дисковый кеш ОС. Запустите каждый тест несколько раз, чтобы «разогреть» кеш диска.
person Jarra McIntyre    schedule 05.01.2018
comment
Спасибо за совет. В конце концов я использовал boost::split с циклом, и он сократился до 0,03 с для 1,24 млн строк. Я принял ответ, поскольку режим отладочной компиляции был серьезной проблемой. - person CBusBus; 06.01.2018
comment
Нет проблем. Рад, что это решено. Спасибо за согласие. - person Jarra McIntyre; 06.01.2018

C++ не записывает все автоматически в тот момент, когда вы ему приказываете. Вместо этого он буферизует данные, чтобы записать их все сразу, что обычно происходит быстрее. Чтобы сказать «Я действительно хочу написать это сейчас», вам нужно сказать что-то вроде std::cout << std::flush (если вы используете std::endl для окончания строк, это происходит автоматически).

Обычно этого делать не нужно; буферы сбрасываются при выходе из программы, или когда вы запрашиваете ввод от пользователя, или что-то в этом роде. Однако ваша программа не завершается, поэтому она никогда не очищает свой буфер. Вы читаете ввод, а затем программа выполняет while(true) вечно, никогда не выдавая вывод.

Решение здесь простое: убрать цикл while в конце программы. У вас не должно быть этого; люди обычно предполагают, что консольная программа завершает работу после завершения. Я бы предположил, что у вас это было, потому что Visual Studio автоматически закрывала окно консоли, когда программа была завершена, но, по-видимому, она не делает этого с помощью Ctrl+F5, что вы используете, так что я не уверен.

person Daniel H    schedule 05.01.2018
comment
В любом случае, '\n' не вызовет флеш? Он не будет бесконечно буферизовать афакт. - person Jarra McIntyre; 06.01.2018
comment
@JarraMcIntyre Это очистит буферы ОС (я думаю, я больше знаком с такими низкоуровневыми деталями POSIX), но iostreams имеет свои собственные внутренние буферы. Я только что подтвердил (опять же, не в Windows), что std::cout << "Hello, world!\n";, за которым сразу же следует бесконечный цикл, или std::abort(); ничего не будет печатать при выключенном sync_with_stdio (однако будет, если я закомментирую эту строку, что меня удивило). - person Daniel H; 06.01.2018
comment
К сожалению, я был сбит с толку тем, где происходит часть буферизации, поэтому я был удивлен эффектом sync_with_stdio. Если вы синхронизируетесь, то вы определенно будете буферизованы строкой (поскольку базовые потоки C) и \n вызывает сброс. Если вы отключите sync_with_stdio, то я не думаю, что в iostreams вообще есть концепция буферизации строк. - person Daniel H; 06.01.2018
comment
Согласно ссылке cpp, cout предоставляет некоторые гарантии в отношении того, когда он сбрасывается (std::flush, std::endl, перед вызовами std::system), но определяется ли реализация сброса '\n'. Я полагаю, что ваш тест показывает, что он сбрасывает реализацию MS stl, если включен sync_with_stdio. Это имеет смысл, так как IIRC, стандартный вывод сбрасывает на «\ n». Если вы отключите эту синхронизацию, то std::cout может использовать свою собственную реализацию буферизации, которая не обязательно имеет такое же поведение. - person Jarra McIntyre; 06.01.2018