3

在过去一周左右的时间里,我一直在重写一大段代码,以使其尽快运行。

该代码正在对衍射激光束进行建模,其本质是一个 640*640 内核在许多 2D 1280*1280 切片上的卷积——每个切片都是沿光束轴的一个新位置。

优化的第一阶段是编译我的函数,第二阶段是学习 Mathematica 喜欢处理大量数据列表——因此一次将多层的 3D 空间传递给它,而不是一个接一个地切片。

然而,这吃了我的内存!

这是我目前的设置:

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 正在运行——我该怎么做?
  • 为什么我不能传递不同维度的编译函数列表?
4

2 回答 2

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 版本等。

于 2011-11-08T23:39:53.873 回答
1

让我瞬间跳出来的是

Abs[ListConvolve[K, #] & /@ UTC] 可以做成 ParallelMap[Abs@ListConvolve[K, #] & , UTC]

但是,令我感到惊讶的是 ParallelTable 比普通表慢,因为这仅在两种情况下出现:并行化比执行任务更昂贵,或者并行化需要子内核之间的过多通信。

您是否在并行化时分发了定义?例如,对于上述内容,您甚至在开始之前首先要 LaunchKernels,然后分发 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]
于 2011-11-07T19:01:32.350 回答