Как получить хорошую производительность одновременного чтения с диска
=====================================================================

Я хотел бы задать вопрос, а затем ответить на него своим собственным ответом, но также посмотреть, какие ответы имеют другие люди.

У нас есть два больших файла, которые мы хотели бы читать из двух отдельных потоков одновременно. Один поток будет последовательно читать файл A, а другой поток будет последовательно читать файл B. Между потоками нет блокировки или связи, оба последовательно читают так быстро, как могут, и оба сразу отбрасывают прочитанные данные.

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

Если мы отключим один из потоков и временно посмотрим на производительность одного потока, мы получим гораздо лучшую пропускную способность (~ 45 МБ / с для этой машины). Очевидно, что плохая двухпоточная производительность является артефактом планировщика дисков ОС.

**Есть ли что-нибудь, что мы можем сделать, чтобы улучшить производительность чтения параллельных потоков?** Возможно, с помощью различных API или путем настройки параметров планировщика диска ОС.

Некоторые детали:

Файлы имеют порядок 2 ГБ каждый на машине с 2 ГБ ОЗУ. Для целей этого вопроса мы считаем, что они не кэшированы и не полностью дефрагментированы. Мы использовали инструменты дефрагментации и перезагрузили, чтобы убедиться, что это так.

Мы не используем специальные API для чтения этих файлов. Поведение повторяется для различных стандартных API-интерфейсов, таких как CreateFile в Win32, fopen в C, std :: ifstream в C ++, FileInputStream в Java и т. Д.

Каждый поток вращается в цикле, вызывая функцию чтения. Мы варьировали число байтов, запрашиваемых у API на каждой итерации, от значений от 1 КБ до 128 МБ. Изменение этого не имело никакого эффекта, поэтому ясно, что количество, которое физически читает ОС после каждого поиска диска, не определяется этим числом. Это именно то, что следует ожидать.

