Оптимизация большой модели - попытка использовать параллелизм - PullRequest
3 голосов
/ 05 ноября 2011

У меня есть кусок кода, который я переписывал за последнюю неделю или около того, чтобы запустить его как можно быстрее.

Код моделирует дифрагированный лазерный луч, и его сущность представляет собой свертку ядра 640 * 640 по множеству 2D срезов 1280 * 1280 - каждый срез представляет собой новую позицию вдоль оси луча.

Первым этапом оптимизации была компиляция моих функций, а вторым этапом было изучение того, что Mathematica любит работать с большими списками данных - таким образом, передавая ему трехмерное пространство сразу из нескольких слоев, а не срезов один за другим.

Однако это съело мою оперативную память!

Вот мои текущие настройки:

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, он запускает несколько ядер, но ест больше оперативной памяти и в конечном итоге медленнее.
  • Я хотел бы иметь возможность запускать его на максимально возможном количестве ядер - у меня работает LightweightGrid - как я могу это сделать?
  • Почему я не могу передать скомпилированные списки функций разных размеров?

Ответы [ 2 ]

1 голос
/ 09 ноября 2011

Ни распараллеливание, ни даже C-компиляция (использование gcc 4.7 с веб-сайта уравнения.com, дополненного VC ++ Express в 64-разрядной версии Windows) действительно улучшает время.

Для запуска этого кода требуется около 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 и т. Д.

1 голос
/ 07 ноября 2011

То, что мгновенно выскакивает у меня, это

Abs [ListConvolve [K, #] & / @ UTC] может быть превращен в ParallelMap [Abs @ ListConvolve [K, #] &, UTC]

Тем не менее, я действительно удивлен, что ParallelTable медленнее, чем обычная таблица, поскольку это имеет место только в 2 ситуациях: распараллеливание обходится дороже, чем выполнение задачи, или распараллеливание требует слишком много связи между под-ядрами.

Распределяли ли вы свои определения при распараллеливании?Например, выше, вы должны сначала запустить 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]
...