mmap() против чтения блоков

Здесь уже есть много хороших outfile ответов, охватывающих многие fstream важные моменты, поэтому я c++ просто добавлю пару проблем, которые fileinput я не видел непосредственно infile выше. То есть этот ответ input-file не следует рассматривать mmap как исчерпывающий из всех fstream плюсов и минусов, а скорее outfile как дополнение к другим ответам infile здесь.

mmap кажется волшебством

Если взять случай, когда cxx файл уже полностью кэширован c++ 1 в качестве базового 2 , mmap может fileinput показаться очень похожим outfile на магию:

  1. mmap требуется только 1 системный вызов для (потенциально) сопоставления всего файла, после чего системные вызовы больше не требуются.
  2. mmap не требует копирования данных файла из ядра в пространство пользователя.
  3. mmap позволяет вам получить доступ к файлу «как к памяти», включая обработку его любыми продвинутыми приемами, которые вы можете использовать с памятью, такими как автоматическая векторизация компилятора, встроенные функции SIMD, предварительная выборка, оптимизированные процедуры синтаксического анализа в памяти , OpenMP и т. Д.

В случае, если файл уже fileinput находится в кеше, это кажется fileinput невозможным: вы просто напрямую fstream обращаетесь к кешу страницы output-files ядра как к памяти, и он не cpp может работать быстрее, чем infile это.

Может.

mmap на самом деле не волшебство, потому что ...

mmap по-прежнему выполняет постраничную работу

Основная скрытая cxx стоимость mmap по сравнению с input-file read(2) (который на самом деле является cxx сопоставимым системным вызовом output-files на уровне ОС для чтения блоков) заключается c++ в том, что с mmap вам нужно сделать fstream "некоторые работают outfile "для каждой страницы file-io размером 4 КБ, доступной cxx в новом сопоставлении, даже input-files если она может быть скрыта output-files механизмом отказа страницы.

В file-io качестве примера типичной input-files реализации, в которой необходимо input-files устранить ошибку всего mmap всего cpp файла, поэтому 100 ГБ / 4 mmap КБ = 25 миллионов ошибок infile для чтения файла размером input-file 100 ГБ. Теперь это будет fileinput minor faults, но 25 миллионов ошибок input-files страниц по-прежнему не будут output-files очень быстрыми. Стоимость file-io незначительной неисправности file-io в лучшем случае, вероятно, составляет cpp несколько сотен нанометров.

mmap сильно зависит от производительности TLB

Теперь fstream вы можете передать MAP_POPULATE в mmap, чтобы mmap он настроил все таблицы страниц input-file перед возвратом, чтобы не input-files было ошибок страниц при доступе file-operations к нему. Теперь у этого есть mmap небольшая проблема, заключающаяся output-files в том, что он также считывает c++ весь файл в ОЗУ, что может output-file взорваться, если вы попытаетесь cpp сопоставить файл размером output-files 100 ГБ - но давайте пока outfile проигнорируем это 3 . Ядру file-operations необходимо выполнять постраничную работу, чтобы output-file настроить эти таблицы страниц output-files (отображается как время ядра). В infile конечном итоге это является cpp основной статьей затрат в output-files подходе mmap, и оно пропорционально output-files размеру файла (т. Е. Не становится input-file относительно менее важным file-operations по мере увеличения размера cxx файла) 4 .

Наконец, даже в outfile пользовательском пространстве c++ доступ к такому отображению mmap не совсем бесплатный (по mmap сравнению с большими буферами infile памяти, не происходящими cxx из файлового mmap) - даже после fstream того, как таблицы страниц mmap настроены, каждый доступ cxx к концептуально новая страница cpp будет нести ошибку TLB. Поскольку fileinput mmap загрузка файла означает fileinput использование кеша страницы cpp и его страниц размером 4 outfile КБ, вы снова несете эти затраты c++ 25 миллионов раз для файла file-io размером 100 ГБ.

