Параллельные вычисления

Продемонстрируем параллельные вычисления в языке Ада на примере подсчета среднего квадратичного значения элементов массива. Задача: по данному массиву чисел A (1 .. N) найти среднее квадратичное, как S = Sqrt (A(1) ** 2 + A(2) ** 2 + ... + A(N) ** 2). Решение: будем выполнять подсчет суммы, используя несколько задач, работающих параллельно.

Настраиваемая функция позволяет нам при необходимости легко поменять тип обрабатываемых значений. Функция принимает, как параметры, вектор исходных значений и желаемую степень распараллеливания.

--  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;

Архив с тесктом этой программы доступен здесь. Эту программу любезно предоставил Дмитрий Анисимков.

----