это упрощенная версия моего кода:
function simula(v)
a = zeros(length(v))
for i in 1:1000000
a -= v
a[1] = 3.
a = a.^2
end
end
v = ones(100)
_ = simula(v) #compile
@time Threads.@threads for i in 1:10
simula(v)
end
Если я запускаю этот код с 1, 2 или 4 потоками, ничего не меняется (с точки зрения времени). Ты знаешь почему? Моя интуиция подсказывает, что операции с векторами уже используют многопоточность. Есть ли способ распараллелить это? Моя цель — запустить симуляцию функции на кластере со 100 ядрами. Заранее спасибо.
По умолчанию используется только 1 поток. Вы можете увидеть это, используя диспетчер задач вашей операционной системы. Сначала вам нужно установить переменную среды JULIA_NUM_THREADS
(установите ее на количество ядер). В Windows вы можете сделать это с помощью пакетного интерпретатора по умолчанию, используя set JULIA_NUM_THREADS=4
(для 4 потоков). При этом программа плохо масштабируется.
Основная причина — не масштабирование из-за сборщика мусора (GC), так как он находится под давлением. Действительно, последовательная программа приводит к 22% времени GC, в то время как параллельная версия, использующая 4 потока, приводит к 45% времени GC (с 6-ядерной машиной). Это много, и это указывает на слишком много выделений. Вы можете переписать свой код, используя простые простые циклы, и тогда он будет работать быстрее в последовательном порядке, а также намного лучше масштабироваться:
function simula(v)
a = zeros(length(v))
for i in 1:1000000
for j in 1:length(a)
a[j] -= v[j]
end
a[1] = 3.
for j in 1:length(a)
a[j] *= a[j]
end
end
end
Более простая реализация заключается в использовании операторов на основе .
, как указано @OscarSmith:
function simula(v)
a = zeros(length(v))
for i in 1:1000000
a .-= v
a[1] = 3.
a .= a.^2
end
end
Последовательная версия примерно в 15 раз быстрее, чем раньше, а параллельная версия, использующая 4 потока, примерно в 40 раз быстрее, чем раньше. С 6 потоками это примерно в 50 раз быстрее, чем раньше. Параллельная версия в 3,7 раза быстрее последовательной. Это не так уж плохо, учитывая небольшое количество итераций.
Обратите внимание, что программа ничего не делает, и компиляторы часто могут оптимизировать коды, которые ничего не делают (т.е. удалять такие части из программы). Вы всегда должны тестировать код с видимыми эффектами, чтобы избежать предвзятости в тесте (и вводящих в заблуждение выводов).
На самом деле. Спасибо. Я отредактировал вопрос, чтобы добавить эту информацию :).
@JérômeRichard спасибо за ваш блестящий комментарий. У меня есть еще один вопрос: pop = zeros(100)
p = rand(100) p /= sum(p) time pop .= rand(Multinomial(100, p)) time pop = rand(Multinomial(100, p)) Почему первый 4 распределения и второй 2 ? Есть ли способ сделать это без каких-либо выделений?
обратите внимание, что здесь вы можете написать эффективный векторизованный код. Это может быть просто
a .-= v
иa .= a.^2