Какой метод является самым быстрым для высокопроизводительного последовательного файлового ввода-вывода в C ++? - PullRequest
48 голосов
/ 29 июля 2009

Предполагая следующее для ...
Выход:
Файл открывается ...
Данные передаются на диск. Данные в памяти находятся в большом непрерывном буфере. Он записывается на диск в необработанном виде непосредственно из этого буфера. Размер буфера настраивается, но фиксирован на время потока. Буферы записываются в файл один за другим. Операции поиска не проводятся.
... файл закрыт.

Введите:
Большой файл (последовательно записанный выше) читается с диска от начала до конца.


Существуют ли общепринятые рекомендации для достижения максимально быстрого последовательного файлового ввода-вывода в C ++?

Некоторые возможные соображения:

  • Рекомендации по выбору оптимального размера буфера
  • Будет ли переносимая библиотека типа boost :: asio слишком абстрагированной, чтобы раскрывать тонкости конкретной платформы, или они могут считаться оптимальными?
  • Всегда ли асинхронный ввод-вывод предпочтительнее синхронного? Что если приложение не связано с другими процессорами?

Я понимаю, что это будет зависеть от платформы. Я приветствую общие рекомендации, а также рекомендации для конкретных платформ.
(мой самый непосредственный интерес к Win x64, но меня также интересуют комментарии по Solaris и Linux)

Ответы [ 7 ]

31 голосов
/ 29 июля 2009

Существуют ли общепринятые рекомендации для достижения максимально быстрого последовательного файлового ввода-вывода в C ++?

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

  1. Используйте отображенную память вместо записи в файлы. Это не всегда быстрее, но дает возможность оптимизировать ввод-вывод специфичным для операционной системы, но относительно переносимым способом, избегая ненужного копирования и используя знания ОС о том, как на самом деле используется диск. («Переносимый», если вы используете оболочку, а не вызов API для конкретной ОС).

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

Некоторые возможные соображения:

  • Рекомендации по выбору оптимального размера буфера

Размер страницы для начинающих, но будьте готовы к настройке оттуда.

  • Будет ли переносимая библиотека типа boost :: asio слишком абстрагированной, чтобы раскрывать тонкости конкретной платформы, или их можно считать оптимальными?

Не думайте, что это оптимально. Это зависит от того, насколько тщательно библиотека работает на вашей платформе, и от того, сколько усилий разработчики приложили для ее ускорения. Сказав, что переносимая библиотека ввода-вывода может быть очень быстрой, потому что быстрые абстракции существуют в большинстве систем, и обычно можно придумать общий API, который охватывает множество основ. Насколько мне известно, Boost.Asio достаточно хорошо настроен для конкретной платформы: для быстрого асинхронного ввода-вывода существует целое семейство API-интерфейсов для конкретных ОС и вариантов ОС (например, epoll * 1040). *, / dev / epoll , kqueue , Windows перекрывает ввод-вывод ), и Asio оборачивает их всех.

  • Всегда ли асинхронный ввод-вывод предпочтительнее синхронного? Что если приложение не связано с другими процессорами?

Асинхронный ввод-вывод не является быстрым в необработанном смысле, чем синхронный ввод-вывод. Что делает асинхронный ввод-вывод, так это гарантирует, что ваш код не тратит время на ожидание завершения ввода-вывода. В общем, это быстрее, чем другой метод не тратить это время, а именно, используя потоки, потому что он обратится к вашему коду, когда ввод-вывод будет готов, а не раньше. Нет никаких фальстартов или проблем с незанятыми потоками, которые необходимо прервать.

12 голосов
/ 29 июля 2009

Общий совет - отключить буферизацию и чтение / запись большими кусками (но не слишком большими, тогда вы будете тратить слишком много времени на ожидание завершения всего ввода-вывода, в противном случае вы могли бы начать жевать в первый раз мегабайт. С помощью этого алгоритма легко найти точку соприкосновения, нужно повернуть только одну ручку: размер куска).

