Продемонстрируем параллельные вычисления в языке Ада на примере подсчета среднего квадратичного значения элементов массива. Задача: по данному массиву чисел A (1 .. N) найти среднее квадратичное, как S = √‾(A12 + A22 + ... + An2). Решение: будем выполнять подсчет суммы, используя несколько задач, работающих параллельно.
Настраиваемая функция позволяет нам при необходимости легко поменять тип обрабатываемых значений. Функция принимает, как параметры, вектор исходных значений и желаемую степень распараллеливания.
-- Sample of the parallel calculations in Ada. -- Distributed under the GNU General Public License -- Author: Dmitriy Anisimkov. generic type Number is digits <>; type Vector_Type is array (Positive range <>) of Number; function SSqrt (Vector : in Vector_Type; Tasks : in Positive) return Number; -- Vector is the numbers for calculation. -- Tasks is the number of parallel tasks for calculation. -- Number of tasks should not be more than number of processors for -- better performance.
Реализация функции состоит из подпрограммы подсчета суммы Square_Sum и задачного типа Calculator, который вычисляет сумму квадратов пренадлежащего ему участка вектора. Жизненный цикл объекта типа Calculator состоит из получения диапазона участка, вычисления суммы квадратов и возврата результата.
-- Sample of the parallel calculations. -- Distributed under the GNU General Public License -- Author: Dmitriy Anisimkov. with Ada.Numerics.Generic_Elementary_Functions; function SSqrt (Vector : in Vector_Type; Tasks : in Positive) return Number is package GEL is new Ada.Numerics.Generic_Elementary_Functions (Number); -- Generic instantiation for numeric functions. function Square_Sum (First, Last : in Positive) return Number; -- Calculates square sum from the First to the Last elements in the -- vector. task type Calculator is entry Start (First, Last : in Positive); -- First and Last is a subrange for this task square sum calculation. entry Stop (Result : out Number); -- Get the result after calculation. end; ---------------- -- Calculator -- ---------------- task body Calculator is First, Last : Positive; Sum : Number; begin accept Start (First, Last : Positive) do -- Copy parameters to the local variables for be able start -- calculation after rendezvous. Calculator.First := First; Calculator.Last := Last; end Start; -- Calculation. Sum := Square_Sum (First, Last); accept Stop (Result : out Number) do -- Return result to the calling task. Result := Sum; end Stop; end Calculator; ---------------- -- Square_Sum -- ---------------- function Square_Sum (First, Last : in Positive) return Number is Sum : Number := 0.0; begin for J in First .. Last loop Sum := Sum + Vector (J) ** 2; end loop; return Sum; end Square_Sum; Sub_Length : constant Positive := Vector'Length / Tasks; Calcs : array (1 .. Tasks - 1) of Calculator; -- Tasks - 1 is to rest some elements for the calculation in the main task. Index : Positive := Vector'First; Next : Positive; Sum : Number; Subsum : Number; begin -- Dispatch subvectors to the parallel tasks. for J in Calcs'Range loop Next := Index + Sub_Length; Calcs (J).Start (First => Index, Last => Next - 1); Index := Next; end loop; -- Calculate square sum of the rest elements in the main task. Sum := Square_Sum (Index, Vector'Last); -- Get results from the parallel tasks. for J in Calcs'Range loop Calcs (J).Stop (Subsum); Sum := Sum + Subsum; end loop; -- Calculate square root and return result. return GEL.Sqrt (Sum); end SSqrt;
Главный модуль настраивает функцию на тип Long_Float и вычисляет среднее квадратичное двухсот элементов вектора, используя разную степень распараллеливания.
with Ada.Text_IO; with SSqrt; procedure SSqrt_Test is subtype Number is Long_Float; type Vector_Type is array (Positive range <>) of Number; function Square_Sum_Sqrt is new SSqrt (Number, Vector_Type); Sample : Vector_Type (1 .. 200); Test_Sum : Number := 0.0; begin for J in Sample'Range loop Sample (J) := Number (J); Test_Sum := Test_Sum + Sample (J) ** 2; end loop; Ada.Text_IO.Put_Line (Number'Image (Square_Sum_Sqrt (Sample, 1)) & Number'Image (Square_Sum_Sqrt (Sample, 2)) & Number'Image (Square_Sum_Sqrt (Sample, 3)) & ASCII.LF & Number'Image (Square_Sum_Sqrt (Sample, 4) ** 2) & Number'Image (Test_Sum)); end SSqrt_Test;
Архив с тесктом этой программы доступен здесь. Эту программу любезно предоставил Дмитрий Анисимков.