我已经为快速的 xoroshiro128plus 伪随机数生成器 写了一个最小实现,用 Fortran 替换内置的
我的问题是如何优化这个子程序以从编译器中获得最后一滴性能,即使只有 10% 的提升也会受到赞赏。这个子程序将在长时间模拟中的紧密循环中使用。我更感兴趣的是一次生成单个随机数,而不是一次生成大向量或 nD 数组。
下面是一个测试程序,让您了解我的子程序如何使用:
random_number
。这个实现非常快(比 random_number
快 4 倍),而且质量对于我的目的来说足够好,我不会在密码应用中使用它。我的问题是如何优化这个子程序以从编译器中获得最后一滴性能,即使只有 10% 的提升也会受到赞赏。这个子程序将在长时间模拟中的紧密循环中使用。我更感兴趣的是一次生成单个随机数,而不是一次生成大向量或 nD 数组。
下面是一个测试程序,让您了解我的子程序如何使用:
program test_xoroshiro128plus
implicit none
integer, parameter :: n = 10000
real*8 :: A(n,n)
integer :: i, j, t0, t1, count_rate, count_max
call system_clock(t0, count_rate, count_max)
do j = 1,n
do i = 1,n
call drand128(A(i,j))
end do
end do
! call drand128(A) ! works also with 2D
call system_clock(t1)
print *, "Time :", real(t1-t0)/count_rate
print *, "Mean :", sum(A)/size(A), char(10), A(1:2,1:3)
contains
impure elemental subroutine drand128(r)
real*8, intent(out) :: r
integer*8 :: s0 = 113, s1 = 19937
s1 = xor(s0,s1)
s0 = xor(xor(ior(ishft(s0,55), ishft(s0,-9)),s1), ishft(s1,14))
s1 = ior(ishft(s1,36), ishft(s1,-28))
r = ishft(s0+s1, -1) / 9223372036854775808.d0
end
end program
random_number
也是如此,除非您发出random_seed()
命令,例如(仅适用于ifort
),我也可以轻松地为我的生成器设置种子。我的观点主要是关于速度的,我可以使用随附MKL的PRNG,但它只适用于长向量,我需要一次一个rand。此外,我的drand128
与MKL的vdrnguniform
具有相同的速度,而无需包含大型模块或设置奇怪的参数。 - AboAmmartemp(i)
一样每次取出一个随机数,但这个策略在运行时间和内存使用方面都证明不够高效。当然,我是指MKL的vdrnguniform
,这是我尝试过的最快的方法。 - AboAmmarxoroshiro128+
的四分之一。 - AboAmmarrandom_seed
,则random_number
始终返回相同的序列。这是处理器相关的行为。不,Fortran的内置函数并不使用Mersenne Twister。每个Fortran供应商都使用他们认为最好的算法。gfortran很久以前使用了MT,但由于其质量差而将其删除。然后,gfortran使用了4个独立的KISS生成器(KISS指Marsaglia prng)。现在,gfortran使用Vigna的xorshift prng之一。 - Steve