📎 Скрытая ловушка mmap: почему async/await не работает как ожидается
Memory-mapped файлы выглядят как идеальное решение: доступ к файлу как к обычному массиву без лишних копирований. Но есть подвох — они могут убить всю конкурентность в async-коде.
Проблема в цифрах
Эксперимент показал шокирующие результаты при чтении 8 файлов по 256 МБ каждый:
- async/await + mmap: 2.5-3 секунды
- 8 потоков + mmap: 0.75-0.8 секунды
- async/await + обычный I/O: 0.62-0.65 секунды
Асинхронная версия с mmap работает как однопоточная — конкурентность полностью исчезает!
Что происходит под капотом?
Memory mapping создает иллюзию, что файл находится в памяти:
let mmapped = unsafe { memmap2::Mmap::map(file)? };
let byte = mmapped[0]; // Выглядит как обычная индексацияНо на самом деле:
1. При первом обращении к странице происходит page fault
2. ОС блокирует поток и загружает данные с диска
3. В это время НЕТ await — исполнитель не может переключиться на другую задачу
4. Весь async runtime замораживается
Коварство скрытой блокировки
Самое опасное —
Mmap разыменовывается в &[u8]. Функция получает обычный срез:fn process_data(data: &[u8]) {
let value = data[1000]; // Может заблокировать весь runtime!
}Код не подозревает, что индексация может вызвать I/O блокировку.
Когда mmap действительно быстр
С прогретым кэшем результаты кардинально меняются:
- Холодный кэш: 2.5 секунды
- Прогретый кэш: 0.12 секунды (в 21 раз быстрее!)
Memory mapping отлично работает, когда данные уже в памяти — меньше накладных расходов, чем у обычного I/O.
Практические выводы
✅ Используйте mmap когда:
- Данные помещаются в память
- Работаете с синхронным кодом
- Нужен произвольный доступ к файлу
❌ Избегайте mmap в async/await когда:
- Файлы больше доступной памяти
- Критична отзывчивость системы
- Используете кооперативную многозадачность
Альтернативы для async
Для асинхронного кода лучше использовать:
-
tokio::fs::File с явными read().await- Разбиение больших файлов на чанки
- Предварительная загрузка критичных данных
Memory mapping — мощный инструмент, но в async-мире он может превратить конкурентный код в последовательный. Знание этой особенности поможет избежать неожиданных проблем с производительностью.
📎 Ссылка
🎙 Новости
📝 База вопросов
