47 lines
1.6 KiB
Python
47 lines
1.6 KiB
Python
# -*- coding: utf-8 -*-
|
|
"""Демо: π методом Монте-Карло в MPI (reduce).
|
|
|
|
Запуск из корня курса:
|
|
mpiexec -n 4 python lab08_mpi/examples/04_pi_reduce_mpi.py
|
|
|
|
Каждый ранг бросает свою порцию «точек» со СВОИМ зерном (rank-зависимым),
|
|
reduce(SUM) сворачивает попадания. Число рангов не меняет формулу —
|
|
меняется только распределение бросков.
|
|
"""
|
|
import random
|
|
import time
|
|
|
|
from mpi4py import MPI
|
|
|
|
comm = MPI.COMM_WORLD
|
|
rank = comm.Get_rank()
|
|
size = comm.Get_size()
|
|
|
|
N_PER_RANK = 500_000
|
|
|
|
|
|
def count_inside(n: int, seed: int) -> int:
|
|
rng = random.Random(seed) # зерно зависит от ранга
|
|
inside = 0
|
|
for _ in range(n):
|
|
x = rng.random()
|
|
y = rng.random()
|
|
if x * x + y * y <= 1.0:
|
|
inside += 1
|
|
return inside
|
|
|
|
|
|
t0 = time.perf_counter()
|
|
my_inside = count_inside(N_PER_RANK, seed=1000 + rank)
|
|
total_inside = comm.reduce(my_inside, op=MPI.SUM, root=0)
|
|
dt = time.perf_counter() - t0
|
|
|
|
if rank == 0:
|
|
total = N_PER_RANK * size
|
|
pi = 4.0 * total_inside / total
|
|
print(f"рангов: {size}, бросков на ранг: {N_PER_RANK:,} (всего {total:,})")
|
|
print(f"π ≈ {pi:.6f} (погрешность {abs(pi - 3.141592653589793):.6f})")
|
|
print(f"время: {dt:.2f} c")
|
|
if size == 1:
|
|
print("\nПопробуйте: mpiexec -n 4 python lab08_mpi/examples/04_pi_reduce_mpi.py")
|
|
print("Каждый ранг добавляет 500k бросков: точность растёт, время — нет.") |