Подтвердить что ты не робот

Каков самый быстрый способ высокопроизводительного последовательного ввода-вывода файлов в С++?

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

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


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

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

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

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

4b9b3361

Ответ 1

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

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

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

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

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

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

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

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

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

  • Асинхронный ввод-вывод всегда предпочтительнее синхронного? Что делать, если приложение не связано с ЦП?

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

Ответ 2

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

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

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

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

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

Ответ 3

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

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

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

Ответ 4

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

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

Ответ 5

Вы спросили о С++, но это звучит так, будто вы прошли мимо, и готовы получить небольшую платформу.

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

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

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

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

Ответ 6

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

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

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

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

Ответ 7

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

mmap ing обеспечивает быстрый доступ к файлам, но сам вызов mmap довольно дорог. Для небольших файлов (16KiB) read и write системные вызовы выигрывают (см. fooobar.com/info/123087/... для чисел при чтении через read и mmap).