multithreading - 使用 Julia 1.3.1 的多线程速度较慢

标签 multithreading julia

我最近开始使用 1.3.1 版本中的新多线程接口(interface)。在我尝试斐波那契示例之后 in this blog post为了获得显着的加速,我开始尝试我的一些旧算法。

我有一个使用梯形方法来计算曲线下方或上方积分的函数:

function trapezoid( x    :: AbstractVector       ,
                    y    :: AbstractVector       ;
                    y0   :: Number         = 0.0 ,
                    inv  :: Number         = NaN )
   int = zeros(length(x)-1)
   for i = 2:length(x)
      if isnan(inv) == true
         int[i-1] = (y[i]+y[i-1]-2y0) * (x[i]-x[i-1]) / 2
      else
         int[i-1] = (2inv-(y[i]+y[i-1])-2y0) * (x[i]-x[i-1]) / 2
      end # if
   end # for
   integral = sum(int) ;
   return integral
end

然后我有一个非常低效的算法,通过比较曲线下方和上方的面积来确定曲线的中点索引:

function EAM_without_threads( x     :: Vector{Float64} ,
                              y     :: Vector{Float64} ,
                              y0    :: Real            ,
                              ymean :: Real            )

    approx = Vector{Float64}(undef,length(x)-1)
    for i in 1:length(x)-1
        x1 = @view(x[1:i  ])
        x2 = @view(x[i:end])
        y1 = @view(y[1:i  ])
        y2 = @view(y[i:end])
        Al = trapezoid( x1 , y1 , y0=y0     )
        Au = trapezoid( x2 , y2 , inv=ymean )

        approx[i] = abs(Al-Au)
    end
    minind = findmin(approx)[2]

    return x[minind]
end

还有:

function EAM_with_threads( x     :: Vector{Float64} ,
                           y     :: Vector{Float64} ,
                           y0    :: Real            ,
                           ymean :: Real            )

    approx = Vector{Float64}(undef,length(x)-1)
    for i in 1:length(x)-1
        x1 = @view(x[1:i  ])
        x2 = @view(x[i:end])
        y1 = @view(y[1:i  ])
        y2 = @view(y[i:end])
        Al = @spawn trapezoid( x1 , y1 , y0=y0     )
        Au = @spawn trapezoid( x2 , y2 , inv=ymean )

        approx[i] = abs(fetch(Al)-fetch(Au))
    end
    minind = findmin(approx)[2]

    return x[minind]
end

这是我用来尝试这两个功能的:

using SpecialFunctions
using BenchmarkTools

x = collect(-10.0:5e-4:10.0)
y = erf.(x)

然后得到这些结果:

julia> @btime EAM_without_threads(x,y,-1.0,1.0)
  7.515 s (315905 allocations: 11.94 GiB)

julia> @btime EAM_with_threads(x,y,-1.0,1.0)
  10.295 s (1274131 allocations: 12.00 GiB)

我不明白...使用htop我可以看到我的所有8个线程几乎都在满负荷工作。这是我的机器:

julia> versioninfo()
Julia Version 1.3.1
Commit 2d5741174c (2019-12-30 21:36 UTC)
Platform Info:
  OS: Linux (x86_64-pc-linux-gnu)
  CPU: Intel(R) Core(TM) i7-4712MQ CPU @ 2.30GHz
  WORD_SIZE: 64
  LIBM: libopenlibm
  LLVM: libLLVM-6.0.1 (ORCJIT, haswell)
Environment:
  JULIA_NUM_THREADS = 8

我知道处理多个线程的开销,并且在小问题中我知道它是否较慢,但为什么在这种情况下?

我也在寻找多线程“良好实践”,因为我猜不是每段代码都会从并行性中受益。

提前谢谢大家。

最佳答案

您的代码在这里做了一些非常多余的工作。它对每个步骤进行完整的梯形积分,而不是仅增量更新 AlAu。在这里,我重写了代码,使其执行零分配,并且我的 EAM 版本在我的计算机上比原始版本快 5 个数量级,且不使用任何线程。

一般来说:在开始研究线程之类的事情之前,请考虑您的算法是否高效。与线程相比,快速算法可以获得更大的加速。

function trapz(x, y; y0=0.0, inv=NaN)
    length(x) != length(y) && error("Input arrays cannot have different lengths")
    s = zero(eltype(x))
    if isnan(inv)
        @inbounds for i in eachindex(x, y)[1:end-1]
            s += (y[i+1] + y[i] - 2y0) * (x[i+1] - x[i])
        end
    else
        @inbounds for i in eachindex(x, y)[1:end-1]
            s += (2inv - (y[i+1] + y[i]) - 2y0) * (x[i+1] - x[i])
        end
    end
    return s / 2
end



function eam(x, y, y0, ymean)
    length(x) != length(y) && error("Input arrays cannot have different lengths")
    Au = trapz(x, y; inv=ymean)
    Al = zero(Au)
    amin = abs(Al - Au)
    ind = firstindex(x)
    @inbounds for i in eachindex(x, y)[2:end-1] # 2:length(x)-1
        Al += (y[i] + y[i-1] - 2y0) * (x[i] - x[i-1]) / 2
        Au -= (2ymean - (y[i] + y[i-1])) * (x[i] - x[i-1]) / 2
        aval = abs(Al - Au)
        if aval < amin
            (amin, ind) = (aval, i)
        end
    end
    return x[ind]
end

此处的基准(我对您的代码使用 @time ,对我自己的代码使用 @btime ,因为使用 @btime< 太耗时了 非常慢的代码):

julia> x = collect(-10.0:5e-4:10.0);

julia> y = erf.(x);

julia> @time EAM_without_threads(x, y, -1.0, 1.0)
 15.611004 seconds (421.72 k allocations: 11.942 GiB, 11.73% gc time)
0.0

julia> @btime eam($x, $y, -1.0, 1.0)
  181.991 μs (0 allocations: 0 bytes)
0.0

一点额外的说明:你不应该写 if isnan(inv) == true,这是多余的。只需编写if isnan(inv)即可。

关于multithreading - 使用 Julia 1.3.1 的多线程速度较慢,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59634148/

相关文章:

linux - 线程如何平等地服务两个数据套接字(而不是控制套接字)?

arrays - 在 Julia 中连接多维数组

julia - 如何在 Julia 中快速求解多项式方程?

julia - 如何不在 Julia 的内部 block 中隐藏变量?

java - 停止长时间等待的线程

java - 从不同类的不同线程修改 JavaFX gui

python - 线程 : AssertionError: group argument must be None for now

multithreading - 如何使用单个端口处理多个逻辑数据流(Winsock)?

performance - Julia:有效地将压缩的元组映射到元组

for-loop - 替代 Julia 中的嵌套 for 循环