Parallel processing 我可以利用并行化使这段代码更快吗?

Parallel processing 我可以利用并行化使这段代码更快吗?,parallel-processing,julia,Parallel Processing,Julia,好的,后续的问题。我想修改的代码当然是: function fdtd1d_local(steps, ie = 200) ez = zeros(ie + 1); hy = zeros(ie); for n in 1:steps for i in 2:ie ez[i]+= (hy[i] - hy[i-1]) end ez[1]= sin(n/10) for i in 1:ie

好的,后续的问题。我想修改的代码当然是:

function fdtd1d_local(steps, ie = 200)
    ez = zeros(ie + 1);
    hy = zeros(ie);
    for n in 1:steps
        for i in 2:ie
            ez[i]+= (hy[i] - hy[i-1])
        end
        ez[1]= sin(n/10)
        for i in 1:ie
            hy[i]+= (ez[i+1]- ez[i])
        end
    end
    (ez, hy)
end

fdtd1d_local(1);
@time sol1=fdtd1d_local(10);
我天真地尝试过:

function fdtd1d_local_parallel(steps, ie = 200)
    ez = dzeros(ie + 1);
    hy = dzeros(ie);
    for n in 1:steps
        for i in 2:ie
            localpart(ez)[i]+= (hy[i] - hy[i-1])
        end
        localpart(ez)[1]= sin(n/10)
        for i in 1:ie
            localpart(hy)[i]+= (ez[i+1]- ez[i])
        end
    end
    (ez, hy)
end

fdtd1d_local_parallel(1);
@time sol2=fdtd1d_local_parallel(10);
结果是正确的,但性能要差得多。那为什么呢?因为并行化不适用于双核旧笔记本电脑,否则我又错了

我承认,关于并行化,我所知道的唯一一件事是它可以加快代码的速度,但并不是每一段代码都可以并行,在尝试并行编程之前,有什么基本的知识应该知道吗


任何帮助都将不胜感激。

有几件事正在进行。首先,请注意所消耗内存的差异。那是有问题的迹象。通过将分配(您的
zero
dzeros
行)与核心算法分离,您将获得更高的清晰度。然而,分配不太可能占用大部分内存;更有可能的是,循环中的某些内容正在使用内存。请注意,您在左侧描述的是
localpart
,但在右侧使用的是原始
DArray
。这可能会触发一些IPC通信。如果需要调试内存消耗,请参阅ProfileView包

第二,我不太清楚您是否真的要将问题分解到各个流程中。您在整个数组的每个元素上循环,相反,您应该让每个工作循环在其自己的数组片段上。但是,您将在LocalPart之间的边缘处遇到问题,因为更新需要相邻的值。您最好使用
SharedArray


最后,启动线程会增加开销;对于小问题,最好不要并行化,而只使用简单的算法。只有当计算时间达到数百毫秒(或更多)时,我才会考虑进行并行化。

N.B.:我是一个相对的Julia、FDTD、Maxwell方程组和并行处理noob

@Thoy提供了一个很好的答案,提出了需要考虑的重要问题

此外,该页面还提供了一些很好的信息,包括软件包的参考和链接,其中一些软件包使用了某种并行处理方式

FDTD的许多并行处理方法似乎将物理环境划分为更小的块,然后并行计算这些块。一个复杂的问题是边界条件必须在相邻块之间传递

利用你的玩具1D问题和我有限的Julia技能,我实现了在我的机器上使用两个内核的玩具。它不是最通用的、模块化的、可扩展的、有效的,也不是最高效的,但它确实演示了并行处理。希望Julia向导能改进它

以下是我使用的Julia代码:

addprocs(2)

@everywhere function ez_front(n::Int, ez::DArray, hy::DArray)
  ez_local=localpart(ez)
  hy_local=localpart(hy)
  ez_local[1]=sin(n/10)
  @simd for i=2:length(ez_local)
    @inbounds ez_local[i] += (hy_local[i] - hy_local[i-1])
  end
end

@everywhere function ez_back(ez::DArray, hy::DArray)
  ez_local=localpart(ez)
  hy_local=localpart(hy)
  index_boundary::Int = first(localindexes(hy)[1])-1
  ez_local[1] += (hy_local[1]-hy[index_boundary])
  @simd for i=2:length(ez_local)
    @inbounds ez_local[i] += (hy_local[i] - hy_local[i-1])
  end
end

@everywhere function hy_front(ez::DArray, hy::DArray)
  ez_local=localpart(ez)
  hy_local=localpart(hy)
  index_boundary = last(localindexes(ez)[1])+1
  @simd for i=1:(length(hy_local)-1)
    @inbounds hy_local[i] += (ez_local[i+1] - ez_local[i])
  end
  hy_local[end] += (ez[index_boundary] - ez_local[end])
