Неожиданное поведение при использовании Threads. @ Threads? - PullRequest
3 голосов
/ 15 апреля 2020

В приведенном ниже коде внутренняя l oop создает вектор из всех 1, а затем устанавливает каждое значение вектора на 0. Ожидается, что сумма этого вектора будет равна 0. Но, если я использую Threads.@threads на внутренней l oop, иногда это не так. Я нарушаю правило о многопоточности с этим кодом? Кажется, что внутреннее l oop не всегда "fini sh". У меня для JULIA_NUM_THREADS установлено значение 4.

N = 1000
M = 1000
for i in 1:N
    test = trues(M)
    Threads.@threads for j in 1:M
        test[j] = 0
    end
    s = sum(test)
    if s > 0
        println("sum(M) was ", s, "!")
    end
end
println("done!")

Пример вывода:

enter image description here

Похоже, никаких проблем с использованием Threads.@threads на внешнем l oop. Как я могу предсказать, где можно использовать Threads.@threads, а где нет?

Ответы [ 2 ]

3 голосов
/ 15 апреля 2020

Это потому, что trues создает BitArray, где логические значения эффективно упаковываются как отдельные биты в 64-битных порциях. В результате доступ к смежным индексам может быть редким.

Это обсуждается в следующем выпуске и должно быть задокументировано, как только этот PR объединен.

Проблема исчезнет, ​​если вы используете другой тип массива, например Vector{Bool}:

N = 1000
M = 1000
for i in 1:N
    test = ones(Bool, M) # creates a Vector{Bool}
    Threads.@threads for j in 1:M
        test[j] = 0
    end
    s = sum(test)
    if s > 0
        println("sum(M) was ", s, "!")
    end
end
println("done!")
2 голосов
/ 15 апреля 2020

Помимо состояния гонки, вы также должны знать, что повторение и изменение элементов в BitArray намного медленнее, чем при Array{Bool}. BitArray s эффективно используют память и могут быть чрезвычайно быстрыми для чанкованных операций, но не подходят для доступа к отдельным элементам:

using BenchmarkTools, Random

function itertest!(a)
    for i in eachindex(a)
        @inbounds a[i] = !a[i]
    end
end

julia> @btime itertest!(a) setup=(a=rand(Bool, 1000));
  70.050 ns (0 allocations: 0 bytes)

julia> @btime itertest!(a) setup=(a=bitrand(1000));
  2.211 μs (0 allocations: 0 bytes)

С другой стороны, для чанкованных операций производительность звездная:

function dottest!(a)
    a .= .!a
end

julia> @btime dottest!(a) setup=(a=rand(Bool, 1000));
  71.795 ns (0 allocations: 0 bytes)

julia> @btime dottest!(a) setup=(a=bitrand(1000));
  6.904 ns (0 allocations: 0 bytes)
...