优化大型模型 - 尝试利用并行性

3
我有一段代码,我已经花了一周左右的时间重写它,以使其尽可能快地运行。该代码模拟了一个衍射激光束,其本质是对640*640内核在许多2D 1280*1280切片上进行卷积 - 每个切片都是沿着光轴的新位置。
优化的第一阶段是编译我的函数,第二阶段是学习到Mathematica喜欢操作大量数据的列表 - 因此将3D空间同时传递给它而不是一个接一个地传递切片。
然而,这样做会消耗我的RAM!
以下是我的当前设置:
Func2[K_ , ZRange_] :=
Module[{layers = Dimensions[ZRange][[1]]},
x = ConstantArray[Table[x, {x, -80, 80, 0.125}, {y, -80, 80, 0.125}], {layers}];
y = ConstantArray[Table[y, {x, -80, 80, 0.125}, {y, -80, 80, 0.125}], {layers}];
z = Table[ConstantArray[z, {1281, 1281}], {z, ZRange}];

UTC = Func3[x, y, z];

Abs[ListConvolve[K, #] & /@ UTC]
] 


Func3 = Compile[{{x, _Real}, {y, _Real}, {z, _Real}},
Module[{Sr2R2 = Sqrt[x^2 + y^2 + z^2]},
0.5 (1. + z/Sr2R2) Exp[2 \[Pi] I (Sr2R2 - z)]/Sr2R2],
RuntimeAttributes -> {Listable},
CompilationTarget -> "C"
];


ZRangeList = {{20., 19., 18., 17., 16., 15., 14., 13., 12., 11.},
               {10., 9., 8., 7., 6., 5., 4., 3., 2., 1.}};


results = Table[Func2[kernel, ZList], {ZList, ZRangeList}];

一些解释:

  • 这项工作分为两个函数,因为我希望尽可能编译。
  • Z值被分成一个列表的列表,以使函数同时评估多个层。

一些问题:

  • 你如何使它更快?
  • 按原样运行时,我的两个核心都在使用但只有一个mathematica内核。如果我使用ParallelTable运行它,则运行多个内核但消耗更多RAM并且最终速度较慢。
  • 我希望能够在尽可能多的核心上运行它-我有一个LightweightGrid正在运行-我该怎么做?
  • 为什么我不能将已编译的函数传递给不同维度的列表?
2个回答

1

我立刻注意到的是

Abs[ListConvolve[K, #] & /@ UTC] 可以改为 ParallelMap[Abs@ListConvolve[K, #] & , UTC]

然而,我真的很惊讶ParallelTable比普通表格慢,因为只有在两种情况下才会出现这种情况:并行化比执行任务更昂贵,或者并行化需要太多子内核之间的通信。

当你并行化时,你是否分发了你的定义?例如,对于上面的例子,在开始之前,你应该先启动Kernels,然后分发K的定义(UTC不需要分发,因为它实际上没有在子内核中使用,而是使用其部分。另外,看看是否可以利用Share[]来减少内存负载。

你有没有考虑使用CUDA来完成这个任务?对于你在函数内部进行的简单数值计算来说,似乎非常完美。

还有,请注意,您不断地重新创建这个表格:Table[x,{x,-80,80,0.125},{y,-80,80,0.125}],为什么不将其变成一个变量,并创建该变量值的ConstantArray?您每次都浪费约0.2秒钟。

最后,一个微小的怪癖:在优化时进行除法运算总是很糟糕的事情 - 它很耗时间:

Module[{Sr2R2 = Sqrt[x^2 + y^2 + z^2]},
       0.5 (1. + z/Sr2R2) Exp[2 \[Pi] I (Sr2R2 - z)]/Sr2R2]

可以稍微改进一下(欢迎检查我的数学):

Module[{R2=N[x^2 + y^2 + z^2],Sr2R2 = Sqrt[R2]}, 
       (0.5 Exp[2 I \[Pi] (Sr2R2 - z)] (Sr2R2 + z))/R2]

1

无论是并行化还是C编译(使用来自equation.com的gcc 4.7并搭配Windows 64位上的VC++Express)均不能改善时间。

运行此代码需要约6.5秒:

    $start = AbsoluteTime[];
Func2[K_, ZRange_] := 
 Module[{layers = Dimensions[ZRange][[1]], x, y, z, UTC, tx, ty, t1},
  tx = Table[x, {x, -80, 80, 0.125}, {y, -80, 80, 0.125}];
  ty = Table[y, {x, -80, 80, 0.125}, {y, -80, 80, 0.125}];
  x = ConstantArray[tx, {layers}];
  y = ConstantArray[ty, {layers}];
  z = Table[ConstantArray[z, {1281, 1281}], {z, ZRange}];
  t1 = AbsoluteTime[];
  UTC = Func3[x, y, z];
  Print["Func3 time = ", AbsoluteTime[] - t1];
  Abs[ListConvolve[K, #] & /@ UTC]]
Func3 = Compile[{{x, _Real, 3}, {y, _Real, 3}, {z, _Real, 3}}, 
   Module[{Sr2R2 = Sqrt[x^2 + y^2 + z^2]}, 
    0.5 (1. + z/Sr2R2) Exp[2 \[Pi] I (Sr2R2 - z)]/Sr2R2]];
ZRangeList = {{20., 19., 18., 17., 16., 15., 14., 13., 12., 
    11.}, {10., 9., 8., 7., 6., 5., 4., 3., 2., 1.}};
SeedRandom[1]; kernel = RandomReal[{-1, 1}, {640, 640}];
results1 = Table[Func2[kernel, ZList], {ZList, ZRangeList}];
AbsoluteTime[] - $start

将所有内容编译成一个函数会变慢(8.1秒):

$start = AbsoluteTime[]; 
CFunc2 = Compile[{{kern, _Real, 2}, {ZRange, _Real, 1}}, 
    Module[{layers = Length[ZRange], x, y, z, UTC, ty, Sr2R2}, 
     ty = Table[y, {x, -80, 80, 0.125}, {y, -80, 80, 0.125}]; 
      x = Table[x, {layers}, {x, -80, 80, 0.125}, {y, -80, 80, 0.125}]; 
      y = Table[y, {layers}, {x, -80, 80, 0.125}, {y, -80, 80, 0.125}]; 
      z = Table[ConstantArray[z, {1281, 1281}], {z, ZRange}]; 
      Sr2R2 = Sqrt[x^2 + y^2 + z^2]; UTC = 0.5*(1. + z/Sr2R2)*
        (Exp[2*Pi*I*(Sr2R2 - z)]/Sr2R2); 
      Abs[(ListConvolve[kern, #1] & ) /@ UTC]]]; 
ZRangeList = {{20., 19., 18., 17., 16., 15., 14., 13., 12., 11.}, 
    {10., 9., 8., 7., 6., 5., 4., 3., 2., 1.}}; 
SeedRandom[1]; kernel = RandomReal[{-1, 1}, {640, 640}]; 
results = Table[CFunc2[kernel, ZList], {ZList, ZRangeList}]; 
AbsoluteTime[] - $start

通常很难确定ParallelTable等函数何时真正有用。这取决于问题、规模、Mathematica版本等因素。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接