|
!---------------------------------------------------------------------- |
|
! Standalone benchmark: Original vs Clean vs Inlined vs Intrinsic vs Ziggurat |
|
! |
|
! Build (try each and compare): |
|
! gfortran -O2 -fdollar-ok -o ran_gauss_bench ran_gauss_bench.f90 |
|
! gfortran -O3 -march=native -fdollar-ok -o ran_gauss_bench ran_gauss_bench.f90 |
|
! gfortran -O3 -march=native -ffast-math -fdollar-ok -o ran_gauss_bench ran_gauss_bench.f90 |
|
! |
|
! Run: |
|
! ./ran_gauss_bench |
|
! |
|
! This benchmarks the radiation hot path: pseudo-random, exact-gaussian, |
|
! no sigma_cut, 6-element vector (matching radiation_mod usage). |
|
!---------------------------------------------------------------------- |
|
|
|
module rng_bench_mod |
|
|
|
implicit none |
|
|
|
integer, parameter :: rp = selected_real_kind(15, 307) |
|
integer, parameter :: sp = selected_real_kind(6, 37) |
|
integer, parameter :: kr4b = selected_int_kind(9) |
|
|
|
integer(kr4b), parameter :: im_nr_ran = 2147483647 |
|
|
|
integer, parameter :: pseudo_random$ = 1 |
|
integer, parameter :: exact_gaussian$ = 4 |
|
|
|
type random_state_struct |
|
integer(kr4b) :: ix = -1, iy = -1 |
|
logical :: number_stored = .false. |
|
real(rp) :: h_saved = 0 |
|
integer :: engine = pseudo_random$ |
|
integer :: seed = 0 |
|
real(sp) :: am = 0 |
|
integer :: gauss_converter = exact_gaussian$ |
|
real(rp) :: gauss_sigma_cut = -1 |
|
end type |
|
|
|
type (random_state_struct), target, save :: ran_state_save |
|
|
|
contains |
|
|
|
!---------------------------------------------------------------------- |
|
! pointer_to_ran_state: resolve state pointer once |
|
!---------------------------------------------------------------------- |
|
function pointer_to_ran_state(ran_state) result (ran_state_ptr) |
|
type (random_state_struct), optional, target :: ran_state |
|
type (random_state_struct), pointer :: ran_state_ptr |
|
if (present(ran_state)) then |
|
ran_state_ptr => ran_state |
|
else |
|
ran_state_ptr => ran_state_save |
|
endif |
|
end function |
|
|
|
!---------------------------------------------------------------------- |
|
! ran_seed_put: initialize RNG state |
|
!---------------------------------------------------------------------- |
|
subroutine ran_seed_put(seed) |
|
integer, intent(in) :: seed |
|
type (random_state_struct), pointer :: r_state |
|
r_state => ran_state_save |
|
r_state%am = nearest(1.0, -1.0) / im_nr_ran |
|
if (seed == 0) then |
|
r_state%seed = 12345 |
|
else |
|
r_state%seed = seed |
|
endif |
|
r_state%iy = ior(ieor(888889999, abs(r_state%seed)), 1) |
|
r_state%ix = ieor(777755555, abs(r_state%seed)) |
|
r_state%number_stored = .false. |
|
end subroutine |
|
|
|
!---------------------------------------------------------------------- |
|
! ran_uniform_scalar: Marsaglia xorshift + Park-Miller |
|
!---------------------------------------------------------------------- |
|
subroutine ran_uniform_scalar(harvest, ran_state) |
|
type (random_state_struct), optional, target :: ran_state |
|
type (random_state_struct), pointer :: r_state |
|
real(rp), intent(out) :: harvest |
|
integer(kr4b) :: k |
|
integer(kr4b), parameter :: ia = 16807, iq = 127773, ir = 2836 |
|
|
|
r_state => pointer_to_ran_state(ran_state) |
|
if (r_state%iy < 0) call ran_seed_put(r_state%seed) |
|
|
|
r_state%ix = ieor(r_state%ix, ishft(r_state%ix, 13)) |
|
r_state%ix = ieor(r_state%ix, ishft(r_state%ix, -17)) |
|
r_state%ix = ieor(r_state%ix, ishft(r_state%ix, 5)) |
|
k = r_state%iy / iq |
|
r_state%iy = ia * (r_state%iy - k*iq) - ir * k |
|
if (r_state%iy < 0) r_state%iy = r_state%iy + im_nr_ran |
|
harvest = r_state%am * ior(iand(im_nr_ran, ieor(r_state%ix, r_state%iy)), 1) |
|
end subroutine |
|
|
|
!---------------------------------------------------------------------- |
|
! ran_uniform_vector |
|
!---------------------------------------------------------------------- |
|
subroutine ran_uniform_vector(harvest, ran_state) |
|
type (random_state_struct), optional, target :: ran_state |
|
real(rp), intent(out) :: harvest(:) |
|
integer :: i |
|
do i = 1, size(harvest) |
|
call ran_uniform_scalar(harvest(i), ran_state) |
|
enddo |
|
end subroutine |
|
|
|
! ===================================================================== |
|
! ORIGINAL versions (upstream code, no modifications) |
|
! ===================================================================== |
|
|
|
subroutine orig_gauss_scalar(harvest, ran_state, sigma_cut) |
|
type (random_state_struct), optional, target :: ran_state |
|
type (random_state_struct), pointer :: r_state |
|
real(rp), intent(out) :: harvest |
|
real(rp), optional :: sigma_cut |
|
real(rp) :: a(2), v1, v2, r, sig_cut |
|
|
|
r_state => pointer_to_ran_state(ran_state) |
|
|
|
sig_cut = 1000 |
|
if (r_state%gauss_sigma_cut > 0) sig_cut = r_state%gauss_sigma_cut |
|
if (present(sigma_cut)) then |
|
if (sigma_cut > 0) sig_cut = sigma_cut |
|
endif |
|
|
|
do |
|
if (r_state%number_stored) then |
|
r_state%number_stored = .false. |
|
harvest = r_state%h_saved |
|
if (sig_cut <= 0 .or. abs(harvest) < sig_cut) exit |
|
endif |
|
|
|
do |
|
call ran_uniform_vector(a, ran_state) |
|
v1 = 2*a(1) - 1 |
|
v2 = 2*a(2) - 1 |
|
r = v1**2 + v2**2 |
|
if (r > 0 .and. r < 1) exit |
|
enddo |
|
|
|
r = sqrt(-2*log(r)/r) |
|
r_state%h_saved = v2 * r |
|
r_state%number_stored = .true. |
|
harvest = v1 * r |
|
if (sig_cut <= 0 .or. abs(harvest) < sig_cut) exit |
|
enddo |
|
end subroutine |
|
|
|
subroutine orig_gauss_vector(harvest, ran_state, sigma_cut) |
|
type (random_state_struct), optional, target :: ran_state |
|
real(rp), optional :: sigma_cut |
|
real(rp), intent(out) :: harvest(:) |
|
integer :: i |
|
do i = 1, size(harvest) |
|
call orig_gauss_scalar(harvest(i), ran_state, sigma_cut) |
|
enddo |
|
end subroutine |
|
|
|
! ===================================================================== |
|
! CLEAN versions (structural opts only, no inlining) |
|
! ===================================================================== |
|
|
|
subroutine clean_gauss_scalar(harvest, ran_state, sigma_cut) |
|
type (random_state_struct), optional, target :: ran_state |
|
type (random_state_struct), pointer :: r_state |
|
real(rp), intent(out) :: harvest |
|
real(rp), optional :: sigma_cut |
|
real(rp) :: a(2), v1, v2, r, sig_cut |
|
|
|
r_state => pointer_to_ran_state(ran_state) |
|
|
|
sig_cut = 1000 |
|
if (r_state%gauss_sigma_cut > 0) sig_cut = r_state%gauss_sigma_cut |
|
if (present(sigma_cut)) then |
|
if (sigma_cut > 0) sig_cut = sigma_cut |
|
endif |
|
|
|
do |
|
if (r_state%number_stored) then |
|
r_state%number_stored = .false. |
|
harvest = r_state%h_saved |
|
if (sig_cut <= 0 .or. abs(harvest) < sig_cut) exit |
|
endif |
|
|
|
do |
|
call ran_uniform_vector(a, ran_state) |
|
v1 = 2*a(1) - 1 |
|
v2 = 2*a(2) - 1 |
|
r = v1**2 + v2**2 |
|
if (r > 0 .and. r < 1) exit |
|
enddo |
|
|
|
r = sqrt(-2*log(r)/r) |
|
r_state%h_saved = v2 * r |
|
r_state%number_stored = .true. |
|
harvest = v1 * r |
|
if (sig_cut <= 0 .or. abs(harvest) < sig_cut) exit |
|
enddo |
|
end subroutine |
|
|
|
subroutine clean_gauss_vector(harvest, ran_state, sigma_cut) |
|
type (random_state_struct), optional, target :: ran_state |
|
type (random_state_struct), pointer :: r_state |
|
real(rp), optional :: sigma_cut |
|
real(rp), intent(out) :: harvest(:) |
|
real(rp) :: a(2), v1, v2, r, sig_cut |
|
integer :: i, n |
|
|
|
n = size(harvest) |
|
r_state => pointer_to_ran_state(ran_state) |
|
|
|
sig_cut = 1000 |
|
if (r_state%gauss_sigma_cut > 0) sig_cut = r_state%gauss_sigma_cut |
|
if (present(sigma_cut)) then |
|
if (sigma_cut > 0) sig_cut = sigma_cut |
|
endif |
|
|
|
! Sigma-cut active: fall back to scalar |
|
if (sig_cut < 10) then |
|
do i = 1, n |
|
call clean_gauss_scalar(harvest(i), ran_state, sigma_cut) |
|
enddo |
|
return |
|
endif |
|
|
|
i = 1 |
|
|
|
! Use stored value from previous call |
|
if (r_state%number_stored) then |
|
r_state%number_stored = .false. |
|
harvest(i) = r_state%h_saved |
|
i = i + 1 |
|
endif |
|
|
|
! Batch Box-Muller pairs |
|
do while (i <= n) |
|
do |
|
call ran_uniform_vector(a, ran_state) |
|
v1 = 2*a(1) - 1 |
|
v2 = 2*a(2) - 1 |
|
r = v1**2 + v2**2 |
|
if (r > 0 .and. r < 1) exit |
|
enddo |
|
|
|
r = sqrt(-2*log(r)/r) |
|
harvest(i) = v1 * r |
|
i = i + 1 |
|
|
|
if (i <= n) then |
|
harvest(i) = v2 * r |
|
i = i + 1 |
|
else |
|
r_state%h_saved = v2 * r |
|
r_state%number_stored = .true. |
|
endif |
|
enddo |
|
end subroutine |
|
|
|
! ===================================================================== |
|
! INLINED versions (inlined RNG + local var register promotion) |
|
! ===================================================================== |
|
|
|
subroutine inlined_gauss_scalar(harvest, ran_state, sigma_cut) |
|
type (random_state_struct), optional, target :: ran_state |
|
type (random_state_struct), pointer :: r_state |
|
real(rp), intent(out) :: harvest |
|
real(rp), optional :: sigma_cut |
|
real(rp) :: v1, v2, r, sig_cut, u1, u2 |
|
|
|
integer(kr4b) :: k, l_ix, l_iy |
|
integer(kr4b), parameter :: ia = 16807, iq = 127773, ir = 2836 |
|
real(sp) :: l_am |
|
|
|
r_state => pointer_to_ran_state(ran_state) |
|
|
|
sig_cut = 1000 |
|
if (r_state%gauss_sigma_cut > 0) sig_cut = r_state%gauss_sigma_cut |
|
if (present(sigma_cut)) then |
|
if (sigma_cut > 0) sig_cut = sigma_cut |
|
endif |
|
|
|
if (r_state%iy < 0) call ran_seed_put(r_state%seed) |
|
|
|
l_ix = r_state%ix |
|
l_iy = r_state%iy |
|
l_am = r_state%am |
|
|
|
do |
|
if (r_state%number_stored) then |
|
r_state%number_stored = .false. |
|
harvest = r_state%h_saved |
|
if (sig_cut <= 0 .or. abs(harvest) < sig_cut) exit |
|
endif |
|
|
|
do |
|
l_ix = ieor(l_ix, ishft(l_ix, 13)) |
|
l_ix = ieor(l_ix, ishft(l_ix, -17)) |
|
l_ix = ieor(l_ix, ishft(l_ix, 5)) |
|
k = l_iy/iq |
|
l_iy = ia*(l_iy - k*iq) - ir * k |
|
if (l_iy < 0) l_iy = l_iy + im_nr_ran |
|
u1 = l_am * ior(iand(im_nr_ran, ieor(l_ix, l_iy)), 1) |
|
|
|
l_ix = ieor(l_ix, ishft(l_ix, 13)) |
|
l_ix = ieor(l_ix, ishft(l_ix, -17)) |
|
l_ix = ieor(l_ix, ishft(l_ix, 5)) |
|
k = l_iy/iq |
|
l_iy = ia*(l_iy - k*iq) - ir * k |
|
if (l_iy < 0) l_iy = l_iy + im_nr_ran |
|
u2 = l_am * ior(iand(im_nr_ran, ieor(l_ix, l_iy)), 1) |
|
|
|
v1 = 2*u1 - 1 |
|
v2 = 2*u2 - 1 |
|
r = v1**2 + v2**2 |
|
if (r > 0 .and. r < 1) exit |
|
enddo |
|
|
|
r = sqrt(-2*log(r)/r) |
|
r_state%h_saved = v2 * r |
|
r_state%number_stored = .true. |
|
harvest = v1 * r |
|
if (sig_cut <= 0 .or. abs(harvest) < sig_cut) exit |
|
enddo |
|
|
|
r_state%ix = l_ix |
|
r_state%iy = l_iy |
|
end subroutine |
|
|
|
subroutine inlined_gauss_vector(harvest, ran_state, sigma_cut) |
|
type (random_state_struct), optional, target :: ran_state |
|
type (random_state_struct), pointer :: r_state |
|
real(rp), optional :: sigma_cut |
|
real(rp), intent(out) :: harvest(:) |
|
real(rp) :: v1, v2, r, sig_cut, u1, u2 |
|
real(sp) :: l_am |
|
integer :: i, n |
|
|
|
integer(kr4b) :: k, l_ix, l_iy |
|
integer(kr4b), parameter :: ia = 16807, iq = 127773, ir = 2836 |
|
|
|
n = size(harvest) |
|
r_state => pointer_to_ran_state(ran_state) |
|
|
|
if (r_state%iy < 0) call ran_seed_put(r_state%seed) |
|
|
|
sig_cut = 1000 |
|
if (r_state%gauss_sigma_cut > 0) sig_cut = r_state%gauss_sigma_cut |
|
if (present(sigma_cut)) then |
|
if (sigma_cut > 0) sig_cut = sigma_cut |
|
endif |
|
|
|
! Sigma-cut active: fall back to scalar |
|
if (sig_cut < 10) then |
|
do i = 1, n |
|
call inlined_gauss_scalar(harvest(i), ran_state, sigma_cut) |
|
enddo |
|
return |
|
endif |
|
|
|
l_ix = r_state%ix |
|
l_iy = r_state%iy |
|
l_am = r_state%am |
|
|
|
i = 1 |
|
|
|
! Use stored value from previous call |
|
if (r_state%number_stored) then |
|
r_state%number_stored = .false. |
|
harvest(i) = r_state%h_saved |
|
i = i + 1 |
|
endif |
|
|
|
! Fast loop with inlined RNG |
|
do while (i <= n) |
|
do |
|
l_ix = ieor(l_ix, ishft(l_ix, 13)) |
|
l_ix = ieor(l_ix, ishft(l_ix, -17)) |
|
l_ix = ieor(l_ix, ishft(l_ix, 5)) |
|
k = l_iy/iq |
|
l_iy = ia*(l_iy - k*iq) - ir * k |
|
if (l_iy < 0) l_iy = l_iy + im_nr_ran |
|
u1 = l_am * ior(iand(im_nr_ran, ieor(l_ix, l_iy)), 1) |
|
|
|
l_ix = ieor(l_ix, ishft(l_ix, 13)) |
|
l_ix = ieor(l_ix, ishft(l_ix, -17)) |
|
l_ix = ieor(l_ix, ishft(l_ix, 5)) |
|
k = l_iy/iq |
|
l_iy = ia*(l_iy - k*iq) - ir * k |
|
if (l_iy < 0) l_iy = l_iy + im_nr_ran |
|
u2 = l_am * ior(iand(im_nr_ran, ieor(l_ix, l_iy)), 1) |
|
|
|
v1 = 2*u1 - 1 |
|
v2 = 2*u2 - 1 |
|
r = v1**2 + v2**2 |
|
if (r > 0 .and. r < 1) exit |
|
enddo |
|
|
|
r = sqrt(-2*log(r)/r) |
|
harvest(i) = v1 * r |
|
i = i + 1 |
|
|
|
if (i <= n) then |
|
harvest(i) = v2 * r |
|
i = i + 1 |
|
else |
|
r_state%h_saved = v2 * r |
|
r_state%number_stored = .true. |
|
endif |
|
enddo |
|
|
|
r_state%ix = l_ix |
|
r_state%iy = l_iy |
|
end subroutine |
|
|
|
! ===================================================================== |
|
! INTRINSIC versions (Fortran random_number + Box-Muller) |
|
! Uses gfortran's built-in xoshiro256** PRNG |
|
! NOTE: Not drop-in compatible with bmad (different state management) |
|
! ===================================================================== |
|
|
|
subroutine intrinsic_gauss_vector(harvest) |
|
real(rp), intent(out) :: harvest(:) |
|
real(rp) :: u(2), v1, v2, r |
|
integer :: i, n |
|
|
|
n = size(harvest) |
|
i = 1 |
|
|
|
do while (i <= n) |
|
do |
|
call random_number(u) |
|
v1 = 2*u(1) - 1 |
|
v2 = 2*u(2) - 1 |
|
r = v1**2 + v2**2 |
|
if (r > 0 .and. r < 1) exit |
|
enddo |
|
|
|
r = sqrt(-2*log(r)/r) |
|
harvest(i) = v1 * r |
|
i = i + 1 |
|
|
|
if (i <= n) then |
|
harvest(i) = v2 * r |
|
i = i + 1 |
|
endif |
|
enddo |
|
end subroutine |
|
|
|
! ===================================================================== |
|
! ZIGGURAT Gaussian generator (Marsaglia & Tsang, 2000) |
|
! Uses the inlined Marsaglia+Park-Miller uniform RNG for fair comparison |
|
! ~2% rejection rate vs ~21.5% for Box-Muller rejection |
|
! ===================================================================== |
|
|
|
subroutine ziggurat_gauss_vector(harvest, ran_state) |
|
type (random_state_struct), optional, target :: ran_state |
|
type (random_state_struct), pointer :: r_state |
|
real(rp), intent(out) :: harvest(:) |
|
real(rp) :: x, y, u0, f0, f1 |
|
real(sp) :: l_am |
|
integer :: i, n, j |
|
|
|
integer(kr4b) :: k, l_ix, l_iy, iz |
|
integer(kr4b), parameter :: ia = 16807, iq = 127773, ir = 2836 |
|
|
|
! Ziggurat tables (128 layers) |
|
integer, parameter :: nzig = 128 |
|
real(rp), save :: xtab(0:nzig), ytab(0:nzig), ktab(0:nzig) |
|
logical, save :: zig_init = .false. |
|
|
|
real(rp), parameter :: zig_r = 3.442619855899_rp ! tail start |
|
real(rp), parameter :: zig_v = 9.91256303526217e-3_rp ! area of each layer |
|
|
|
n = size(harvest) |
|
r_state => pointer_to_ran_state(ran_state) |
|
if (r_state%iy < 0) call ran_seed_put(r_state%seed) |
|
|
|
! One-time table initialization |
|
if (.not. zig_init) then |
|
xtab(nzig) = zig_v / exp(-0.5_rp * zig_r**2) |
|
xtab(nzig-1) = zig_r |
|
ytab(nzig) = exp(-0.5_rp * zig_r**2) |
|
|
|
do j = nzig-2, 1, -1 |
|
xtab(j) = sqrt(-2.0_rp * log(zig_v / xtab(j+1) + exp(-0.5_rp * xtab(j+1)**2))) |
|
ytab(j+1) = exp(-0.5_rp * xtab(j+1)**2) |
|
enddo |
|
xtab(0) = 0.0_rp |
|
ytab(0) = 1.0_rp |
|
ytab(1) = exp(-0.5_rp * xtab(1)**2) |
|
|
|
do j = 0, nzig-1 |
|
ktab(j) = xtab(j) / xtab(j+1) |
|
enddo |
|
ktab(nzig) = 0.0_rp |
|
zig_init = .true. |
|
endif |
|
|
|
l_ix = r_state%ix |
|
l_iy = r_state%iy |
|
l_am = r_state%am |
|
|
|
do i = 1, n |
|
sample: do |
|
! Generate uniform random integer and float |
|
l_ix = ieor(l_ix, ishft(l_ix, 13)) |
|
l_ix = ieor(l_ix, ishft(l_ix, -17)) |
|
l_ix = ieor(l_ix, ishft(l_ix, 5)) |
|
k = l_iy/iq |
|
l_iy = ia*(l_iy - k*iq) - ir * k |
|
if (l_iy < 0) l_iy = l_iy + im_nr_ran |
|
iz = iand(ieor(l_ix, l_iy), 127) ! layer index 0..127 |
|
u0 = l_am * ior(iand(im_nr_ran, ieor(l_ix, l_iy)), 1) |
|
|
|
! Map u0 to [-1, 1) for sign |
|
x = (2*u0 - 1) * xtab(iz+1) |
|
|
|
! Fast acceptance: |x| < x_{iz} |
|
if (abs(x) < xtab(iz)) then |
|
harvest(i) = x |
|
exit sample |
|
endif |
|
|
|
if (iz == 0) then |
|
! Tail sampling using -log(U)/r |
|
do |
|
l_ix = ieor(l_ix, ishft(l_ix, 13)) |
|
l_ix = ieor(l_ix, ishft(l_ix, -17)) |
|
l_ix = ieor(l_ix, ishft(l_ix, 5)) |
|
k = l_iy/iq |
|
l_iy = ia*(l_iy - k*iq) - ir * k |
|
if (l_iy < 0) l_iy = l_iy + im_nr_ran |
|
f0 = l_am * ior(iand(im_nr_ran, ieor(l_ix, l_iy)), 1) |
|
|
|
l_ix = ieor(l_ix, ishft(l_ix, 13)) |
|
l_ix = ieor(l_ix, ishft(l_ix, -17)) |
|
l_ix = ieor(l_ix, ishft(l_ix, 5)) |
|
k = l_iy/iq |
|
l_iy = ia*(l_iy - k*iq) - ir * k |
|
if (l_iy < 0) l_iy = l_iy + im_nr_ran |
|
f1 = l_am * ior(iand(im_nr_ran, ieor(l_ix, l_iy)), 1) |
|
|
|
x = -log(max(f0, 1.0e-30_rp)) / zig_r |
|
y = -log(max(f1, 1.0e-30_rp)) |
|
if (2*y > x**2) exit |
|
enddo |
|
if (u0 < 0.5_rp) then |
|
harvest(i) = -(x + zig_r) |
|
else |
|
harvest(i) = x + zig_r |
|
endif |
|
exit sample |
|
endif |
|
|
|
! Wedge sampling |
|
l_ix = ieor(l_ix, ishft(l_ix, 13)) |
|
l_ix = ieor(l_ix, ishft(l_ix, -17)) |
|
l_ix = ieor(l_ix, ishft(l_ix, 5)) |
|
k = l_iy/iq |
|
l_iy = ia*(l_iy - k*iq) - ir * k |
|
if (l_iy < 0) l_iy = l_iy + im_nr_ran |
|
y = l_am * ior(iand(im_nr_ran, ieor(l_ix, l_iy)), 1) |
|
|
|
if (ytab(iz) + y * (ytab(iz+1) - ytab(iz)) < exp(-0.5_rp * x**2)) then |
|
harvest(i) = x |
|
exit sample |
|
endif |
|
enddo sample |
|
enddo |
|
|
|
r_state%ix = l_ix |
|
r_state%iy = l_iy |
|
end subroutine |
|
|
|
end module rng_bench_mod |
|
|
|
! ===================================================================== |
|
! MAIN BENCHMARK PROGRAM |
|
! ===================================================================== |
|
|
|
program ran_gauss_bench |
|
use rng_bench_mod |
|
implicit none |
|
|
|
integer, parameter :: n_calls = 4000000 ! ~400 elements * 10k particles |
|
integer, parameter :: vec_size = 6 ! radiation_mod uses 6-element vectors |
|
integer, parameter :: n_warmup = 100000 |
|
real(rp) :: harvest(vec_size) |
|
real(rp) :: t_start, t_end, dummy |
|
integer :: i, seed_val |
|
|
|
seed_val = 12345 |
|
|
|
write(*,'(A)') '======================================================' |
|
write(*,'(A)') ' ran_gauss standalone benchmark' |
|
write(*,'(A,I0,A,I0)') ' n_calls = ', n_calls, ' vec_size = ', vec_size |
|
write(*,'(A)') '======================================================' |
|
write(*,*) |
|
|
|
! --- Correctness check: all three must produce identical sequences --- |
|
write(*,'(A)') 'Correctness check (first 3 vectors from seed 12345):' |
|
write(*,*) |
|
|
|
call ran_seed_put(seed_val) |
|
write(*,'(A)') ' Original:' |
|
do i = 1, 3 |
|
call orig_gauss_vector(harvest, sigma_cut=-1.0_rp) |
|
write(*,'(A,6F12.8)') ' ', harvest |
|
enddo |
|
|
|
call ran_seed_put(seed_val) |
|
write(*,'(A)') ' Clean:' |
|
do i = 1, 3 |
|
call clean_gauss_vector(harvest, sigma_cut=-1.0_rp) |
|
write(*,'(A,6F12.8)') ' ', harvest |
|
enddo |
|
|
|
call ran_seed_put(seed_val) |
|
write(*,'(A)') ' Inlined:' |
|
do i = 1, 3 |
|
call inlined_gauss_vector(harvest, sigma_cut=-1.0_rp) |
|
write(*,'(A,6F12.8)') ' ', harvest |
|
enddo |
|
|
|
write(*,*) |
|
write(*,'(A)') '------------------------------------------------------' |
|
write(*,'(A)') ' Benchmarks (vector path, vec_size=6)' |
|
write(*,'(A)') '------------------------------------------------------' |
|
write(*,*) |
|
|
|
! --- WARMUP --- |
|
call ran_seed_put(seed_val) |
|
do i = 1, n_warmup |
|
call orig_gauss_vector(harvest, sigma_cut=-1.0_rp) |
|
enddo |
|
|
|
! --- Original --- |
|
call ran_seed_put(seed_val) |
|
dummy = 0 |
|
call cpu_time(t_start) |
|
do i = 1, n_calls |
|
call orig_gauss_vector(harvest, sigma_cut=-1.0_rp) |
|
dummy = dummy + harvest(1) |
|
enddo |
|
call cpu_time(t_end) |
|
write(*,'(A,F8.3,A,F12.6)') ' Original: ', t_end - t_start, 's (checksum: ', dummy, ')' |
|
|
|
! --- Clean --- |
|
call ran_seed_put(seed_val) |
|
dummy = 0 |
|
call cpu_time(t_start) |
|
do i = 1, n_calls |
|
call clean_gauss_vector(harvest, sigma_cut=-1.0_rp) |
|
dummy = dummy + harvest(1) |
|
enddo |
|
call cpu_time(t_end) |
|
write(*,'(A,F8.3,A,F12.6)') ' Clean: ', t_end - t_start, 's (checksum: ', dummy, ')' |
|
|
|
! --- Inlined --- |
|
call ran_seed_put(seed_val) |
|
dummy = 0 |
|
call cpu_time(t_start) |
|
do i = 1, n_calls |
|
call inlined_gauss_vector(harvest, sigma_cut=-1.0_rp) |
|
dummy = dummy + harvest(1) |
|
enddo |
|
call cpu_time(t_end) |
|
write(*,'(A,F8.3,A,F12.6)') ' Inlined: ', t_end - t_start, 's (checksum: ', dummy, ')' |
|
|
|
! --- Intrinsic (Fortran random_number + Box-Muller) --- |
|
call random_seed() ! intrinsic seeding (not bmad state) |
|
dummy = 0 |
|
call cpu_time(t_start) |
|
do i = 1, n_calls |
|
call intrinsic_gauss_vector(harvest) |
|
dummy = dummy + harvest(1) |
|
enddo |
|
call cpu_time(t_end) |
|
write(*,'(A,F8.3,A,F12.6)') ' Intrinsic:', t_end - t_start, 's (checksum: ', dummy, ')' |
|
|
|
! --- Ziggurat --- |
|
call ran_seed_put(seed_val) |
|
dummy = 0 |
|
call cpu_time(t_start) |
|
do i = 1, n_calls |
|
call ziggurat_gauss_vector(harvest) |
|
dummy = dummy + harvest(1) |
|
enddo |
|
call cpu_time(t_end) |
|
write(*,'(A,F8.3,A,F12.6)') ' Ziggurat: ', t_end - t_start, 's (checksum: ', dummy, ')' |
|
|
|
write(*,*) |
|
write(*,'(A)') '------------------------------------------------------' |
|
write(*,*) |
|
|
|
! --- Original scalar --- |
|
call ran_seed_put(seed_val) |
|
dummy = 0 |
|
call cpu_time(t_start) |
|
do i = 1, n_calls * vec_size |
|
call orig_gauss_scalar(harvest(1), sigma_cut=-1.0_rp) |
|
dummy = dummy + harvest(1) |
|
enddo |
|
call cpu_time(t_end) |
|
write(*,'(A,F8.3,A,F12.6)') ' Original: ', t_end - t_start, 's (checksum: ', dummy, ')' |
|
|
|
! --- Clean scalar (same as original for scalar) --- |
|
call ran_seed_put(seed_val) |
|
dummy = 0 |
|
call cpu_time(t_start) |
|
do i = 1, n_calls * vec_size |
|
call clean_gauss_scalar(harvest(1), sigma_cut=-1.0_rp) |
|
dummy = dummy + harvest(1) |
|
enddo |
|
call cpu_time(t_end) |
|
write(*,'(A,F8.3,A,F12.6)') ' Clean: ', t_end - t_start, 's (checksum: ', dummy, ')' |
|
|
|
! --- Inlined scalar --- |
|
call ran_seed_put(seed_val) |
|
dummy = 0 |
|
call cpu_time(t_start) |
|
do i = 1, n_calls * vec_size |
|
call inlined_gauss_scalar(harvest(1), sigma_cut=-1.0_rp) |
|
dummy = dummy + harvest(1) |
|
enddo |
|
call cpu_time(t_end) |
|
write(*,'(A,F8.3,A,F12.6)') ' Inlined: ', t_end - t_start, 's (checksum: ', dummy, ')' |
|
|
|
write(*,*) |
|
write(*,'(A)') ' (Intrinsic/Ziggurat are vector-only)' |
|
write(*,*) |
|
write(*,'(A)') '======================================================' |
|
|
|
end program ran_gauss_bench |