LINUX.ORG.RU

Замедление расчетов в параллельных коллекциях

 


0

2

Добрался до параллельных коллекций в Scala. По заверениям того же Хорстманна они специально оптимизированы для работы с многопоточными программами. Решил проверить насколько возрастет скорость расчетов при переходе от последовательной коллекции к параллельной и набросал следующий простой тестовый код:

object Main extends App {
  import collection.parallel._
  
  val array = (1 to 500).toArray;
  val parallelArray = array.par;
  val seqStartTime = System.currentTimeMillis;
  array.sum;
  val seqInterval = System.currentTimeMillis - seqStartTime;
  val parallelStartTime = System.currentTimeMillis;
  parallelArray.sum;
  val parallelInterval = System.currentTimeMillis - parallelStartTime;
  
  println(s"Время последовательного сложения заняло $seqInterval миллисекунд");
  println(s"Время параллельного сложения заняло $parallelInterval миллисекунд")
}

Каково же было мое изумление, когда вывод программы показал следующее:

Время последовательного сложения заняло 13 миллисекунд
Время параллельного сложения заняло 53 миллисекунд
Как такое может быть? На векторе из тысячи элементов цифры следующие:
Время последовательного сложения заняло 15 миллисекунд
Время параллельного сложения заняло 49 миллисекунд

На ноутбуке же разница еще больше. Характеристики моего процессора таковы:

vendor_id	: AuthenticAMD
cpu family	: 21
model		: 1
model name	: AMD FX(tm)-8150 Eight-Core Processor           
stepping	: 2
microcode	: 0x600063d
cpu MHz		: 1400.000
cache size	: 2048 KB
physical id	: 0
siblings	: 8
core id		: 3
cpu cores	: 4
apicid		: 7
initial apicid	: 3
fpu		: yes
fpu_exception	: yes
cpuid level	: 13
wp		: yes
flags		: fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ht syscall nx mmxext fxsr_opt pdpe1gb rdtscp lm constant_tsc rep_good nopl nonstop_tsc extd_apicid aperfmperf pni pclmulqdq monitor ssse3 cx16 sse4_1 sse4_2 popcnt aes xsave avx lahf_lm cmp_legacy svm extapic cr8_legacy abm sse4a misalignsse 3dnowprefetch osvw ibs xop skinit wdt lwp fma4 nodeid_msr topoext perfctr_core perfctr_nb arat cpb hw_pstate npt lbrv svm_lock nrip_save tsc_scale vmcb_clean flushbyasid decodeassists pausefilter pfthreshold
bogomips	: 7248.17
TLB size	: 1536 4K pages
clflush size	: 64
cache_alignment	: 64
address sizes	: 48 bits physical, 48 bits virtual
power management: ts ttp tm 100mhzsteps hwpstate cpb

Всем спасибо.

★★★★★

Последнее исправление: cetjs2 (всего исправлений: 1)

Никто тебе сейчас всякие SIMD городить не будет. Оно просто запускает лямбды в множетсве потоков, хотя количество потоков меньше количества элементов и потоки созданы заранее.

1. Ты создал кучу задач

2. Напряг сборщик мусора

3. Собрал все результаты

4. Сам вызов лямбд - уже виртуальный вызов

5. Не факт что sum был написан через aggregate, а не через foldLeft.

В итоге чтобы посмотреть на луну, ты слетал на луну на звезде смерти.

Парралельные коллекции нужны когда вычисление на каждом элементе тяжелое

Хочешь проверить - создай 1000 элементов BigInteger и выполни .map(x=>factorial(x))

vertexua ★★★★★
()
Последнее исправление: vertexua (всего исправлений: 3)

На векторе из тысячи элементов цифры следующие

А что тысяча, а не 10-20 элементов? Профит от распараллеливания в данном случае можно получить только на очень большом сете данных. Большие - обрабатываются в один поток несколько секунд и более.

mashina ★★★★★
()

Правда ваша. Вопрос закрыт.

LongLiveUbuntu ★★★★★
() автор топика

Тест плохой — нет предварительного прогрева, из-за этого код выполняется в интерпетируемом режиме и еще и компилится в фоне. Стандартный тред-пул в параллельных коллекциях наверняка стартует «лениво» прямо в процессе вычисления.

