Я пытаюсь использовать функции параллельного программирования Delphi XE7 Update 1.
Я создал простой цикл TParallel.For
, который в основном выполняет некоторые фиктивные операции, чтобы передать время.
Я запустил программу на 36 vCPU на экземпляре AWS (c4.8xlarge), чтобы попытаться понять, что может быть усиление параллельного программирования.
Когда я впервые запускаю программу и выполняю цикл TParallel.For
, я вижу значительный выигрыш (хотя admitelly намного меньше, чем я ожидал с 36 vCPU):
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 машинах vCPU, поэтому кажется, что этот эффект происходит только во второй раз, когда в программе вызывается 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(2 vCPU согласно AWS). В этом случае я всегда получаю небольшое улучшение, и я не получаю худшего результата при последующих вызовах TParallel.For
вскоре после.
Parallel matches: 23077054 in 2057ms
Single Threaded matches: 23077054 in 2900ms
Таким образом, кажется, что этот эффект возникает, когда доступно много ядер (36), что очень жалко, потому что вся точка параллельного программирования - извлечь выгоду из многих ядер. Я задаюсь вопросом, является ли это ошибкой библиотеки из-за большого количества ядер или того факта, что в этом случае подсчет ядра не равен 2.
UPDATE: после тестирования с различными экземплярами разных vCPU считается в AWS, это похоже на поведение:
- 36 vCPU (c4.8xlarge). Вам необходимо подождать минуты между последующими вызовами на валлильный TParallel call (это делает его непригодным для производство)
- 32 vCPU (c3.8xlarge). Вам необходимо подождать минуты между последующими вызовами на валлильный TParallel call (это делает его непригодным для производство)
- 16 vCPU (c3.4xlarge). Вы должны подождать второй раз. Его можно использовать, если нагрузка низкая, но время отклика все еще важно.
- 8 vCPU (c3.2xlarge). Кажется, что он работает нормально
- 4 vCPU (c3.xlarge). Кажется, что он работает нормально
- 2 vCPU (m3.large). Кажется, что он работает нормально