Разительная производительность между однопоточным и двухпотоковым процессором повторяется в Windows 2000, Windows XP (32-разрядной и 64-разрядной), Windows Server 2003, а также с аппаратным RAID5 и без него.

 кажется, в политике планирования ввода-вывода Windows. Согласно тому, что я нашел[Вот](http://engr.smu.edu/~kocan/7343/fall05/slides/chapter11.ppt) Есть много способов для О.С. планировать запросы диска. В то время как Linux и другие могут выбирать между различными политиками, до Vista Windows была заблокирована в одной политике: очередь FIFO, где все запросы были разделены на блоки по 64 КБ. Я полагаю, что эта политика является причиной проблемы, с которой вы столкнулись: планировщик будет смешивать запросы из двух потоков, вызывая непрерывный поиск между различными областями диска.  
Теперь, хорошая новость заключается в том, что в соответствии с[Вот](https://technet.microsoft.com/en-us/magazine/cc162494.aspx) а также[Вот](http://widefox.pbwiki.com/IO)В Vista появился более интеллектуальный планировщик дисков, в котором вы можете установить приоритет ваших запросов, а также выделить минимальную пропускную способность для вашего процесса.  
Плохая новость заключается в том, что я не нашел способа изменить политику дисков или размер буферов в предыдущих версиях Windows. Кроме того, даже если повышение приоритета дискового ввода-вывода вашего процесса повысит производительность по сравнению с другими процессами, у вас все равно будут проблемы, когда ваши потоки конкурируют друг с другом.  
Что я могу предложить, так это изменить свое программное обеспечение, введя собственную политику доступа к диску.  
Например, вы можете использовать такую политику в вашей ветке B (аналогично для ветки A):

Вы можете использовать семафоры для проверки состояния или счетчики perfmon, чтобы получить состояние текущей дисковой очереди. Значения X и / или Y также можно автоматически настраивать, проверяя фактические скорости передачи и медленно изменяя их, тем самым максимизируя пропускную способность при работе приложения на разных машинах и / или O.S. Вы можете обнаружить, что уровни кэша, памяти или RAID так или иначе влияют на них, но с автоматической настройкой вы всегда получите наилучшую производительность в каждом сценарии.



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



е другие операционные системы сторонних производителей, которые мы тестировали, не страдают от этой проблемы. Linux, FreeBSD и Mac OS X (последняя версия на другом оборудовании) значительно ухудшают общую пропускную способность при переходе от одного потока к двум. Linux, например, снизился с ~ 45 МБ / с до ~ 42 МБ / с. Эти другие операционные системы должны считывать большие фрагменты файла между каждым поиском, и поэтому не тратят почти все свое время ожидания на диске для поиска.

Наше решение для Windows - передать`FILE_FLAG_NO_BUFFERING` флаг для`CreateFile` и использовать большие (~ 16MiB) чтения в каждом вызове`ReadFile`, Это неоптимально по нескольким причинам:

Файлы не кэшируются при таком чтении, поэтому нет никаких преимуществ, которые обычно дает кэширование.Ограничения при работе с этим флагом намного сложнее, чем при обычном чтении (выравнивание буферов чтения по границам страниц и т. Д.).(Как последнее замечание. Объясняет ли это, почему подкачка под Windows является настолько адской? То есть, Windows неспособна выполнять ввод-вывод для нескольких файлов одновременно с какой-либо эффективностью, поэтому при подкачке все другие операции ввода-вывода вынуждены быть непропорционально медленными.)

Изменить, чтобы добавить некоторые дополнительные детали для Уилла Дина:

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

Несколько рабочих станций Dell (Intel Xeon) разных возрастов под управлением Windows 2000, Windows XP (32-разрядная версия) и Windows XP (64-разрядная версия) с одним диском.Сервер Dell 1U (Intel Xeon) под управлением Windows Server 2003 (64-разрядная версия) с RAID 1 + 0.Рабочая станция HP (AMD Opteron) с Windows XP (64-разрядная версия), Windows Server 2003 и аппаратный RAID 5.Мой домашний марочный ПК (AMD Athlon64) под управлением Windows XP (32-разрядная версия), FreeBSD (64-разрядная версия) и Linux (64-разрядная версия) с одним диском.Мой домашний MacBook (Intel Core1) под управлением Mac OS X, один диск SATA.Мой дом[Koolu](http://koolu.com/Koolu-WE-Appliance/Works-Everywhere-Appliance.html) ПК под управлением Linux. По сравнению с другими системами он значительно слабее, но я продемонстрировал, что даже эта машина может превзойти сервер Windows с RAID5 при многопоточном чтении с диска.Загрузка ЦП во всех этих системах была очень низкой во время тестов, и антивирус был отключен.

Я забыл упомянуть, но мы также попробовали нормальный Win32`CreateFile` API с`FILE_FLAG_SEQUENTIAL_SCAN` флаг установлен. Этот флаг не решил проблему.



 что вы не видите никакой разницы между широким диапазоном версий Windows и ничем между одним приводом и аппаратным raid-5.

Это всего лишь «интуитивное чувство», но это заставляет меня сомневаться, что это действительно простая проблема с поиском. Помимо OS x и Raid5, все это пробовали на одной и той же машине - вы попробовали другую машину? - Разве это не то же и другое. А вы не пытался это сделать? » Ваш процессор в основном равен нулю во время этого теста?

Какое самое короткое приложение, которое вы можете написать, демонстрирует эту проблему? - Мне было бы интересно попробовать это здесь.



[IOCompletionPorts](https://msdn.microsoft.com/en-us/library/aa363862(VS.85).aspx) под виндой? В Windows через C ++ есть глубокая глава на эту тему, и, если повезет,[это также доступно на MSDN](https://msdn.microsoft.com/en-us/library/cc500399.aspx).



Было бы интересно попробовать это на Vista или Win2008, так как люди, кажется, сообщают о некоторых значительных улучшениях ввода / вывода в некоторых случаях.

Мое единственное предложение относительно другого API - попробовать отобразить файлы в памяти - вы пробовали это? К сожалению, при 2 ГБ на файл вы не сможете отобразить несколько целых файлов на 32-разрядной машине, а это означает, что это не так тривиально, как могло бы быть.