Использование ofstream для вывода буферизованного текста для повышения производительности - PullRequest
9 голосов
/ 24 февраля 2012

Мне нужно написать программу, которая будет записывать много символов в выходной файл.Моей программе также нужно написать новую строку для лучшего форматирования.Я понимаю, что ofstream является буферизованным потоком, и если мы используем буферизованный поток для файла io, мы повышаем производительность.Однако, если мы используем std::endl, выход будет сброшен, и мы потеряем любой потенциальный прирост производительности из-за буферизованного вывода.

Полагаю, если я использую '\n' для новой строки, вывод будет только сброшенкогда мы будем std::endl.Это правильно?И есть ли какие-то приемы, которые можно использовать для повышения производительности при выводе файла?

Примечание. Я хочу очистить буферизованный вывод при завершении операций записи в файл.Я думаю, что таким образом я могу минимизировать файловый ввод-вывод и, таким образом, повысить производительность.

Ответы [ 3 ]

18 голосов
/ 24 февраля 2012

Обычно пользователи потоковых классов не должны связываться со сбросом потока, если требуется максимальная производительность: потоки внутренне очищают свой буфер, когда он заполнен.Это на самом деле более эффективно, чем ожидание, пока все выходные данные не будут готовы, особенно для больших файлов: буферизованные данные записываются, пока они все еще находятся в памяти.Если вы создаете огромный буфер и записываете его только один раз, система виртуальной памяти поместит части данных на диск, но не файл.Его нужно будет прочитать с диска и записать снова.

Главное в отношении std::endl состоит в том, что люди злоупотребляют этим концом строки, что приводит к сбросу буфера, и они не знают о последствиях для производительности.Намерение std::endl состоит в том, чтобы люди могли контролировать файлы в разумные моменты времени.Чтобы это было эффективным, им нужно знать, что они делают.К сожалению, было слишком много людей, неосведомленных о том, что делает std::endl, которые рекламировали его использование в качестве окончания строки, так что оно используется во многих местах, где это явно неправильно.

Тем не менее, ниже приведен рядвещи, которые вы могли бы попытаться улучшить производительность.Я предполагаю, что вам нужен форматированный вывод (который вам не даст использование std::ofstream::write()).

  • Очевидно, что не используйте std::endl, если вам не нужно.Если код записи уже существует и использует std::endl во многих местах, некоторые из которых, возможно, находятся вне вашего контроля, вы можете использовать буфер потока фильтрации, который использует свой внутренний буфер разумного размера и который не перенаправляет вызовы на его sync()Функция в основной буфер потока.Хотя для этого требуется дополнительная копия, это лучше, чем некоторые ложные сбросы, поскольку это на несколько порядков дороже.
  • Хотя это не должно влиять на std::ofstream s, вызывая std::ios_base::sync_with_stdio(false), используемый дляпроизводительность на некоторых реализациях.Возможно, вы захотите взглянуть на использование другой реализации IOstream, если это даст эффект, потому что, вероятно, с производительностью не все в порядке.
  • Убедитесь, что вы используете std::locale, std::codecvt<...> которого возвращает true при вызове его always_noconv().Это легко проверить с помощью std::use_facet<std::codecvt<char, char, stdd::mbstate_t> >(out.get_loc()).always_noconv().Вы можете использовать std::locale("C"), чтобы получить std::locale, для которого это должно быть верно.
  • Некоторые реализации локали используют очень неэффективные реализации своих числовых аспектов и даже, если они достаточно хороши, реализация по умолчаниюиз std::num_put<char> фасета все еще может делать то, что вам действительно не нужно.Особенно, если ваше числовое форматирование достаточно простое, то есть вы не меняете флаги форматирования, вы не заменяете отображение символов (т.е. вы не используете забавный std::ctype<char> фасет) и т. Д., Может быть разумно использоватьпользовательский std::num_put<char> фасет: довольно просто создать быструю, но простую функцию форматирования для целочисленных типов и хорошую функцию форматирования для чисел с плавающей запятой, которая не использует snprintf() внутри.

Некоторыелюди предложили использовать файлы с отображением в памяти, но это разумно, если размер целевого файла известен заранее.Если это так, то это отличный способ улучшить производительность, иначе это не стоит беспокоиться.Обратите внимание, что вы можете использовать форматирование потока для файлов с отображенной памятью (или, в более общем случае, с любым интерфейсом вывода), создав пользовательский std::streambuf, который использует интерфейс отображения памяти.Я обнаружил, что отображение памяти иногда эффективно при использовании их с std::istream s.Во многих случаях различия не имеют большого значения.

Давным-давно я написал свою собственную реализацию IOStreams и locales, которая не страдает от некоторых проблем производительности, упомянутых выше (она доступна в мой сайт , но он немного устаревший, и я почти 10 лет не трогал его).Есть много вещей, которые могут быть улучшены по сравнению с этой реализацией, но у меня нет современной реализации, которую я был бы готов опубликовать где-нибудь.Надеюсь, скоро - то, о чем я думаю уже почти 10 лет ...

3 голосов
/ 24 февраля 2012

Печать \n не будет (обязательно) сбрасывать вывод, в то время как печать std::endl или std::flush будет.

Если вы хотите быструю запись и не заботитесь, есть ли данные довсе готово, затем выполните все свои действия с помощью \n и не беспокойтесь об этом (поскольку закрытие файла также очистит поток).

Если вы 'Вы по-прежнему не получаете желаемой производительности, вы можете использовать fstream :: read (char *, int) - это позволяет вам читать данные в блоках любого размера, которые вы хотите (попробуйте блоки большего размера и посмотрите, поможет ли это).

0 голосов
/ 08 сентября 2016

Да, endl очищает поток. Не используйте его для больших файлов.

Также убедитесь, что установлен буфер потока . По крайней мере, реализация MSVC копирует 1 символ за раз в filebuf, когда не установлен буфер (см. streambuf::xsputn). Это может сделать ваше приложение связанным с процессором, что приведет к снижению скорости ввода-вывода.

Итак, добавьте что-то подобное в ваш код, прежде чем писать:

char buf[256 * 1024];
mystream.rdbuf()->pubsetbuf(buf, sizeof(buf));

Примечание: полный образец приложения можно найти здесь .

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...