Ответ 1
Я создал две тестовые программы на основе ваших, чтобы сравнить System.Threading
и OTL
. Я построил с обновлением XE7 1 и OTL r1397. Источник OTL, который я использовал, соответствует выпуску 3.04. Я построил с 32-битным компилятором Windows, используя варианты сборки выпуска.
Моя тестовая машина - это двойной Intel Xeon E5530 под управлением Windows 7 x64. Система имеет два четырехъядерных процессора. Всего 8 процессоров, но система говорит, что из-за гиперпотока существует 16. Опыт подсказывает мне, что гиперпоточность - это просто маркетинговый гуф, и я никогда не видел масштабирования на 8-м уровне.
Теперь для двух программ, которые почти идентичны.
System.Threading
program SystemThreadingTest;
{$APPTYPE CONSOLE}
uses
System.Diagnostics,
System.Threading;
const
maxItems = 5000;
DataSize = 100000;
procedure DoTest;
var
matches: integer;
i, j: integer;
sw: TStopWatch;
referenceStr: string;
begin
Randomize;
SetLength(referenceStr, DataSize);
for i := low(referenceStr) to high(referenceStr) do
referenceStr[i] := Chr(Ord('a') + Random(26));
// parallel
matches := 0;
sw := TStopWatch.StartNew;
TParallel.For(1, maxItems,
procedure(Value: integer)
var
index: integer;
found: integer;
begin
found := 0;
for index := low(referenceStr) to high(referenceStr) do
if (((Value mod 26) + Ord('a')) = Ord(referenceStr[index])) then
inc(found);
AtomicIncrement(matches, found);
end);
Writeln('Parallel matches: ', matches, ' in ', sw.ElapsedMilliseconds, 'ms');
// serial
matches := 0;
sw := TStopWatch.StartNew;
for i := 1 to maxItems do
for j := low(referenceStr) to high(referenceStr) do
if (((i mod 26) + Ord('a')) = Ord(referenceStr[j])) then
inc(matches);
Writeln('Serial matches: ', matches, ' in ', sw.ElapsedMilliseconds, 'ms');
end;
begin
while True do
DoTest;
end.
OTL
program OTLTest;
{$APPTYPE CONSOLE}
uses
Winapi.Windows,
Winapi.Messages,
System.Diagnostics,
OtlParallel;
const
maxItems = 5000;
DataSize = 100000;
procedure ProcessThreadMessages;
var
msg: TMsg;
begin
while PeekMessage(Msg, 0, 0, 0, PM_REMOVE) and (Msg.Message <> WM_QUIT) do begin
TranslateMessage(Msg);
DispatchMessage(Msg);
end;
end;
procedure DoTest;
var
matches: integer;
i, j: integer;
sw: TStopWatch;
referenceStr: string;
begin
Randomize;
SetLength(referenceStr, DataSize);
for i := low(referenceStr) to high(referenceStr) do
referenceStr[i] := Chr(Ord('a') + Random(26));
// parallel
matches := 0;
sw := TStopWatch.StartNew;
Parallel.For(1, maxItems).Execute(
procedure(Value: integer)
var
index: integer;
found: integer;
begin
found := 0;
for index := low(referenceStr) to high(referenceStr) do
if (((Value mod 26) + Ord('a')) = Ord(referenceStr[index])) then
inc(found);
AtomicIncrement(matches, found);
end);
Writeln('Parallel matches: ', matches, ' in ', sw.ElapsedMilliseconds, 'ms');
ProcessThreadMessages;
// serial
matches := 0;
sw := TStopWatch.StartNew;
for i := 1 to maxItems do
for j := low(referenceStr) to high(referenceStr) do
if (((i mod 26) + Ord('a')) = Ord(referenceStr[j])) then
inc(matches);
Writeln('Serial matches: ', matches, ' in ', sw.ElapsedMilliseconds, 'ms');
end;
begin
while True do
DoTest;
end.
И теперь вывод.
Выход System.Threading
Parallel matches: 19230817 in 374ms Serial matches: 19230817 in 2423ms Parallel matches: 19230698 in 374ms Serial matches: 19230698 in 2409ms Parallel matches: 19230556 in 368ms Serial matches: 19230556 in 2433ms Parallel matches: 19230635 in 2412ms Serial matches: 19230635 in 2430ms Parallel matches: 19230843 in 2441ms Serial matches: 19230843 in 2413ms Parallel matches: 19230905 in 2493ms Serial matches: 19230905 in 2423ms Parallel matches: 19231032 in 2430ms Serial matches: 19231032 in 2443ms Parallel matches: 19230669 in 2440ms Serial matches: 19230669 in 2473ms Parallel matches: 19230811 in 2404ms Serial matches: 19230811 in 2432ms ....
Выход OTL
Parallel matches: 19230667 in 422ms Serial matches: 19230667 in 2475ms Parallel matches: 19230663 in 335ms Serial matches: 19230663 in 2438ms Parallel matches: 19230889 in 395ms Serial matches: 19230889 in 2461ms Parallel matches: 19230874 in 391ms Serial matches: 19230874 in 2441ms Parallel matches: 19230617 in 385ms Serial matches: 19230617 in 2524ms Parallel matches: 19231021 in 368ms Serial matches: 19231021 in 2455ms Parallel matches: 19230904 in 357ms Serial matches: 19230904 in 2537ms Parallel matches: 19230568 in 373ms Serial matches: 19230568 in 2456ms Parallel matches: 19230758 in 333ms Serial matches: 19230758 in 2710ms Parallel matches: 19230580 in 371ms Serial matches: 19230580 in 2532ms Parallel matches: 19230534 in 336ms Serial matches: 19230534 in 2436ms Parallel matches: 19230879 in 368ms Serial matches: 19230879 in 2419ms Parallel matches: 19230651 in 409ms Serial matches: 19230651 in 2598ms Parallel matches: 19230461 in 357ms ....
Я оставил версию OTL долгое время, и шаблон никогда не менялся. Параллельная версия всегда была примерно в 7 раз быстрее, чем серийный.
Заключение
Код удивительно прост. Единственный разумный вывод, который можно сделать, заключается в том, что реализация System.Threading
является дефектной.
Были многочисленные отчеты об ошибках, относящиеся к новой библиотеке System.Threading
. Все признаки того, что его качество плохое. Embarcadero имеет долгую историю выпуска нестандартного библиотечного кода. Я думаю о TMonitor
, помощнике строки XE3, более ранних версиях System.IOUtils
, FireMonkey. Список можно продолжить.
Кажется очевидным, что качество - большая проблема с Embarcadero. Выпущен код, который достаточно четко не проверен адекватно, если вообще. Это особенно неприятно для библиотеки потоков, где ошибки могут лежать бездействующими и отображаться только в определенных конфигурациях аппаратного и программного обеспечения. Опыт от TMonitor
заставляет меня поверить, что Embarcadero не обладает достаточным опытом для создания высококачественного, правильного, потокового кода.
Мой совет заключается в том, что вы не должны использовать System.Threading
в своей текущей форме. До тех пор, пока не будет видно, что он обладает достаточным качеством и правильностью, его следует избегать. Я предлагаю вам использовать OTL.
EDIT: оригинальная версия OTL программы имела утечку памяти в реальном времени, которая произошла из-за уродливой детали реализации. Parallel.For создает задачи с модификатором .Unobserved. Это приводит к тому, что указанные задачи могут быть уничтожены только в том случае, если какое-либо внутреннее окно сообщения получает сообщение "прекращено действие". Это окно создается в том же потоке, что и в Parallel.For caller, т.е. В основном потоке в этом случае. Поскольку основной поток не обрабатывал сообщения, задачи никогда не уничтожались, а потребление памяти (плюс другие ресурсы) просто складывалось. Возможно, из-за этой программы повесили через некоторое время.