如何在工作进程中使用SharedArray上的插值,而不需要每个进程创建自己的数据副本



我正在64位Debian Stretch中使用Julia 0.4.6("测试")。我目前正在一个4核4Gbyte虚拟机中使用它。

我主要想做的是让主进程设置一个3D SharedArray,然后在工作进程上并行化从该3D阵列派生的2D阵列的生成。问题是,如果我想让工作人员通过插值访问该卷(所有这些,因为我无法预测他们实际需要哪些位),我似乎会遇到内存使用率过高的问题。

下面是一些演示我尝试做什么的最小代码…

首先是无插值版本:

# Initialize 1GByte of SharedArray
volume=SharedArray(Float32,(512,512,1024))  # 1 GByte of shared data
volume[:,:,:]=rand(Float32,(512,512,1024))
# Function to compute directly from array
function test0()
  image=SharedArray(Float32,(512,512))
  @sync @parallel for y=1:512
    for x=1:512
      image[x,y]=volume[x,y,512]
    end
  end
  sdata(image)
end
println(mean(test0()))

无论我是在没有工人的情况下运行它,还是在-p 2、-p 4、-p 8或-p 16的情况下,这都很好(没有太多性能改进,但请记住,这只是一个在每次迭代中进行更多计算的模型)。

然而,这个版本:

using Interpolations
# Function to compute via interpolator
function test1()
  sampler=interpolate(volume,BSpline(Linear()),OnGrid())
  image=SharedArray(Float32,(512,512))
  @sync @parallel for i=1:512*512
    x,y=ind2sub((512,512),i)
    image[x,y]=sampler[x,y,512]
  end
  sdata(image)
end
println(mean(test1()))

用-p 2启动系统交换,-p 4使工作进程以OutOfMemoryError s终止。

我猜是插值器到工作进程的序列化忽略了它位于SharedArray上的事实,而只是将数据批发复制到每个工作进程中的非共享副本中。

我的问题是:我能做些什么来改善这一点?对于我在这里尝试做的事情,有没有更好的模式,或者@<something>的一些巧妙使用,会导致每个工作人员从共享数组中插入,而不是从它自己的副本中插入?

更新:

这种方法似乎可以有效地将插值对象的创建推进到每个工作进程中:

function test(volume)
  image=SharedArray(Float32,(512,512))
  sampler=Nullable()
  @sync @parallel for i=1:512*512
    x,y=ind2sub((512,512),i)
    if isnull(sampler)
      warn(STDERR,"Allocating sampler...")
      sampler=Nullable(Interpolations.interpolate(sdata(volume),Interpolations.BSpline(Interpolations.Linear()),Interpolations.OnGrid()))
      warn(STDERR,"...allocated sampler")
    end
    image[x,y]=get(sampler)[x,y,512]
  end
  sdata(image)
end

然而,它仍然面临着完全相同的问题,并且由于内存耗尽,多个工作进程将开始交换或无法创建采样器。

仔细检查Interpolations.jsl代码,就会发现它通常希望填充和/或预过滤给定的数组(这显然意味着它要复制)。。。然而,我还不能很好地阅读Julia,以了解BSpline(Linear())是否有机会绕过正在制作的副本,避免每个进程的巨大内存开销。我看到二次插值提供了一个有趣的选项,即"原地"进行预过滤,但我还没有进一步探索这一点(一个重要的问题似乎是避免所有试图预过滤共享数据的工作人员)。

显然,解决方案是使用interpolate!版本的插值。这与我投入的大量工作流程配合得很好:

using Interpolations
# Function to compute via interpolator
function test(volume)
  image=SharedArray(Float32,(512,512))
  sampler=interpolate!(
    volume,
    Interpolations.BSpline(Interpolations.Linear()),
    Interpolations.OnGrid()
  )
  @sync @parallel for i=1:512*512
    x,y=ind2sub((512,512),i)
    image[x,y]=sampler[x,y,512]
  end
  sdata(image)
end
# Initialize 1GByte of SharedArray
volume=SharedArray(Float32,(512,512,1024))  # 1 GByte of shared data
volume[:,:,:]=rand(Float32,(512,512,1024))
println(mean(test(volume)))

还有一个prefilter!,它暗示可能也可以类似地使用更高阶插值方法,但线性插值对我来说已经足够好了。

相关内容

  • 没有找到相关文章

最新更新