Delphi 的并行计算

2017-04-12 21:14:22来源:CSDN作者:pcplayer人点击

所谓并行计算,可以让一段代码让 CPU 的多个核同时开跑,非常明显地提高代码执行速度。

所谓“程序”,这个中文单词,严格意义上来说,就是按照特定顺序,一步一步地执行一些指令。这是标准的串行计算。串行计算的好处是有上下文依赖关系的事情,不会搞错顺序。好比先洗碗,再打饭,程序这样写了,计算机绝对不会搞错成先打了饭吃完了才发现碗先没洗导致肚子疼。


但很多时候,我们有大量数据需要计算,并且数据之间没有前后依赖关系。比如:图片处理。我需要逐个像素处理,但处理第一个像素和处理第二个像素,没有依赖关系。处理第一行和处理第二行,也没有依赖关系。这时候,可以同时并行地处理。传统做法是,我们写一个循环,一行一行的逐行处理;在一行中,我们写个循环,一个像素一个像素地逐个处理。最终结果就是一张图片,我们是逐个像素地处理。如果图片有 100 万个像素,就要循环 100 万次。很消耗时间。


现代的 CPU,都是多核的。一个循环跑下来,只使用了一个核。其它几个核都空闲着。并行计算的概念就是,把上面说的那种没有先后依赖关系的数据处理,分到几个核里面同时处理。就好比有 12 个碗要洗,找一个人来一只一只地洗,需要12分钟;找4个人来一起洗,每个人只需要洗三个碗,洗完 12 个碗只需要3分钟。


Delphi 已经支持并行计算。语法上,该怎么写呢?我们来看一个例子。这个例子的代码,是我自己写了以后测试过的(在英巴的官方论坛上请教了一下,得到 Delphi 官方人员的帮助下写出来的代码)。测试程序是一个跑在手机里的 APP,测试 Delphi 的并行计算代码跑安卓手机上面,是否能利用手机 CPU 的多个核。这个代码完成一个图片逐个像素的处理:将 RGB565 格式的图片转为 RGB888 的图片,每个像素单独计算。注意这是 Delphi 写的手机程序,是基于 FireMonkey 框架的,里面的 TBitmap 和 VCL 底下的 TBitmap 有点不一样。


看代码,第一个函数,对图片像素的一行,做一个循环,将一行里面的每个像素拿出来,做一次转换计算:

//这是计算一行的颜色值。其中 InputPtr 是 RGB565 的数据在内存里面的指针;ColorPtr 是 TBitmap 的 TBitmapData 的指针。 //BTW: 以下代码有点问题:颜色值可能搞错,出来的结果,灰色正确,黄色变成了蓝色。图像清晰度没问题。procedure TForm1.ConvertOneLine(const BmpWidth: Integer; InputPtr: PWord;  ColorPtr: PByte);var  Col: Integer;  Color: Word;begin  for Col := 1 to BmpWidth do  begin    Color := InputPtr^;    Inc(InputPtr);    ColorPtr^ := (( Color         and $1F) * $FF) div $1F;    Inc(ColorPtr);    ColorPtr^ := (((Color shr  5) and $3F) * $FF) div $3F;    Inc(ColorPtr);    ColorPtr^ := (((Color shr 11) and $1F) * $FF) div $1F;    Inc(ColorPtr);    ColorPtr^ := $FF;    Inc(ColorPtr);;  end;end;

第二个函数,是对图片的行数,做一个循环,逐行处理。这个函数里面调用上面那个一行里面逐点处理的方法。实际上就是两层循环:


//以下代码,有串行计算的代码,也有并行计算的代码,都测试通过。procedure TForm1.ConvertRGB565ToRGB8888(const BmpWidth, BmpHeight: Integer; RGB565DataPtr: PWord; BmpData: TBitmapData);var  InputPitch: Integer;  Row, Col: Integer;  Color: Word;  ColorPtr: PByte;  InputPtr: PWord; // Pointer to RGB565 databegin{------------------------------------------------------------------------  把 RGB565 的数据转换到 FireMonkey 的 TBitmap 里面去。算法来自 Delphi 官方网站论坛里面的一个程序员对我的问题的回复。------------------------------------------------------------------------}  InputPitch:= BmpWidth*2;  //将下面的串行循环,改为并行循环,在4核手机上测试通过。  //大概比串行循环的时间少一半(并没有少 1/4)。  TParallel.For(1, BmpHeight, procedure(Row: Integer)  begin    InputPtr:= PWord(PByte(RGB565DataPtr) + (Row-1)*InputPitch);    ColorPtr:= PByte(BmpData.Data) + (BmpHeight-Row)*BmpData.Pitch;    Self.ConvertOneLine(BmpWidth, InputPtr, ColorPtr);  end  );  { 测试结果:以下串行计算代码执行正确。  for Row := 1 to BmpHeight do  begin    InputPtr:= PWord(PByte(RGB565DataPtr) + (Row-1)*InputPitch);    ColorPtr:= PByte(BmpData.Data) + (BmpHeight-Row)*BmpData.Pitch;    Self.ConvertOneLine(BmpWidth, InputPtr, ColorPtr);    //TParallel.&For  end;  }end;

下面是主程序,输入一张图片,调用上面那个函数,完成转换:

//以下代码是调用 ConvertRGB565ToRGB8888 的主程序:procedure TForm1.DrawRGB656OntoBMP;var  //直接将 RGB656 的数据画到 BITMAP 上去试试看:  ABitmap: TBitmap;  BmpData: TBitmapData;  BmpWidth: Integer;  BmpHeight: Integer;  RGB565DataPtr, InputPtr: PWord; // Pointer to RGB565 data  AMemoryStream: TMemoryStream;  Fn: string;  T: TDateTime;  InputPitch: Integer;  Row, Col: Integer;  Color: Word;  ColorPtr: PByte;begin// 这段代码的测试结果:1. 在安卓下,直接出现浮点运算异常错误;//2. 在 WINDOWS 底下,执行完成,但没有图像显示。并且转换过程耗时 200MS.跟踪 ScanlineToAlphaColor 函数内部,实际上是逐个点转换为 RGB888  Fn := Memo1.Lines.Strings[0];  AMemoryStream := TMemoryStream.Create;  AMemoryStream.LoadFromFile(Fn);  try    //ABitmap := Image1.Bitmap;    BmpWidth:= 1280;    BmpHeight:= 720;    Image1.Bitmap.SetSize(BmpWidth, BmpHeight);    RGB565DataPtr := PWord(NativeInt(AMemoryStream.Memory) + 66);    InputPitch:= BmpWidth*2; // Bytes by row    T := Now;    if Image1.Bitmap.Map(TMapAccess.Write, BmpData) then  //Map 方法取得 Bitmap 内部的数据结构,然后才能直接操作数据结构内部的指针。    try      Self.ConvertRGB565ToRGB8888(BmpWidth, BmpHeight, RGB565DataPtr, BmpData);    finally      Image1.Bitmap.Unmap(BmpData);      Image1.UpdateEffects;    end;    Memo1.Lines.Add('time consuming = ' + IntToStr(MilliSecondsBetween(Now, T)));  finally    AMemoryStream.Free;  end;end;

测试结果:

对720P 的 RGB565 转码为 RGBA888,串行计算在 Find5 手机上耗时58MS,并行计算耗时 32MS。


结论:Delphi 的并行计算代码,确实可以让多个核同时跑起来,缩短计算时间。

最新文章

123

最新摄影

微信扫一扫

第七城市微信公众平台