Ну и в целом 500 элементов это слишком мало, про это все уже выше сказали.

Вот результат последовательного запуска твоего теста два раза в цикле:

Время последовательного сложения заняло 2 миллисекунд
Время параллельного сложения заняло 33 миллисекунд
Время последовательного сложения заняло 1 миллисекунд
Время параллельного сложения заняло 1 миллисекунд

А вот что получается на 500000 элементах после «прогрева»:

Время последовательного сложения заняло 17 миллисекунд
Время параллельного сложения заняло 6 миллисекунд

это на двухядерном проце macbook air 2010

maxcom ★★★★★
()
Последнее исправление: maxcom (всего исправлений: 3)
Ответ на: комментарий от maxcom

(5*10^5)/(6/10^3)~=85мегаопс. Поскольку это инты: 500к елементов - это 2метра - это л2. ((5*10^5)/(6/10^3)×4)÷2^30 - это 300метров/с. л2 это десятки гигов, а твоя кора2 - это 30гигаопс теоретически. С твоего камня реально снять гигаопс точно.

http://pastebin.com/Yp8CnyJb - gcc -O2 -march=native -mssse3 -lgomp - запусти и напиши выхлоп, поглядим чё там да как. Мне аж интересно.

anonymous
()
Ответ на: комментарий от mashina

Сумма елементов - это идеально паралелющийся случай с нулевым оверхедом - единственный оверхед который тут возникает - это создание нити + её запуск + её ожидание. И это всяко быстрее 15милесекунд. Уж создание 50к нитей/с его камень и ось осилит, а уж тысячу точно.

Поэтому в его случаее 2нити должны давать профит, ну либо минимум так же работать.

Профит от распараллеливания в данном случае можно получить только на очень большом сете данных.

Нет, на данных, где созданание нити требует меньше времени, чем вычисления одной нити.

И даже 1мс - это тот случай, ибо создаётся нить раз в 100быстрее, чем эта милисекунда.

Большие - обрабатываются в один поток несколько секунд и более.

Нет.

Если у тебя это не так - это проблема твоего языка/его реализации, а не нитей, распараллеливания, сета данных и прочего.

anonymous
()
Ответ на: комментарий от anonymous

Кстати, на ноутбуке все печально в плане параллельности. На том же количестве элементов разница сохраняется и весьма существенная, даром, что Core i7 о 8 ядрах. Зато последовательный вариант быстрее некуда.

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

LongLiveUbuntu ★★★★★
() автор топика
Ответ на: комментарий от anonymous

Да, на ноуте прогрев отчасти помогает. Но это ж зашибись просто.

LongLiveUbuntu ★★★★★
() автор топика
Ответ на: комментарий от LongLiveUbuntu

Кстати, на ноутбуке все печально в плане параллельности.

Да ноутбучный камень не особо отличается от десктомного - он обкрамсан по кешам, частоте, но в целом тоже самое.

На том же количестве элементов разница сохраняется и весьма существенная, даром, что Core i7 о 8 ядрах.

Поиграйся и попытайся догнать это: http://pastebin.com/Yp8CnyJb

Вся штука в том, что с этим языком/реализацией что-то нетак - слишком нереальные цифры даже с учётом нулевой оптимизации(хотя тут нечего оптимизировать) и нтерпритации(тут нечего интерпритировать).

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

Да.

http://pastebin.com/GaxwhaLL - вот я тут наговнякал бенч, который бенчит сколько прога может создавать/завершать тредов в секунду. Там поумолчанию на 4-х вёдрах.

Он запускает n нитей в каждой из которых он делает 100к раз инкримент, дёргая его каждый раз в новой нити. Т.е. ему надо запустить/завершить 100к тредов в 4-х по умолчанию тредах.

1нить: 203983.024764tp/s
2нити: 336276.808522tp/s
3нити: 413393.800974tp/s
Далее уже пичаль, ибо у меня 4физических ведра. Там уже начинается медленная кончина ведра.
anonymous
()

Никогда и ни при каких обстоятельствах не меряй производительность под jvm таким макаром. Люди годами тулзы для этого правильные пишут (jmh, caliper), а тут взял два таймстемпа и думаешь, что не херню померял. Погляди, если интересно оф. примеры для jmh - там многие подводные камни при бенчмаркинге освещают.

migesok
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.