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

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

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


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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Если mmap() не по душе, тогда есть fadvise() , а для действительно жестких – asynchronous ввод-вывод.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  • Почему мой SSH-вход медленный?
  • Google Chrome работает медленнее до localhost
  • Будет ли M.2 NVMe или перенесена графика медленных дискретных графических карт
  • Java lambdas в 20 раз медленнее анонимных classов
  • Как определить идеальный размер буфера при использовании FileInputStream?
  • Какой метод работает лучше: .Any () vs .Count ()> 0?
  • SandForce контроллер SSD Сбор мусора для систем без поддержки TRIM
  • Эффективное цветовое отображение изображения на Java
  • StringBuilder vs Конкатенация строк в toString () в Java
  • Как я могу конвертировать секунды с эпохи в часы / минуты / секунды в Java?
  • Почему LINQ JOIN намного быстрее, чем связывание с WHERE?
  • Давайте будем гением компьютера.