Почему многопоточность этого кода приводит к таким противоречивым временам? - PullRequest
2 голосов
/ 14 июня 2011

У меня есть функция, которая обрабатывает большое изображение.По спецификации самое большое это изображение может быть 55 МБ.Обработка влечет за собой разбиение изображения на несколько разных полос, а затем воссоздание изображения путем добавления этих полос обратно в выходное изображение.Поскольку изображение очень большое, я не могу сохранить все четыре изображения, а также входные и выходные изображения в памяти одновременно в 32-битной системе.В результате я помещаю каждое изображение на диск, а затем считываю его порциями.

Перед многопоточностью псевдокод выглядит следующим образом:

for (y is 0 to ysize)
   unsigned short* ptr1 = ReadLineFromDisk(image1, y)
   unsigned short* ptr2 = ReadLineFromDisk(image2, y)
   unsigned short* ptr3 = ReadLineFromDisk(image3, y)
   unsigned short* ptr4 = ReadLineFromDisk(image4, y)
   unsigned short* outPtr = &(outImage[y*inXSize])
   for (x is 0 to xsize, ++x, ++ptr1, ++ptr2, ++ptr3, ++ptr4, ++outPtr){
        outPtr = combination of ptr1, ptr2, ptr3, ptr4;
   }
}

Этот код выполняется за 3 секундыдвухъядерный компьютер со стандартным жестким диском на 500 ГБ, использующий высокопроизводительный счетчик.

Если увеличить число строк, считываемых с диска, примерно до 100, а затем выполнить это с помощью кода, который выглядит следующим образом:

chunksize = 100;
for (y is 0 to ysize by chunksize)
   unsigned short* ptr1 = ReadChunkFromDisk(image1, y)
   unsigned short* ptr2 = ReadChunkFromDisk(image2, y)
   unsigned short* ptr3 = ReadChunkFromDisk(image3, y)
   unsigned short* ptr4 = ReadChunkFromDisk(image4, y)
   unsigned short* outPtr = &(outImage[y*inXSize])
   for (x is 0 to xsize*chunk, ++x, ++ptr1, ++ptr2, ++ptr3, ++ptr4, ++outPtr){
        outPtr = combination of ptr1, ptr2, ptr3, ptr4;
   }
}

Этот код работает быстрее, чем предыдущий код, до 1,5 секунд.

Вопрос 1. Почему этот код быстрее?

Iпредположить, что это быстрее, потому что, по моему опыту, большие непрерывные чтения быстрее, чем меньшие для того же объема данных.То есть, если я читаю 100 строк данных одновременно, это быстрее, чем 100 отдельных операций чтения, по крайней мере, для обычного (не SSD) жесткого диска.Моя гипотеза близка к правильной?

Несмотря на это, процессор здесь интенсивно не используется.Увеличение размера кеша на самом деле непозволительно, в этом случае 1.5 - лучшее, что я могу получить, а затем значение немного падает (не уверен, почему это тоже будет, за исключением того, что, возможно, какое-то кэширование диска играет роль).Это подводит меня к

Вопрос 2: Почему в размере куска будет сладкое пятно?

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

Итак, я перехожу к размещению внешнего цикла в лямбда-выражении и использованию Intel TBB для потокового кода, что-то вроде:

chunksize = 100;
parallel_for (y is 0 to ysize by chunksize in a lambda expression)
   unsigned short* ptr1 = ReadChunkFromDisk(image1, y)
   unsigned short* ptr2 = ReadChunkFromDisk(image2, y)
   unsigned short* ptr3 = ReadChunkFromDisk(image3, y)
   unsigned short* ptr4 = ReadChunkFromDisk(image4, y)
   unsigned short* outPtr = &(outImage[y*inXSize])
   for (x is 0 to xsize*chunk, ++x, ++ptr1, ++ptr2, ++ptr3, ++ptr4, ++outPtr){
        outPtr = combination of ptr1, ptr2, ptr3, ptr4;
   }
}

Этот код варьируется в диапазоне скоростей от 0,4 секунд до 1,6 секунд.

Это приводит меня к:

Вопрос 3: не должно ли это увеличение скорости быть не более чем в 2 раза, а не в 4 раза?

