Одно из самых медленных действий, которое можно совершить в приложении, — это системный вызов. Они медленные, потому что требуют перехода в ядро, а это дорогостоящая операция. Что делать, если нужно выполнять много операций ввода-вывода с диском, но при этом важна производительность? Один из вариантов — использовать memory maps.
Memory maps — это современный механизм Unix, позволяющий «включить» файл в виртуальную память. В контексте Unix «современный» означает появившийся где-то в 1980-х или позже. У вас есть файл с данными, вы вызываете
mmap, и получаете указатель на участок памяти, где эти данные находятся. Теперь, вместо того чтобы делать seek и read, вы просто читаете по этому указателю, регулируя смещение, чтобы попасть в нужное место.Производительность
Ниже сравнение производительности при чтении файла через memory map и через
ReaderAt. ReaderAt использует системный вызов pread(), совмещающий seek и read, а mmap просто читает напрямую из памяти.
Случайное чтение (ReaderAt): 416.4 ns/op
Случайное чтение (mmap): 3.3 ns/op
---
Итерация (ReaderAt): 333.3 ns/op
Итерация (mmap): 1.3 ns/op
Почти как магия. Возможно кто-то из вас помнит Varnish Cache – ускоритель HTTP, разработанный для динамических веб-сайтов с большим количеством контента. В 2006 году именно эта возможность делала его настолько быстрым при выдаче контента. Varnish Cache использовал memory maps, чтобы доставлять данные с невероятной скоростью.
Кроме того, поскольку можно работать с указателями на память, выделенную memory map, снижается нагрузка на память и уменьшается общая задержка.
Обратная сторона memory maps
Недостаток memory maps в том, что по сути в них нельзя писать. Причина кроется в устройстве виртуальной памяти.
Когда вы пытаетесь записать в область виртуальной памяти, которая не отображена на физическую, процессор вызывает page fault. Современный CPU отслеживает, какие страницы виртуальной памяти соответствуют каким физическим страницам. Если вы пишете в страницу, которая не сопоставлена, процессору нужна помощь.
При page fault операционная система:
1. Выделяет новую страницу памяти.
2. Считывает содержимое файла по нужному смещению.
3. Записывает это содержимое в новую страницу.
4. Затем управление возвращается приложению, и оно перезаписывает эту страницу новыми данными.
Можно просто поаплодировать тому, насколько это неэффективно. Думаю, можно с уверенностью сказать: писать через memory map — плохая идея, если важна производительность, особенно если есть риск, что файл ещё не загружен в физическую память.
Вот несколько бенчмарков:
Запись через mmap, страницы не в памяти: 1870 ns/op
Запись через mmap, страницы в памяти: 79 ns/op
WriterAt: 303 ns/op
Как видно, наличие страниц в кэше критически влияет на производительность.
WriterAt, использующий системный вызов pwrite, даёт куда более предсказуемые результаты.Тем не менее, в начале Varnish Cache действительно писал через memory map. Как-то это срабатывало — в основном потому, что конкуренты тогда были гораздо хуже.
Источник
@go_for_devs