Теперь фактическая file-operations стоимость этих промахов TLB output-files сильно зависит, по крайней c++ мере, от следующих аспектов output-file вашего оборудования: (а) сколько c++ у вас блоков TLB 4K и как input-files работает остальная часть output-file кэширования переводов (б) насколько file-io хорошо аппаратное обеспечение outfile предварительная выборка имеет cpp дело с TLB - например, может fstream ли предварительная выборка input-file запускать обход страницы? (c) насколько output-file быстро и насколько параллельно cpp работает аппаратное обеспечение input-files перехода по страницам. В file-operations современных высокопроизводительных file-io процессорах Intel x86 аппаратное output-files обеспечение обхода страниц output-file в целом очень сильное: имеется outfile как минимум 2 параллельных cpp обхода страниц, обход страниц cxx может происходить одновременно c++ с продолжением выполнения, а input-files аппаратная предварительная fstream выборка может запускать обход cpp страниц. Таким образом, влияние c++ TLB на нагрузку чтения потоковой передачи довольно infile невелико - и такая нагрузка outfile часто будет работать одинаково mmap независимо от размера страницы. Однако fstream другое оборудование обычно cxx намного хуже!

read() позволяет избежать этих ошибок

Системный вызов file-io read(), который обычно лежит в file-io основе вызовов типа "чтение output-files блока", предлагаемых, например, в mmap C, C++ и других языках, имеет cpp один основной недостаток, о output-files котором все хорошо знают:

  • Каждый вызов read() из N байтов должен копировать N байтов из ядра в пространство пользователя.

С c++ другой стороны, это позволяет output-files избежать большинства затрат, перечисленных c++ выше - вам не нужно отображать input-files 25 миллионов страниц размером input-files 4K в пользовательское пространство. Обычно fstream вы можете malloc один небольшой file-io буфер в пространстве пользователя output-file и повторно использовать его fstream для всех ваших вызовов read. На output-files стороне ядра почти нет проблем input-files с страницами 4K или пропусками file-io TLB, потому что вся оперативная fileinput память обычно линейно отображается output-file с использованием нескольких file-io очень больших страниц (например, страниц outfile размером 1 ГБ на x86), поэтому c++ базовые страницы в кэше страниц mmap покрываются очень эффективно file-io в пространстве ядра.

Итак, у output-files вас есть следующее сравнение, чтобы mmap определить, что быстрее при input-file однократном чтении большого cpp файла:

Является ли дополнительная постраничная работа, подразумеваемая подходом mmap, более дорогостоящей, чем побайтовая работа по копированию содержимого файла из ядра в пространство пользователя, подразумеваемая с помощью read()?

Во многих системах output-files они фактически сбалансированы. Обратите input-files внимание, что каждый из них file-io масштабируется с совершенно infile разными атрибутами оборудования input-file и стека ОС.

В частности, подход fstream mmap становится относительно cxx быстрее, если:

  • В ОС предусмотрена быстрая обработка мелких сбоев и, в частности, оптимизация для устранения мелких сбоев, например устранение неполадок.
  • ОС имеет хорошую реализацию MAP_POPULATE, которая может эффективно обрабатывать большие карты в тех случаях, когда, например, базовые страницы являются смежными в физической памяти.
  • Оборудование обладает высокой производительностью перевода страниц, например большими TLB, быстрыми TLB второго уровня, быстрым и параллельным обходом страниц, хорошим взаимодействием предварительной выборки с переводом и т. д.

... в то время mmap как подход read() становится относительно c++ быстрее, когда:

  • Системный вызов read() обладает хорошей производительностью копирования. Например, хорошая производительность copy_to_user на стороне ядра.
  • Ядро имеет эффективный (относительно пользовательского) способ отображения памяти, например, используя только несколько больших страниц с аппаратной поддержкой.
  • Ядро имеет быстрые системные вызовы и способ сохранять записи TLB ядра во время системных вызовов.

