Как получить хорошую производительность одновременного чтения с диска - PullRequest
26 голосов
/ 12 августа 2008

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

У нас есть два больших файла, которые мы хотели бы читать из двух отдельных потоков одновременно. Один поток будет последовательно читать файл 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 и без него.

Ответы [ 6 ]

11 голосов
/ 13 августа 2008

Кажется, проблема в политике планирования ввода-вывода 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 также можно автоматически настраивать, проверяя фактические скорости передачи и медленно изменяя их, тем самым максимизируя пропускную способность при работе приложения на разных машинах и / или O.S. Вы можете обнаружить, что уровни кэша, памяти или RAID так или иначе влияют на них, но с автоматической настройкой вы всегда получите наилучшую производительность в каждом сценарии.

5 голосов
/ 12 августа 2008

Я хотел бы добавить еще несколько примечаний в мой ответ. Все другие операционные системы сторонних производителей, которые мы тестировали, не страдают от этой проблемы. 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 ПК под управлением Linux. Значительно слабее по сравнению с другими системами, но я продемонстрировал, что даже эта машина может превзойти сервер Windows с RAID5 при выполнении многопоточных операций чтения с диска.

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

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

1 голос
/ 13 августа 2008

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

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

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

0 голосов
/ 13 августа 2008

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

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

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

0 голосов
/ 13 августа 2008

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

0 голосов
/ 13 августа 2008

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...