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

StackOverflow https://stackoverflow.com/questions/9191

  •  08-06-2019
  •  | 
  •  

Вопрос

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

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

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

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

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

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

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

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

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

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

Это было полезно?

Решение

Похоже, проблема кроется в политике планирования ввода-вывода Windows.Согласно тому, что я нашел здесь есть много способов получить операционную.чтобы запланировать запросы к диску.В то время как Linux и другие устройства могут выбирать между различными политиками, до Vista Windows была заблокирована в рамках одной политики:очередь FIFO, где все запросы разделены на блоки по 64 КБ.Я считаю, что эта политика является причиной проблемы, с которой вы столкнулись:планировщик будет смешивать запросы от двух потоков, вызывая непрерывный поиск между различными областями диска.
Итак, хорошей новостью является то, что, согласно здесь и здесь, Vista представила более интеллектуальный планировщик дисков, в котором вы можете устанавливать приоритет ваших запросов, а также выделять минимальную плохую ширину для вашего процесса.
Плохая новость заключается в том, что я не нашел способа изменить политику диска или размер буферов в предыдущих версиях Windows.Кроме того, даже если повышение приоритета дискового ввода-вывода вашего процесса повысит производительность по сравнению с другими процессами, у вас все равно останутся проблемы, связанные с конкуренцией ваших потоков друг с другом.
Что я могу предложить, так это модифицировать ваше программное обеспечение, введя самодельную политику доступа к диску.
Например, вы могли бы использовать подобную политику в своем потоке B (аналогично для потока A).:

if THREAD A is reading from disk then wait for THREAD A to stop reading or wait for X ms
Read for X ms (or Y MB)
Stop reading and check status of thread A again  

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

Другие советы

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

Наше решение для Windows заключается в передаче FILE_FLAG_NO_BUFFERING флаг для CreateFile и использовать большие (~ 16 мбАйТ) чтения при каждом вызове 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.
  • Мой дом Кулу Компьютер под управлением Linux.Значительно слабее по сравнению с другими системами, но я продемонстрировал, что даже эта машина может превзойти Windows server с RAID5 при выполнении многопоточных операций чтения с диска.

Загрузка процессора во всех этих системах во время тестов была очень низкой, а антивирус был отключен.

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

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

Это всего лишь "внутреннее чутье", но это заставляет меня сомневаться в том, что это действительно простая проблема поиска.Кроме OS X и Raid5, все это было опробовано на одной машине - вы пробовали на другой машине?Является ли загрузка вашего процессора практически нулевой во время этого теста?

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

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

Используете ли вы Отчеты о выполнении IOCompletionPorts под Окнами?В Windows через C ++ есть подробная глава на эту тему, и, как назло, так оно и есть, он также доступен на MSDN.

Пол - видел обновление.Очень интересно.

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top