end

@everywhere function hy_back(ez::DArray, hy::DArray)
  ez_local=localpart(ez)
  hy_local=localpart(hy)
  @simd for i=2:(length(hy_local)-1)
    @inbounds hy_local[i] += (ez_local[i+1] - ez_local[i])
  end
  hy_local[end] -= ez_local[end]
end


function fdtd1d_parallel(steps::Int, ie::Int = 200)
  ez = dzeros((ie,),workers()[1:2],2)
  hy = dzeros((ie,),workers()[1:2],2)
  for n = 1:steps
    @sync begin
      @async begin
        remotecall(workers()[1],ez_front,n,ez,hy)
        remotecall(workers()[2],ez_back,ez,hy)
      end
    end
    @sync begin
      @async begin
        remotecall(workers()[1],hy_front,ez,hy)
        remotecall(workers()[2],hy_back,ez,hy)
      end
    end
  end
  (convert(Array{Float64},ez), convert(Array{Float64},hy))
end

fdtd1d_parallel(1);
@time sol2=fdtd1d_parallel(10);
在我的机器(一台旧的32位双核笔记本电脑)上,这个并行版本并不比本地版本快,直到
ie
被设置为大约
5000000


这是朱丽亚学习并行处理的一个有趣的例子,但是如果我需要用FDTD来求解麦斯威尔方程组,我首先会考虑许多已经可用的FDTD软件库。也许Julia包可以与其中一个接口。

好吧,正如问题中提到的,我的第二个示例只是一个幼稚的试验。(我不太理解教程中相对简单的部分囧), 遗憾的是,
SharedArray
不适用于Windows。问题中的示例实际上是一个简化的1D Maxwell方程解算器,当列表(
ez
hy
,在3D情况下还有更多)变得更大,。值得一提的是,此代码仅在v0.3上工作,
@inbounds
也加快了代码的速度。在将
@inbounds
添加到非并行版本后,除非我在
100
周围使用
步骤,否则并行化代码不会显示其优势,我无法很好地理解此行为。anyway,谢谢你的努力!是的,我通常是Julia repo的负责人。并行化(设置、消息传递等)会带来一些开销,我的机器只有一条到内存的慢总线,核心必须共享。尽管答案是“不是最通用、模块化、可扩展、有效或高效的”,没有人可以忽视你的努力,千金市骨, 那么,+50:D
elapsed time: 0.0418593 seconds (3457828 bytes allocated)
sol2==sol1
true
addprocs(2)

@everywhere function ez_front(n::Int, ez::DArray, hy::DArray)
  ez_local=localpart(ez)
  hy_local=localpart(hy)
  ez_local[1]=sin(n/10)
  @simd for i=2:length(ez_local)
    @inbounds ez_local[i] += (hy_local[i] - hy_local[i-1])
  end
end

@everywhere function ez_back(ez::DArray, hy::DArray)
  ez_local=localpart(ez)
  hy_local=localpart(hy)
  index_boundary::Int = first(localindexes(hy)[1])-1
  ez_local[1] += (hy_local[1]-hy[index_boundary])
  @simd for i=2:length(ez_local)
    @inbounds ez_local[i] += (hy_local[i] - hy_local[i-1])
  end
end

@everywhere function hy_front(ez::DArray, hy::DArray)
  ez_local=localpart(ez)
  hy_local=localpart(hy)
  index_boundary = last(localindexes(ez)[1])+1
  @simd for i=1:(length(hy_local)-1)
    @inbounds hy_local[i] += (ez_local[i+1] - ez_local[i])
  end
  hy_local[end] += (ez[index_boundary] - ez_local[end])
end

@everywhere function hy_back(ez::DArray, hy::DArray)
  ez_local=localpart(ez)
  hy_local=localpart(hy)
  @simd for i=2:(length(hy_local)-1)
    @inbounds hy_local[i] += (ez_local[i+1] - ez_local[i])
  end
  hy_local[end] -= ez_local[end]
end


function fdtd1d_parallel(steps::Int, ie::Int = 200)
  ez = dzeros((ie,),workers()[1:2],2)
  hy = dzeros((ie,),workers()[1:2],2)
  for n = 1:steps
    @sync begin
      @async begin
        remotecall(workers()[1],ez_front,n,ez,hy)
        remotecall(workers()[2],ez_back,ez,hy)
      end
    end
    @sync begin
      @async begin
        remotecall(workers()[1],hy_front,ez,hy)
        remotecall(workers()[2],hy_back,ez,hy)
      end
    end
  end
  (convert(Array{Float64},ez), convert(Array{Float64},hy))
end

fdtd1d_parallel(1);
@time sol2=fdtd1d_parallel(10);