Это не должно удивлять. Когда вы запрашиваете большие чтения (особенно без указания file_flag_sequential_scan), вы, по сути, боретесь с менеджером кэша - менеджер кэша оптимизирован для относительно небольших (64 КБ, 256 КБ) операций ввода-вывода, а когда вы запускаете большие операции ввода-вывода, вещи могут получить сбивает с толку. Вот почему переход к небуферизованному вводу-выводу может быть таким огромным выигрышем - менеджер кеша уходит с дороги.

няюсь за несколько длинное вступление)

Во время разработки приложения, которое предварительно обрабатывает весь большой файл (> 400 МБ) в буферном кеше для ускорения фактического запуска позже, я проверил, имеет ли чтение 4 МБ за раз все еще какие-либо заметные преимущества по сравнению с чтением только блоков по 1 МБ за раз. Удивительно, но меньшие запросы на самом деле оказались быстрее. Это казалось нелогичным, поэтому я провел более обширный тест.

Буферный кэш был очищен перед запуском тестов (просто для смеха, я тоже сделал один прогон с файлом в буферах. Буферный кэш обеспечивает скорость до 2 ГБ / с независимо от размера запроса, хотя с удивительными +/- 30% случайная дисперсия).
Все операции чтения использовали перекрывающийся файл ReadFile с одним и тем же целевым буфером (дескриптор был открыт сFILE_FLAG_OVERLAPPED а такжебез FILE_FLAG_NO_BUFFERING). Используемый жесткий диск несколько устарел, но полностью функционален, размер кластера NTFS составляет 8 КБ. Диск был дефрагментирован после первоначального запуска (6 фрагментов против нефрагментированных, нулевая разница). Для лучшего изображения я также использовал больший файл, ниже цифры для чтения 1 ГБ.

Результаты были действительно удивительными:

4MB x 256    : 5ms per request,    completion 25.8s @ ~40 MB/s
1MB x 1024   : 11.7ms per request, completion 23.3s @ ~43 MB/s
32kB x 32768 : 12.6ms per request, completion 15.5s @ ~66 MB/s
16kB x 65536 : 12.8ms per request, completion 13.5s @ ~75 MB/s

Итак, это говорит о том, что отправкадесять тысяч запросов длиной в два кластера на самом деле лучше, чем отправлять несколько сотен больших, непрерывных чтений. Время отправки (время до возврата ReadFile) существенно возрастает по мере увеличения количества запросов, но время асинхронного завершения почти вдвое меньше.
Процессорное время ядра составляет около 5-6% в каждом случае (для четырехъядерного процессора, так что на самом деле нужно сказать 20-30%), когда асинхронные операции чтения завершаются, что является удивительным количеством ЦП - очевидно, ОС делает некоторые пренебрежительное количество занятого ожидания тоже. 30% CPU в течение 25 секунд на частоте 2,6 ГГц, это довольно много циклов для того, чтобы ничего не делать.

Есть идеи, как это можно объяснить? Может быть, у кого-то здесь есть более глубокое понимание внутренней работы Windows с перекрытием ввода-вывода? Или что-то не так с идеей, что вы можете использовать ReadFile для чтения мегабайта данных?

Я могу видеть, как планировщик ввода-вывода мог бы оптимизировать несколько запросов, сводя к минимуму количество запросов, особенно когда запросы являются произвольным доступом (а это не так!). Я также вижу, как жесткий диск сможет выполнить аналогичную оптимизацию, учитывая несколько запросов в NCQ.
Тем не менее, мы говорим о смехотворном количестве смехотворно маленьких запросов, которые, тем не менее, превосходят то, что кажется разумным, в 2 раза.

Примечание: Очевидный победитель - отображение памяти. Я почти склонен добавить «неудивительно», потому что я большой поклонник отображения памяти, но в данном случае это на самом деледействительно удивляет Мне, так как «запросы» еще меньше, а ОС должна быть еще менее способна прогнозировать и планировать IO. Сначала я не тестировал отображение памяти, потому что казалось нелогичным, что он мог бы конкурировать даже удаленно. Так много для твоей интуиции, хе.

