我可以利用并行化来加快这段代码的速度吗?

时间:2014-04-25 10:48:15

标签: parallel-processing julia

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

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);
elapsed time: 3.4292e-5 seconds (4148 bytes allocated)

我天真地尝试过:

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);
elapsed time: 0.0418593 seconds (3457828 bytes allocated)
sol2==sol1
true

结果是正确的,但性能要差得多。所以为什么?因为并行化不适用于双核老式笔记本电脑,或者我又错了?

嗯,我承认我对并行化唯一了解的是它可以加速代码但不是每一段代码都可以并行,在尝试并行编程之前是否应该知道任何基本知识?

任何帮助都将不胜感激。

2 个答案:

答案 0 :(得分:3)

有几件事正在发生。首先,注意消耗的内存差异。这表明出现了问题。通过从核心算法中分离分配(您的zerosdzeros行),您将获得更大的清晰度。但是,分配使用的内存不太可能;更有可能的是,循环中的某些东西正在使用内存。请注意,您正在描述左侧的localpart,但您正在使用右侧的原始DArray。这可能会触发一些IPC流量。如果需要调试内存消耗,请参阅ProfileView包。

其次,对我来说,你真的在​​进程中解决问题并不明显。你循环遍历整个数组的每个元素,而应该让每个工作循环遍历它自己的数组。但是,您将在localparts之间的边缘遇到问题,因为更新需要相邻的值。你最好使用SharedArray

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

答案 1 :(得分:1)

N.B。:我是亲戚朱莉娅,FDTD,麦克斯韦方程组和并行处理菜鸟。

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

此外,Wikipedia Finite-difference time-domain method页面提供了一些参考资料和软件包链接的良好信息,其中一些使用了某种并行处理方式。

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

使用你的玩具1D问题和我有限的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左右。

这是在Julia学习并行处理的一个有趣案例,但如果我需要使用FDTD解决Maxwell方程,我首先考虑已经有的许多FDTD软件库。也许Julia软件包可以与其中一个接口。