我正在尝试 Delphi XE7 Update 1 的并行编程功能。
我创建了一个简单的 TParallel.For
循环,基本上是进行一些虚假操作来消磨时间。
我在 AWS 实例(c4.8xlarge)上启动程序,使用36个虚拟CPU来测试并行编程可能带来的收益。
当我第一次启动程序并执行 TParallel.For
循环时,我看到了显着的收益(虽然承认比我预期的36个虚拟CPU要少得多):
Parallel matches: 23077072 in 242ms
Single Threaded matches: 23077072 in 2314ms
如果我不关闭程序并在36个vCPU机器上稍后再次运行该传递(例如,立即或10-20秒钟后),则并行传递会大大恶化:
Parallel matches: 23077169 in 2322ms
Single Threaded matches: 23077169 in 2316ms
如果我没有关闭程序,并且等几分钟(不是几秒钟,而是几分钟)再运行密码破解程序,我会再次得到第一次启动程序时的结果(响应时间提高了10倍)。
在36个虚拟CPU的计算机上,刚启动程序后的第一次运行总是比较快的,因此似乎只有在程序第二次调用TParallel.For
函数时才会出现这种效果。
这是我正在运行的示例代码:
unit ParallelTests;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
System.Threading, System.SyncObjs, System.Diagnostics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls;
type
TForm1 = class(TForm)
Button1: TButton;
Memo1: TMemo;
SingleThreadCheckBox: TCheckBox;
ParallelCheckBox: TCheckBox;
UnitsEdit: TEdit;
Label1: TLabel;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.Button1Click(Sender: TObject);
var
matches: integer;
i,j: integer;
sw: TStopWatch;
maxItems: integer;
referenceStr: string;
begin
sw := TStopWatch.Create;
maxItems := 5000;
Randomize;
SetLength(referenceStr,120000); for i := 1 to 120000 do referenceStr[i] := Chr(Ord('a') + Random(26));
if ParallelCheckBox.Checked then begin
matches := 0;
sw.Reset;
sw.Start;
TParallel.For(1, MaxItems,
procedure (Value: Integer)
var
index: integer;
found: integer;
begin
found := 0;
for index := 1 to length(referenceStr) do begin
if (((Value mod 26) + ord('a')) = ord(referenceStr[index])) then begin
inc(found);
end;
end;
TInterlocked.Add(matches, found);
end);
sw.Stop;
Memo1.Lines.Add('Parallel matches: ' + IntToStr(matches) + ' in ' + IntToStr(sw.ElapsedMilliseconds) + 'ms');
end;
if SingleThreadCheckBox.Checked then begin
matches := 0;
sw.Reset;
sw.Start;
for i := 1 to MaxItems do begin
for j := 1 to length(referenceStr) do begin
if (((i mod 26) + ord('a')) = ord(referenceStr[j])) then begin
inc(matches);
end;
end;
end;
sw.Stop;
Memo1.Lines.Add('Single Threaded matches: ' + IntToStr(Matches) + ' in ' + IntToStr(sw.ElapsedMilliseconds) + 'ms');
end;
end;
end.
这是否是按设计工作的?我发现了这篇文章(http://delphiaball.co.uk/tag/parallel-programming/)建议我让库自己决定线程池,但如果我必须等待数分钟才能使请求更快地得到服务,使用并行编程的意义在哪里?
我对如何使用 TParallel.For
循环有什么遗漏吗?
请注意,我在 AWS m3.large 实例上无法重现此问题(根据 AWS,该实例有 2 个 vCPU)。在该实例上,我总是能够获得轻微的改进,并且在不久之后连续调用 TParallel.For
时也不会得到更差的结果。
Parallel matches: 23077054 in 2057ms
Single Threaded matches: 23077054 in 2900ms
看起来,这种情况发生在有许多可用核心(36个)时,这很遗憾,因为并行编程的整个重点是从许多核心中获益。我想知道这是否是库错误,因为核心计数高或在这种情况下核心计数不是2的幂。
更新:在AWS中使用不同vCPU计数的各种实例进行测试后,似乎出现了以下情况:
- 36 vCPUs(c4.8xlarge)。在普通TParallel调用之间必须等待几分钟(这使其在生产环境中无法使用)
- 32 vCPUs(c3.8xlarge)。在普通TParallel调用之间必须等待几分钟(这使其在生产环境中无法使用)
- 16 vCPUs(c3.4xlarge)。您必须等待次秒时间。如果负载低但响应时间仍然重要,则可以使用
- 8 vCPUs(c3.2xlarge)。它似乎正常工作
- 4 vCPUs(c3.xlarge)。它似乎正常工作
- 2 vCPUs(m3.large)。它似乎正常工作