Повторное отображение / отмена отображения вида с разными смещениями занимает практически нулевое время. Использование просмотра 16 МБ и сбой каждой страницы с помощью простого цикла for (), считывающего один байт на страницу, завершается за 9,2 с при скорости ~ 111 МБ / с. Загрузка ЦП всегда ниже 3% (одно ядро). Тот же компьютер, тот же диск, все то же самое.

Также кажется, что Windows загружает 8 страниц в буферный кэш за раз, хотя фактически создается только одна страница. Сбой каждой 8-й страницы выполняется с той же скоростью и загружает с диска одинаковое количество данных, но показывает меньшие показатели «физической памяти» и «системного кэша» и только 1/8 отказов страниц. Последующие чтения подтверждают, что страницы, тем не менее, окончательно находятся в буферном кеше (без задержки, без активности диска).

(Возможно, очень, очень отдаленно связано сОтображенный в память файл быстрее при огромном последовательном чтении?)

Чтобы сделать это немного более наглядным:

Обновить:

С помощьюFILE_FLAG_SEQUENTIAL_SCAN Кажется, что-то вроде «баланса» чтения 128k, улучшая производительность на 100%. С другой стороны, этосильно влияет на чтение 512k и 256k (вам интересно, почему?) и не оказывает реального влияния ни на что другое. График МБ / с для блоков меньшего размера, возможно, выглядит немного более «четным», но во время выполнения нет никакой разницы.

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

При учетефактический Асинхронное или «немедленное» асинхронное чтение. Заметим, что свыше 256 КБ Windows выполняет каждый асинхронный запрос асинхронно. Чем меньше размер блока, тем больше запросов обслуживается «немедленно»,даже когда они не доступны сразу (т.е. ReadFile просто работает синхронно). Я не могу разобрать четкую схему (например, «первые 100 запросов» или «более 1000 запросов»), но, похоже, существует обратная корреляция между размером запроса и синхронностью. При размере блока 8 Кб каждый асинхронный запрос обслуживается синхронно.
Буферизованные синхронные передачи по какой-то причине в два раза быстрее асинхронных (не знаю почему), следовательно, чем меньше размеры запросов, тем быстрее общая передача, потому что больше передач выполняется синхронно.

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

Обновление 2:

Небуферизованный ввод-вывод делает графики производительности для тестовых сценариев запросов 1M, 4M и 512k несколько выше и более «колючим» с максимумами в 90-х ГБ / с, но и с резкими минимумами, общее время выполнения для 1 ГБ находится в пределах +/- 0,5 s буферизованного прогона (однако запросы с меньшим размером буфера выполняются значительно быстрее, потому что с более чем 2558 запросами в полете возвращается ERROR_WORKING_SET_QUOTA). Измеренное использование ЦП равно нулю во всех небуферизованных случаях, что неудивительно, поскольку любой ввод-вывод выполняется через DMA.

Еще одно очень интересное наблюдение сFILE_FLAG_NO_BUFFERING является то, что это значительно меняет поведение API.CancelIO больше не работает, по крайней мере, в некотором смыслеотмена ввода-вывода, С небуферизованными запросами в полете,CancelIO будет просто блокировать, пока все запросы не будут завершены. Адвокат, вероятно, будет утверждать, что функция не может быть привлечена к ответственности за пренебрежение своим долгом, потому что больше нет запросов в полете, когда она возвращается, так что каким-то образом она выполнила то, о чем просили - но мое понимание «отменить» несколько отличается.
С участиембуферномперекрывается IO,CancelIO просто перережет веревку, все операции в полете прекратятся немедленно, как и следовало ожидать.

Еще одна забавная вещь заключается в том, что процессубиваемым пока все запросы не будут выполнены или не выполнены. Этот вид имеет смысл, если ОС выполняет DMA в это адресное пространство, но, тем не менее, это потрясающая «функция».

Ответы на вопрос(1)

Ваш ответ на вопрос