Это двухъядерный компьютер, на котором я запускаю эти тесты, поэтомув идеальном мире один поток читает с диска, а другой обрабатывает.Даже когда он работает с четырехкратным увеличением скорости, он использует только 80% процессоров, а не 100%, поэтому узкое место на диске остается.Но увеличение в 4 раза означает, что что-то еще происходит.

Я также предполагаю, что широкий диапазон различий в скорости заключается в том, что потоки не полностью синхронизированы при чтении, если так происходит увеличение скорости.Настоящий, последний вопрос:

Вопрос 4: Как я могу получить последовательное увеличение скорости в 4 раза?

1 Ответ

4 голосов
/ 14 июня 2011

Ответ 1: Да, вы привязаны к диску, поэтому ЦП не будет слишком много привязываться, и да, чтение больших фрагментов более эффективно (если фрагменты выровнены скэш диска) .

Ответ 2: Диск, имеющий кэш-память 8 МБ и вращающийся со скоростью 10 000 об / мин, может получить пропускную способность от 60 до 80 МБ / с, поэтому«сладкое пятно» - читать фрагменты, выровненные по размеру кэша. Вы можете увеличить свой буфер, но сохранить его в соответствии с размером кэша: т.е. 8 МБ, 16 МБ, 32 МБ и т. Д.

Ответ 3: В идеале вы хотели бывыделите один поток для чтения с диска, а другой - для обработки данных (вы можете использовать несколько потоков для обработки).Многопоточность чтения с диска может немного увеличить производительность, но это, как правило, не так.Я не знаю, почему вы думаете, что «что-то еще» происходит, когда вы получаете увеличение в 4 раза.

Ответ 3 Обновление: Честно говоря, я точно не знаю, почему это происходит, но я также видел это с многопоточным дисковым вводом / выводом в приложениях .NET.На самом деле, у меня даже есть пример теста на C #, который демонстрирует такое же увеличение производительности , что вы заметили.Обратите внимание, что в моем тесте я загружаю HTML-страницы, которые примерно соответствуют тому, что вы видите в «диком» (около 80-160 КБ каждая), поэтому я не выравниваю свои чтения с дисковым кешем. Возможно, , что одновременное чтение нескольких потоков на самом деле более эффективно, поскольку вы используете преимущества дискового кэша, несмотря на то, что вы выполняете многократное чтение.Конечно, это всего лишь предположение, что у меня еще нет доказательств, чтобы подкрепить , поэтому, пожалуйста, возьмите его с крошкой соли!Я думаю, что если ваши файлы достаточно велики и ваш поток чтения с диска на самом деле имеет буфер, выровненный с дисковым кешем, то добавление большего количества потоков вообще не увеличит вашу скорость. Если вы все еще видите улучшение скорости, сообщите нам!

Ответ 4: Попробуйте следующее:

  1. Выровняйтебуфер с размером кеша вашего диска.
  2. Сократите количество приложений, которые одновременно пытаются получить доступ к диску.
  3. Загрузите в память как можно большую часть изображенияи запустите достаточное количество потоков, чтобы полностью использовать ваш ЦП (вы будете импровизировать по количеству потоков, поиграйте и посмотрите, где «сладкое пятно»).
  4. Используйте только один поток чтения с диска и убедитесь, чточто он постоянно читает !!!

И снова, вы привязаны к диску, так что вы, возможно, никогда не получите 100% загрузку ЦП.

Ответ 4 Обновление:
Я не думаю, что TBB от Intel на самом деле является причиной увеличения производительности, которую вы (и я) наблюдаете ... как я уже сказал, мой лучший думаю - это то, что несколько потоков могут быть более эффективными, если они предоставляютer использование дискового кэша.Я даже не уверен, что это правильное предположение, поэтому не цитируйте меня без тестирования!

Чтение:
Я нашел очень подробную диссертацию под названием Асинхронный / многопоточный ввод / вывод в товарных системах с несколькими дисками - исследование производительности , котороепроводит удивительный анализ и тестирование случаев, когда многопоточный ввод / вывод превосходит однопоточный ввод / вывод.Осмотрите страницу 86.

Д-р.У Доббса также есть статья на эту тему , хотя у меня не было возможности прочитать все это, я просто пролистал ее.

...