Параллельные вычисления в Delphi: правильное подсчет четных чисел с TParallel.For
Параллельные вычисления в Delphi могут быть реализованы с помощью компонентов, таких как TParallel.For. Однако, при использовании параллельных алгоритмов важно учитывать возможные проблемы, связанные с доступом к общему ресурсу и "ложным обменом данных" (false sharing).
В данной статье мы рассмотрим, как правильно использовать TParallel.For для подсчета четных чисел в одномерном массиве, избегая проблем, связанных с параллельными вычислениями.
Пример задачи
Дано простое задание: найти четные числа в одномерном массиве. В последовательном коде это выглядит следующим образом:
begin
odds := 0;
Ticks := TThread.GetTickCount;
for i := 0 to MaxArr-1 do
if ArrXY[i] mod 2 = 0 then
Inc(odds);
Ticks := TThread.GetTickCount - Ticks;
writeln('Serial: ' + Ticks.ToString + 'ms, odds: ' + odds.ToString);
end;
Кажется, что задача хорошо подходит для параллельной обработки, и можно использовать TParallel.For:
begin
odds := 0;
Ticks := TThread.GetTickCount;
TParallel.For(0, MaxArr-1, procedure(I:Integer)
begin
if ArrXY[i] mod 2 = 0 then
inc(odds);
end);
Ticks := TThread.GetTickCount - Ticks;
writeln('Parallel - false odds: ' + Ticks.ToString + 'ms, odds: ' + odds.ToString);
end;
Однако, при выполнении такой программы, мы сталкиваемся с двумя проблемами:
Неправильный подсчет четных чисел.
Увеличенное время выполнения по сравнению с последовательным вариантом.
Проблема доступа к общему ресурсу
Первая проблема связана с доступом к общему ресурсу odds. Для решения этой проблемы следует использовать TInterlocked.Increment вместо Inc.
Проблема "ложного обмена"
Вторая проблема связана с явлением "ложного обмена" (false sharing), когда потоки параллельно работают с данными, расположенными в одной кэш-линии памяти, что приводит к ненужным задержкам.
Решение проблемы "ложного обмена"
Для решения проблемы "ложного обмена" можно использовать локальные переменные для хранения промежуточных результатов, а затем суммировать их после завершения параллельных задач. Это можно реализовать, используя массив для хранения промежуточных результатов для каждого потока:
var
sums: array of Integer;
begin
SetLength(sums, MaxArr);
for I := 0 to MaxArr-1 do
sums[I] := 0;
Ticks := TThread.GetTickCount;
TParallel.For(0, MaxArr-1,
procedure(I:Integer)
begin
if ArrXY[i] mod 2 = 0 then
Inc(sums[I]);
end);
Ticks := TThread.GetTickCount - Ticks;
odds := 0;
for I := 0 to MaxArr-1 do
Inc(odds, sums[I]);
writeln('Parallel: ' + Ticks.ToString + 'ms, odds: ' + odds.ToString);
end;
Заключение
При использовании TParallel.For для подсчета четных чисел в массиве важно учитывать доступ к общему ресурсу и явление "ложного обмена". Использование локальных переменных для хранения промежуточных результатов и последующее их суммирование позволяет избежать этих проблем. Следуя этим рекомендациям, можно эффективно использовать параллельные вычисления в Delphi для решения различных задач.
Пример использования компонента `TParallel.For` в Delphi для подсчета четных чисел в массиве, с учетом избежания проблем параллельных вычислений, таких как некорректный доступ к общему ресурсу и "ложный обмен данных".
Комментарии и вопросы
Получайте свежие новости и обновления по Object Pascal, Delphi и Lazarus прямо в свой смартфон. Подпишитесь на наш Telegram-канал delphi_kansoftware и будьте в курсе последних тенденций в разработке под Linux, Windows, Android и iOS
Материалы статей собраны из открытых источников, владелец сайта не претендует на авторство. Там где авторство установить не удалось, материал подаётся без имени автора. В случае если Вы считаете, что Ваши права нарушены, пожалуйста, свяжитесь с владельцем сайта.