Два пункта - во-первых, пример написан на Фортране, но я думаю, что он подходит для любого языка; во-вторых, встроенные генераторы случайных чисел не являются действительно случайными, и существуют другие генераторы, но мы не заинтересованы в их использовании для того, что мы делаем.
Большинство дискуссий по случайным начальным числам подтверждают, что если программа не заполняет их во время выполнения, то начальное число создается во время компиляции. Таким образом, при каждом запуске программы генерируется одна и та же последовательность чисел, что не подходит для случайных чисел. Одним из способов преодоления этого является заполнение генератора случайных чисел системными часами.
Однако, при работе параллельно с MPI на многоядерной машине, системный тактовый подход для нас породил такие же проблемы. В то время как последовательности менялись от запуска к запуску, все процессоры получали одинаковые системные часы и, таким образом, одинаковое случайное начальное число и одинаковые последовательности.
Итак, рассмотрим следующий пример кода:
PROGRAM clock_test
IMPLICIT NONE
INCLUDE "mpif.h"
INTEGER :: ierr, rank, clock, i, n, method
INTEGER, DIMENSION(:), ALLOCATABLE :: seed
REAL(KIND=8) :: random
INTEGER, PARAMETER :: OLD_METHOD = 0, &
NEW_METHOD = 1
CALL MPI_INIT(ierr)
CALL MPI_COMM_RANK(MPI_COMM_WORLD, rank, ierr)
CALL RANDOM_SEED(SIZE=n)
ALLOCATE(seed(n))
DO method = 0, 1
SELECT CASE (method)
CASE (OLD_METHOD)
CALL SYSTEM_CLOCK(COUNT=clock)
seed = clock + 37 * (/ (i - 1, i = 1, n) /)
CALL RANDOM_SEED(put=seed)
CALL RANDOM_NUMBER(random)
WRITE(*,*) "OLD Rank, dev = ", rank, random
CASE (NEW_METHOD)
OPEN(89,FILE='/dev/urandom',ACCESS='stream',FORM='UNFORMATTED')
READ(89) seed
CLOSE(89)
CALL RANDOM_SEED(put=seed)
CALL RANDOM_NUMBER(random)
WRITE(*,*) "NEW Rank, dev = ", rank, random
END SELECT
CALL MPI_BARRIER(MPI_COMM_WORLD, ierr)
END DO
CALL MPI_FINALIZE(ierr)
END PROGRAM clock_test
Что при запуске на моей рабочей станции с 2 ядрами дает:
OLD Rank, dev = 0 0.330676306089146
OLD Rank, dev = 1 0.330676306089146
NEW Rank, dev = 0 0.531503215980609
NEW Rank, dev = 1 0.747413828750221
Итак, мы преодолели проблему с часами, прочитав вместо этого начальное число из /dev/urandom
. Таким образом, каждое ядро получает свое случайное число.
Какие существуют другие начальные подходы, которые будут работать в многоядерной системе MPI и при этом быть уникальными для каждого ядра, от запуска к запуску?