Небольшой анализ, насколько хорошо ускоряется решение множества СЛАУ в параллельном режиме. Сначала график.
Тем не менее, AMD здесь постаралась на славу, если вспомнить, что на 4 ядра в этой архитектуре имеется всего два полноценных АЛУ с плавающей точкой. Несмотря на это, в параллельном режиме обеспечивается трехкратное ускорение!
Но еще более впечатляющих результатов AMD добилась в AMD Ryzen 5 3600. Несмотря на 6 физических ядер, на небольших размерностях ускорение превышает 6 раз. Старшие мобильные Intel вроде тоже так умеют, но на более меньших размерностях, которые в этот раз в тест не попали.
Есть и общая закономерность, которую демонстрируют все процессоры: замедление ускорения от параллельного решения СЛАУ при росте размерности задачи. Особенно это заметно у десктопного Ryzen 5 3600, а также мобильных Intel i5-8300H и Intel i7-6700HQ. Причем последний начинает сдуваться даже раньше, на 384 переменных.
В итоге ускорение параллельной обработки у мобильных процессоров Intel становится меньше единицы, т. е. один поток выполнит работу быстрее, чем восемь!
Десктопные процессоры не демонстрируют такую особенность, видимо, это произойдет на одной из следующих размерностях, которые я не тестировал.
Объясняется эта закономерность несоответствием скорости процессора и пропускной способностью памяти. При решении СЛАУ каждая операция слишком проста, и когда строки памяти становятся слишком длинные, то каждое ядро слишком часто обращается к памяти, увеличивается промахи мимо кэша, а пропускной способности шины памяти становится недостаточно, что бы успеть обслужить запросы всех ядер.
Видимо, при решение подобных задач, обрабатывающих простым алгоритмом огромный объем памяти, использование технологии HyperThreading выглядит не очень оправданным. Т. е. ее видимо, либо необходимо отключить, либо просто использовать в два раза меньше вычислительных потоков, чем ядер.
Впрочем, это не гарантирует, что на очень больших размерностях этого будет достаточно. Все зависит от скорости шины памяти. Если она достаточна медленна, а обработка одного элемента слишком быстра, то количество потоков придется еще больше ограничить.
Кстати, самые слабенькие процессоры из теста, Intel i3-3227U и AMD A10-4600M демонстрируют крайне слабое падение ускорения в зависимости от размерности задачи. Они просто достаточно медленные, и ядер не так много, поэтому подсистема памяти успевает обслуживать их запросы.

Согласен, все выглядит так, что потоки сражаются за место в кэше, вместо реальной работы. Может [Размер кэша]/[Размер Одно Системы] использовать чтобы запускать оптимальное число параллельных вычислений? Система из 1536 уравнений занимает ~18 МБ (если 64-битный тип), кэш у Ryzen 5 вроде 32 МБ, что может хватить для 2 потоков, но явно не больше.
ОтветитьУдалитьНо вообще, как вы уже раньше писали, для больших систем распараллеливание внутри решения одной системы скорее всего сможет загрузить все ядра, так как будет гораздо больше попаданий в кэш.
Работа с кэшем она вообще интересна: с одной он очень важен и без него все медленно, а с другой, на сколько я знаю, никак не лимитирован между потоками, любой поток/процесс может замедлить что угодно просто прочитав кучу данных из памяти.
В этом плане архитектура Cuda куда более продумана для вычислений, там работа с shared memory более явна (по сути та же быстрая SDRAM): программист решает что в нее положить, определяет размер, и потоки владеют ею до окончания вычислений.
> Может [Размер кэша]/[Размер Одно Системы] использовать чтобы запускать оптимальное число параллельных вычислений?
УдалитьОтличная идея! Я при анализе как-то протупил и упустил из виду количество потоков, пытался привязаться просто к размеру одной СЛАУ и кэша 1 и 2 уровней, не очень удачно.
А вот если учесть количество потоков, как ты предлагаешь, то все сразу встает на свои места. Как только [размер СЛАУ]*[кол-во потоков] выходит за размер кэша третьего уровня, так сразу производительность проседает.
Это, кстати, объясняет, почему Intel i7-6700HQ теряет производительность уже на 384 переменных: 8 (по кол-ву потоков) СЛАУ такой размерности занимают 9 МиБ, а кэш 3 уровня у этого процессора - всего 6 МиБ.
> Работа с кэшем она вообще интересна: с одной он очень важен и без него все медленно, а с другой, на сколько я знаю,
Удалить> никак не лимитирован между потоками, любой поток/процесс может замедлить что угодно просто прочитав кучу данных из памяти.
Меня в связи с этим несколько удивляют процессоры с 64 или со 128 ядрами. Понятно, что там и память имеет большее количество каналов и следовательно, более высокую пропускную способность, но, тем не менее, обеспечить эффективную работу процессора в этих условиях на однородной памяти достаточно сложно. Что ставит вопрос об эффективности и целесообразности подобных систем.
Но все же справедливости ради один поток не очень-то сильно сможет замедлить остальные на других ядрах, если только он один интенсивно обращается к памяти. Ведь есть еще кэш L1/L2, которые привязаны к конкретному ядру. Проблема начинается, когда таких потоков становится достаточно много.
> обеспечить эффективную работу процессора в этих условиях на однородной памяти достаточно сложно.
УдалитьТам внутри до 8 по сути не зависимых процессоров, и как я понял, можно настроить как их отображать: UNA (однородная память, для приложений - один процессор с кучей ядер) или NUMA (ОС и приложения видят что по сути процессоры не зависимые, и память не однородна, сложнее использовать, но зато более эффективно, если приложение умеет работать с NUMA)
Как я понимаю основное предназначение этого монстра - это что-то типа домашнего видео монтажа для youtube-ров: задача распараллеливается отлично, но иметь дома кластер из кучи машин - это сложно в настройке, плюс скорее всего по электроэнергии менее оптимально, что в некоторых странах важная составляющая. Ну или просто старый монолитный софт запускать, который не умеет в кластере работать, а переписывать его никто не собирается.
> поток не очень-то сильно сможет замедлить остальные на других ядрах
Это пока ОС не решит его убрать с ядра чтобы выполнить что-нибудь другое, а вот дальше непонятно что с ним делать: ждать пока тоже ядро освободиться, ведь там уже L1 и L2 для него прогреты, вроде оптимально, но с другой стороны, а вдруг тот, кто сейчас занял ядро, уже весь кэш обновил, тогда зря ждали... Чтобы "помочь" ОС принимать решение можно привязать процессы или трэды к конкретным ядрам (Processor affinity), но по дефолту на сколько я знаю разброд и шатания.