Кроме того, для ввода mmap() использование общего и доступного только для чтения файла является (если не самым быстрым, тогда) наиболее эффективным способом. Вызовите madvise(), если у вашей платформы есть, чтобы сообщить ядру, как вы будете проходить по файлу, чтобы он мог выполнить чтение вперед и снова быстро выбросить страницы.

Для вывода, если у вас уже есть буфер, рассмотрите возможность подкрепления его файлом (также с mmap()), чтобы вам не приходилось копировать данные в пользовательском пространстве.

Если mmap() вам не по вкусу, то есть fadvise(), а для действительно сложных асинхронный файл ввода / вывода.

(все вышеперечисленное - POSIX, имена Windows могут отличаться).

7 голосов
/ 29 июля 2009

Для Windows вы должны убедиться, что используете вызов FILE_FLAG_SEQUENTIAL_SCAN в вызове CreateFile (), если вы решите использовать вызов Windows API для конкретной платформы. Это оптимизирует кэширование для ввода / вывода. Что касается размеров буфера, то обычно рекомендуется размер буфера, кратный размеру сектора диска. 8K - хорошая отправная точка с небольшим выигрышем от увеличения.

В этой статье рассматривается сравнение между асинхронностью и синхронизацией в Windows.

http://msdn.microsoft.com/en-us/library/aa365683(VS.85).aspx

3 голосов
/ 29 июля 2009

Как вы отметили выше, все зависит от компьютера / системы / библиотек, которые вы используете. Быстрое решение в одной системе может быть медленным в другой.

Тем не менее, общая рекомендация заключается в том, чтобы записывать как можно больше блоков.
Обычно запись байта за раз является самой медленной.

Лучший способ узнать наверняка - это написать несколько разных способов и профилировать их.

2 голосов
/ 21 февраля 2017

В Linux буферизованное чтение и запись значительно ускоряют процесс, все больше с увеличением размеров буферов, но отдача уменьшается, и вы, как правило, хотите использовать BUFSIZ (определяется stdio.h), так как больший размер буфера выиграл ' не очень помогает.

mmap ing обеспечивает самый быстрый доступ к файлам, но сам вызов mmap довольно дорог. Для небольших файлов (16 КБ) read и write системные вызовы win (см. https://stackoverflow.com/a/39196499/1084774 для чисел при чтении через read и mmap).

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

Вы получите самую быструю производительность, используя CreateFile и ReadFile. Откройте файл с помощью FILE_FLAG_SEQUENTIAL_SCAN.

Чтение с размером буфера, равным степени двойки. Только бенчмаркинг может определить это число. Я видел, что это было 8K один раз. В другой раз я обнаружил, что это 8M! Это сильно варьируется.

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

Отображение памяти не самый быстрый способ. У него больше накладных расходов, потому что вы не можете контролировать размер блока, а ОС должна отказывать на всех страницах.

2 голосов
/ 30 июля 2009

Вы спрашивали о C ++, но, похоже, вы уже прошли через это и готовы немного узнать о платформе.

В Windows FILE_FLAG_SEQUENTIAL_SCAN с сопоставлением файлов, вероятно, самый быстрый способ. Фактически, ваш процесс может завершиться до того, как файл действительно попадет на диск. Без явной блокировки операции очистки Windows может начать писать эти страницы до 5 минут.

Вы должны быть осторожны, если файлы находятся не на локальных устройствах, а на сетевом диске. Сетевые ошибки будут отображаться как ошибки SEH, которые вам нужно будет подготовить для обработки.

В * nixes вы можете получить более высокую производительность при последовательной записи на устройство с необработанным диском. Это возможно и в Windows, но не так хорошо поддерживается API. Это позволит избежать небольших накладных расходов на файловую систему, но может оказаться недостаточным, чтобы быть полезным.

Грубо говоря, ОЗУ в 1000 и более раз быстрее, чем диски, а ЦП все еще быстрее. Вероятно, существует не так много логических оптимизаций, которые могут помочь, за исключением исключения движений головок дисков (поиска), когда это возможно. Здесь может помочь существенный диск только для этого файла.

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