Аппаратные mmap факторы, указанные выше, сильно различаются infile для разных платформ, даже outfile в пределах одного семейства input-files (например, в поколениях x86 input-files и особенно в сегментах рынка) и c++ определенно в разных архитектурах output-files (например, ARM против x86 file-io против PPC).

Факторы ОС также outfile постоянно меняются, с различными mmap улучшениями с обеих сторон, вызывающими input-file большой скачок относительной output-files скорости для того или иного fstream подхода. Последний список fileinput включает:

  • Добавление функции устранения неисправностей, описанной выше, которая действительно помогает в случае mmap без MAP_POPULATE.
  • Добавление методов быстрого пути copy_to_user в arch/x86/lib/copy_user_64.S, например, использование REP MOVQ, когда это быстро, что действительно помогает в случае read().

Обновление после Spectre и Meltdown

Устранение уязвимостей mmap Spectre и Meltdown значительно mmap увеличило стоимость системного file-io вызова. В системах, которые fstream я измерил, стоимость системного file-operations вызова "ничего не делать" (который infile является оценкой чистых накладных input-files расходов системного вызова, не fstream считая любой фактической c++ работы, выполняемой вызовом) составляла c++ примерно 100 нс при обычном cpp использовании. современная mmap система Linux примерно до cxx 700 нс. Кроме того, в зависимости file-operations от вашей системы исправление input-files page-table isolation, специально предназначенное output-file для Meltdown, может иметь input-file дополнительные эффекты в cpp нисходящем направлении помимо fstream стоимости прямого системного cxx вызова из-за необходимости cxx перезагрузки записей TLB.

Все input-file это является относительным fileinput недостатком методов на основе outfile read() по сравнению с методами infile на основе mmap, поскольку методы input-file read() должны выполнять один системный output-files вызов для каждого значения file-io «размера буфера» данных. Вы infile не можете произвольно увеличить cpp размер буфера, чтобы амортизировать input-files эту стоимость, поскольку file-io использование больших буферов input-file обычно работает хуже, поскольку outfile вы превышаете размер L1 и, следовательно, постоянно output-file испытываете промахи кеша.

С output-files другой стороны, с помощью file-operations mmap вы можете отобразить большую file-io область памяти с помощью file-io MAP_POPULATE и получить к ней эффективный cpp доступ за счет всего одного fstream системного вызова.


1 Это более file-io или менее также включает mmap случай, когда файл не был mmap полностью кэширован для начала, но outfile когда упреждающее чтение c++ ОС достаточно хорошо, чтобы input-files он выглядел так (т. е. , страница input-files обычно кэшируется к тому cxx моменту, когда вы этого хотите). Это output-file тонкая проблема, потому что input-file способ упреждающего чтения output-files часто сильно различается input-files между вызовами mmap и read и может file-io быть дополнительно отрегулирован mmap вызовами «советовать», как mmap описано в 2 .

2 ... потому что, если input-files файл не кэширован, ваше поведение mmap будет полностью зависеть file-operations от проблем ввода-вывода, в outfile том числе от того, насколько fileinput симпатичен ваш шаблон доступа fstream к базовое оборудование - и input-file все ваши усилия должны быть mmap направлены на обеспечение input-file максимально удобного доступа, например fileinput с помощью вызовов madvise или fadviseoutput-files любых изменений уровня приложения, которые input-file вы можете внести для улучшения mmap шаблонов доступа).

3 Вы можете input-file обойти это, например, последовательно cxx mmap в окнах меньшего размера, скажем fstream 100 МБ.

4 На самом деле оказывается, что output-files подход MAP_POPULATE (по крайней мере, одна output-file комбинация оборудования и cpp ОС) лишь немного быстрее, чем c++ его неиспользование, вероятно, потому mmap что ядро ​​использует faultaround - поэтому file-operations фактическое количество мелких mmap неисправностей уменьшается cxx примерно в 16 раз.

c++

file-io

fstream

mmap

2022-10-24T07:58:46+00:00