【发布时间】:2014-04-03 10:43:56
【问题描述】:
我正在研究差分进化优化算法的实现,并希望通过并行计算种群成员来加快计算时间。 我正在使用 OmniThread 库,并且已经成功地并行化了我的循环,却发现它的运行速度比串行实现慢。
我把代码精简到了本质来测试并行化,精简版也出现了同样的问题:并行版比串行版慢。
关键是我传递了多个动态数组,应该为每个总体成员写入输出。每个数组都有一个专用于人口成员的维度,因此对于每个人口成员,可以访问一组不同的数组索引。这也意味着在并行实现中没有 2 个线程会写入同一个数组元素。
在我用来测试的代码下方(差分进化中的实际代码有一个DoWork 过程,还有更多const 参数和var 数组)
unit Unit1;
interface
type
TGoalFunction = reference to function(const X, B: array of extended): extended;
TArrayExtended1D = array of extended;
TArrayExtended2D = array of TArrayExtended1D;
TClassToTest = class abstract
private
class procedure DoWork(const AGoalFunction: TGoalFunction; const AInputArray: TArrayExtended2D; var AOutputArray1: TArrayExtended1D; var AOutputArray2: TArrayExtended2D; const AIndex, AIndex2: integer);
public
class procedure RunSerial;
class procedure RunParallel;
end;
function HyperSphere(const X, B: array of extended): extended;
const
DIMENSION1 = 5000;
DIMENSION2 = 5000;
LOOPS = 10;
implementation
uses
OtlParallel;
function HyperSphere(const X, B: array of extended): extended;
var
I: Integer;
begin
Result := 0;
for I := 0 to Length(X) - 1 do
Result := Result + X[I]*X[I];
end;
{ TClassToTest }
class procedure TClassToTest.DoWork(const AGoalFunction: TGoalFunction; const AInputArray: TArrayExtended2D; var AOutputArray1: TArrayExtended1D; var AOutputArray2: TArrayExtended2D; const AIndex, AIndex2: integer);
var
I: Integer;
begin
AOutputArray1[AIndex] := AGoalFunction(AInputArray[AIndex], []);
for I := 0 to Length(AOutputArray2[AIndex]) - 1 do
AOutputArray2[AIndex, I] := Random*AIndex2;
end;
class procedure TClassToTest.RunParallel;
var
LGoalFunction: TGoalFunction;
LInputArray: TArrayExtended2D;
LOutputArray1: TArrayExtended1D;
LOutputArray2: TArrayExtended2D;
I, J, K: Integer;
begin
SetLength(LInputArray, DIMENSION1, DIMENSION2);
for I := 0 to DIMENSION1 - 1 do
begin
for J := 0 to DIMENSION2 - 1 do
LInputArray[I, J] := Random;
end;
SetLength(LOutputArray1, DIMENSION1);
SetLength(LOutputArray2, DIMENSION1, DIMENSION2);
LGoalFunction := HyperSphere;
for I := 0 to LOOPS - 1 do
begin
Parallel.ForEach(0, DIMENSION1 - 1).Execute(
procedure (const value: integer)
begin
DoWork(LGoalFunction, LInputArray, LOutputArray1, LOutputArray2, value, I);
end
);
for J := 0 to DIMENSION1 - 1 do
begin
for K := 0 to DIMENSION2 - 1 do
LInputArray[J, K] := LOutputArray2[J, K];
end;
end;
end;
class procedure TClassToTest.RunSerial;
var
LGoalFunction: TGoalFunction;
LInputArray: TArrayExtended2D;
LOutputArray1: TArrayExtended1D;
LOutputArray2: TArrayExtended2D;
I, J, K: Integer;
begin
SetLength(LInputArray, DIMENSION1, DIMENSION2);
for I := 0 to DIMENSION1 - 1 do
begin
for J := 0 to DIMENSION2 - 1 do
LInputArray[I, J] := Random;
end;
SetLength(LOutputArray1, DIMENSION1);
SetLength(LOutputArray2, DIMENSION1, DIMENSION2);
LGoalFunction := HyperSphere;
for I := 0 to LOOPS - 1 do
begin
for J := 0 to DIMENSION1 - 1 do
begin
DoWork(LGoalFunction, LInputArray, LOutputArray1, LOutputArray2, J, I);
end;
for J := 0 to DIMENSION1 - 1 do
begin
for K := 0 to DIMENSION2 - 1 do
LInputArray[J, K] := LOutputArray2[J, K];
end;
end;
end;
end.
我原本预计我的 8 核处理器的加速比约为 6 倍,但遇到了轻微的减速。我应该改变什么来提高并行运行DoWork 过程的速度?
请注意,我更愿意将实际工作保留在 DoWork 过程中,因为我必须能够在有或没有并行化(布尔标志)的情况下调用相同的算法,同时保持代码主体易于共享维护
【问题讨论】:
-
听起来像是虚假分享。虽然快速扫描表明每个任务都在不同的阵列上工作,所以这无法解释。您的任务是否足够大,可以通过线程来提供帮助?如果您不使用骇人听闻的
Extended数据类型,您也会获得值得的加速。它的对齐属性会严重伤害你。使用Double。 -
@David 此处介绍的任务将执行
Result := Result + X[I]*X[I];5000 次,并且每个外循环也将执行此任务 5000 次(执行 10 次)。 DE 算法中的实际任务是或者可以肯定大得多,但是由实现的用户来确定任务是否足够大以保证并行化(因此它应该以串行和并行方式实现)跨度> -
我认为 FWIW Sqr 比 X*X 好,而且 Random 不是线程安全的。
-
@DavidHeffernan 不是线程安全且慢,问题应该是它(随机)
-
我确实使用基因编程(不在 Delphi 中),这是一个“令人尴尬的并行”问题的一个很好的例子,最好使用进程而不是线程来完成。不需要在人口成员之间共享数据,因此多处理可以提高性能,而不必担心这个、那个或其他东西是否是线程安全的。不幸的是,没有很多语言或其第三方库对多处理提供良好的支持。
标签: multithreading delphi omnithreadlibrary