gpt4 book ai didi

Julia Threads.@threads 在一个简单的例子中不起作用

转载 作者:行者123 更新时间:2023-12-04 17:29:34 30 4
gpt4 key购买 nike

我在 Julia 中运行双循环。代码非常简单。

w = rand(1000,1000)
function regular_demo(w::Array{Float64, 2})
n = size(w)[1]
G = zeros(n,n)
@inbounds for j in 1:n-1
wjj = w[j, j]
for i in j+1:n
wii = w[i, i]
wij = w[i, j]
sum = wii + 2wij + 3wjj
sum_inverse = inv(sum)
G[j,j] = G[j,j] + sum_inverse
G[i,i] = G[i,i] + sum_inverse
G[i,j] = G[i,j] - sum_inverse
G[j,i] = G[j,i] - sum_inverse
end
end
G
end
regular_demo(w)
function thread_demo(w::Array{Float64, 2})
n = size(w)[1]
G = zeros(n,n)
@inbounds Threads.@threads for j in 1:n-1
wjj = w[j, j]
for i in j+1:n
wii = w[i, i]
wij = w[i, j]
sum = wii + 2wij + 3wjj
sum_inverse = inv(sum)
G[j,j] = G[j,j] + sum_inverse
G[i,i] = G[i,i] + sum_inverse
G[i,j] = G[i,j] - sum_inverse
G[j,i] = G[j,i] - sum_inverse
end
end
G
end
thread_demo(w)

regular_demo(w) 和 thread_demo(w) 之间的唯一区别是函数第 4 行中的 @inbounds Threads.@threads。如果我对同一个矩阵 w 同时运行regular_demo(w) 和thread_demo(w)。输出矩阵 G 不同。如何在thread_demo(w) 中实现线程以获得与regular_demo(w) 相同的输出矩阵G。

最佳答案

正确的功能应该如下所示

function thread_demo(w::Array{Float64, 2})
n = size(w)[1]
G = [Threads.Atomic{Float64}(0.0) for i in 1:n, j in 1:n]
a = n % 2 == 1 ? 1 : 0
@inbounds Threads.@threads for j2 in 1:n-1
j = j2 % 2 == 1 ? j2 : n - j2 + a
wjj = w[j, j]
for i in j+1:n
sum_inverse = inv(w[i, i] + 2w[i, j] + 3wjj )
for i in 1:1000
sum_inverse = inv(sum_inverse)
end #used for performance testing
Threads.atomic_add!(G[j,j], sum_inverse)
Threads.atomic_add!(G[j,i], -sum_inverse)
Threads.atomic_add!(G[i,i], sum_inverse)
Threads.atomic_add!(G[i,j], -sum_inverse)
end
end
G
end

您的代码中需要更正以下两件事:
  • Threads.@threads将元素切成相等的部分,但是内部循环的大小会随着 j 的增大而减小值。这需要重新计算 j线程间的工作分配或多或少相等
  • 您的 G j 的各种值的标识符重叠.这意味着您需要在线程之间同步以对相同数据进行操作。您可以为此使用锁(例如 Threads.SpinLock )或原子值。在这里我使用原子值,因为它们在这个例子中更自然。

  • 您可以通过运行以下测试来检查我的代码是否有效:
    gg1 = regular_demo(w);
    gg2 = thread_demo(w);
    @assert all(gg1 .≈ getproperty.(gg2, :value))

    您需要使用近似相等运算符,因为并行性可以改变算术运算的顺序,并且您得到的结果略有不同。

    现在的问题是原子很昂贵。我的代码执行时间比你的 regular_demo 长得多函数,因为需要线程之间的协调,并且循环内的操作非常便宜。但是,很可能您的场景比这个 MWE 复杂得多,并且使用我的代码,您将观察到显着的性能提升(为了测试并行性,您可以尝试在内部循环中添加类似 for i in 1:1000; sum_inverse = inv(sum_inverse); end 的内容并查看自己)。

    请注意,为了让多线程在 Julia 中工作,您需要设置 JULIA_NUM_THREADS具有所需线程数(不大于逻辑 CPU 内核数)的变量。

    视窗:
    set JULIA_NUM_THREADS=4

    Linux:
    export JULIA_NUM_THREADS=4

    请注意,某些 IDE 设置(例如 Juno)默认将线程数设置为等于 CPU 内核数。

    最后但并非最不重要的一点是,您可以通过运行 Threads.nthreads() 来测试您的配置。 :
    julia> Threads.nthreads()
    4

    关于Julia Threads.@threads 在一个简单的例子中不起作用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59743724/

    30 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com