Попробовал задачку посложнее сделать, распределенное вычисление суммы массива (для утяжеления расчета перед суммированием применяется произвольная процедура). Блокирующий прием и передача также нормально работают. Получается примерно в три раза быстрее сделать расчет на 4-х ядрах.
Код:
dia@hp:~/oberon/bb-freenix/MPI$ time mpiexec.mpich2 -n 1 ./blackboxc
Process 0 / 1
Total: 655520136.8390013
real 1m26.015s
user 1m25.872s
sys 0m0.020s
dia@hp:~/oberon/bb-freenix/MPI$ time mpiexec.mpich2 -n 2 ./blackboxc
Process 0 / 2
Process 1 / 2
Process 1 sum is 327760068.4224058
Process 0 sum is 327760068.4224058
Total: 655520136.8448116
real 0m43.154s
user 1m26.192s
sys 0m0.008s
dia@hp:~/oberon/bb-freenix/MPI$ time mpiexec.mpich2 -n 3 ./blackboxc
Process 2 / 3
Process 0 / 3
Process 1 / 3
Process 2 sum is 218508460.3361039
Process 0 sum is 218505838.2555565
Process 1 sum is 218505838.2555565
Total: 655520136.847217
real 0m36.934s
user 1m50.220s
sys 0m0.280s
dia@hp:~/oberon/bb-freenix/MPI$ time mpiexec.mpich2 -n 4 ./blackboxc
Process 0 / 4
Process 1 / 4
Process 3 / 4
Process 2 / 4
Process 3 sum is 163880034.2117313
Process 1 sum is 163880034.2117313
Process 2 sum is 163880034.2117313
Process 0 sum is 163880034.2117313
Total: 655520136.8469253
real 0m30.281s
user 1m54.208s
sys 0m0.096s
Вот так выглядит код модуля. К сообщению приложил сборку, которую использовал, и наброски заголовочных файлов к MPI.
Код:
MODULE Init;
IMPORT SYSTEM, Kernel, Log, Math, LinLog, Strings, ObxRandom, Mpi := MpiMpi;
VAR
res, myrank, size: INTEGER;
sizeStr, rankStr: ARRAY 8 OF CHAR;
status: Mpi.MPI_Status;
PROCEDURE F (a: REAL): REAL;
VAR i, j: INTEGER; res: REAL;
BEGIN
res := 0;
FOR i := 0 TO 1000 DO
res := res + Math.Sin(a) + Math.Cos(a * 0.9) / (Math.Sin(a * 0.9) + Math.Cos(a))
END;
RETURN res
END F;
PROCEDURE SumSin (a: ARRAY OF REAL): REAL;
CONST
bal = 4; (* Балланс, количество процессов, до которого нулевой процесс участвует в расчетах (всегда > 1) *)
VAR
i, p, bite, rest, shift: INTEGER; sum, sum_ext: REAL; sumStr: ARRAY 64 OF CHAR;
BEGIN
(* Проверяем есть ли необходимость параллелить процесс суммирования *)
IF (size > 1) & (LEN(a) > 1000) THEN
(* Разбиваем массив в зависимости от того, участвует нулевой процесс в расчетах или нет *)
IF size <= bal THEN
shift := 0;
bite := LEN(a) DIV size;
rest := bite + LEN(a) MOD size
ELSE
shift := -1;
bite := LEN(a) DIV (size - 1);
rest := bite + LEN(a) MOD (size - 1)
END;
IF myrank = 0 THEN
(* Отправляем данные остальным процессам *)
IF size > 2 THEN
FOR p := 1 TO size - 2 DO
res := Mpi.MPI_Send(SYSTEM.ADR(a[bite * (p + shift)]), bite, Mpi.MPI_DOUBLE, p, 1, Mpi.MPI_COMM_WORLD);
END;
res := Mpi.MPI_Send(SYSTEM.ADR(a[LEN(a) - rest]), rest, Mpi.MPI_DOUBLE, size - 1, 1, Mpi.MPI_COMM_WORLD)
ELSE
res := Mpi.MPI_Send(SYSTEM.ADR(a[bite]), rest, Mpi.MPI_DOUBLE, 1, 1, Mpi.MPI_COMM_WORLD);
END;
sum := 0;
IF size <= bal THEN
FOR i:=0 TO bite - 1 DO
sum := sum + F(a[i])
END;
Strings.RealToString(sum, sumStr);
Log.String("Process " + rankStr + " sum is " + sumStr + 0AX);
END;
(* Собираем результаты расчетов *)
FOR p := 1 TO size - 1 DO
res := Mpi.MPI_Recv(SYSTEM.ADR(sum_ext), 1, Mpi.MPI_DOUBLE, p, 2, Mpi.MPI_COMM_WORLD, SYSTEM.ADR(status));
sum := sum + sum_ext
END
ELSIF myrank = size - 1 THEN
(* Вычисления последнего процесса, в случае двух - второго *)
res := Mpi.MPI_Recv(SYSTEM.ADR(a), rest, Mpi.MPI_DOUBLE, 0, 1, Mpi.MPI_COMM_WORLD, SYSTEM.ADR(status));
sum := 0;
FOR i:=0 TO rest - 1 DO
sum := sum + F(a[i])
END;
Strings.RealToString(sum, sumStr);
Log.String("Process " + rankStr + " sum is " + sumStr + 0AX);
res := Mpi.MPI_Send(SYSTEM.ADR(sum), 1, Mpi.MPI_DOUBLE, 0, 2, Mpi.MPI_COMM_WORLD)
ELSE
(* Вычисления остальных процессов *)
res := Mpi.MPI_Recv(SYSTEM.ADR(a), bite, Mpi.MPI_DOUBLE, 0, 1, Mpi.MPI_COMM_WORLD, SYSTEM.ADR(status));
sum := 0;
FOR i:=0 TO bite - 1 DO
sum := sum + F(a[i])
END;
Strings.RealToString(sum, sumStr);
Log.String("Process " + rankStr + " sum is " + sumStr + 0AX);
res := Mpi.MPI_Send(SYSTEM.ADR(sum), 1, Mpi.MPI_DOUBLE, 0, 2, Mpi.MPI_COMM_WORLD)
END
ELSE
(* Суммируем без использования распределенных вычислений *)
sum := 0;
FOR i:=0 TO LEN(a) - 1 DO
sum := sum + F(a[i])
END
END;
RETURN sum
END SumSin;
PROCEDURE Init*;
VAR a: ARRAY 10000 OF INTEGER; b: ARRAY 500000 OF REAL; i, sum: INTEGER; sum2: REAL; sumStr: ARRAY 64 OF CHAR;
BEGIN
LinLog.Open;
res := Mpi.MPI_Init(SYSTEM.ADR(Kernel.bootInfo.argc), SYSTEM.ADR(Kernel.bootInfo.argv));
res := Mpi.MPI_Comm_size(Mpi.MPI_COMM_WORLD, SYSTEM.ADR(size));
res := Mpi.MPI_Comm_rank(Mpi.MPI_COMM_WORLD, SYSTEM.ADR(myrank));
Strings.IntToString(size, sizeStr);
Strings.IntToString(myrank, rankStr);
Log.String("Process " + rankStr + " / " + sizeStr + 0AX);
IF myrank = 0 THEN
FOR i :=0 TO LEN(b) - 1 DO
b[i] := 0.987981723;
END;
END;
sum2 := SumSin(b);
IF myrank = 0 THEN
Strings.RealToString(sum2, sumStr);
Log.String("Total: " + sumStr + 0AX);
END;
res := Mpi.MPI_Finalize()
END Init;
BEGIN
Init;
Kernel.Quit(0)
END Init.