Скорость разыменования свойств класса в фортране
Я ожидаю ответа, как не волнуйтесь, об этом позаботится компилятор, но я не уверен.
Когда я создаю какой-то метод в каком-то пользовательском типе / классе в Фортране, происходит ли какое-либо снижение производительности из-за ссылок на поля / разыменования объекта, например this%a(i) = this%b(i) + this%c(i)
по сравнению с просто работать с массивами, как a(i) = b(i) + c(i)
более сложный пример:
например, у меня есть эта функция, которая должна интерполировать значение на трехмерной сетке, которое действительно критично к производительности (она будет вызываться внутри тройного цикла над другим трехмерным массивом). Поэтому я думаю, что лучше (для производительности) создать метод класса, или, скорее, создать обычную подпрограмму, которая принимает массив в качестве аргумента.
type grid3D ! 3D grid maps of observables
real, dimension (3) :: Rmin, Rmax, Rspan, step ! grid size and spacing (x,y,z)
integer, dimension (3) :: N ! dimension in x,y,z
real, dimension (3,:, :, :), allocatable :: f ! array storing values of othe observable
contains
procedure :: interpolate => grid3D_interpolate
end type grid3D
function grid3D_interpolate(this, R ) result(ff)
implicit none
! variables
class (grid3D) :: this
real, dimension (3), intent (in) :: R
real :: ff
integer ix0,iy0,iz0
integer ix1,iy1,iz1
real dx,dy,dz
real mx,my,mz
! function body
ix0 = int( (R(1)/this%step(1)) + fastFloorOffset ) - fastFloorOffset
iy0 = int( (R(2)/this%step(2)) + fastFloorOffset ) - fastFloorOffset
iz0 = int( (R(3)/this%step(3)) + fastFloorOffset ) - fastFloorOffset
dx = R(1) - x0*this%step(1)
dy = R(2) - y0*this%step(2)
dz = R(3) - z0*this%step(3)
ix0 = modulo( x0 , this%N(1) )+1
iy0 = modulo( y0 , this%N(2) )+1
iz0 = modulo( z0 , this%N(3) )+1
ix1 = modulo( x0+1 , this%N(1) )+1
iy1 = modulo( y0+1 , this%N(2) )+1
iz1 = modulo( z0+1 , this%N(3) )+1
mx=1.0-dx
my=1.0-dy
mz=1.0-dz
ff = mz*(my*(mx*this%f(ix0,iy0,iz0) &
+dx*this%f(ix1,iy0,iz0)) &
+dy*(mx*this%f(ix0,iy1,iz0) &
+dx*this%f(ix1,iy1,iz0))) &
+dz*(my*(mx*this%f(ix0,iy0,iz1) &
+dx*this%f(ix1,iy0,iz1)) &
+dy*(mx*this%f(ix0,iy1,iz1) &
+dx*this%f(ix1,iy1,iz1)))
end if
end function grid3D_interpolate
end module T_grid3Dvec
1 ответ
На самом деле, нет.
- Пока ваша структура кода достаточно понятна (для компилятора), она может легко ее оптимизировать.
- Как только ваши ООП-структуры становятся слишком сложными или уровень разыменования становится слишком большим, вы можете получить некоторое улучшение от схемы разыменования вручную. (Я использую это довольно часто, хотя обычно для того, чтобы мой код читался человеком. Но однажды у меня было небольшое улучшение, но с кодом, использующим>5 уровней разыменования.)
Вот несколько примеров:
module vec_mod
implicit none
type t_vector
real :: x = 0.
real :: y = 0.
real :: z = 0.
end type
type t_group
type(t_vector),allocatable :: vecs(:)
end type
contains
subroutine sum_vec( vecs, res )
implicit none
type(t_vector),intent(in) :: vecs(:)
type(t_vector),intent(out) :: res
integer :: i
res%x = 0. ; res%y = 0. ; res%z = 0.
do i=1,size(vecs)
res%x = res%x + vecs(i)%x
res%y = res%y + vecs(i)%y
res%z = res%z + vecs(i)%z
enddo
end subroutine
subroutine sum_vec_ptr( vecs, res )
implicit none
type(t_vector),intent(in),target :: vecs(:)
type(t_vector),intent(out) :: res
integer :: i
type(t_vector),pointer :: curVec
res%x = 0. ; res%y = 0. ; res%z = 0.
do i=1,size(vecs)
curVec => vecs(i)
res%x = res%x + curVec%x
res%y = res%y + curVec%y
res%z = res%z + curVec%z
enddo
end subroutine
subroutine sum_vecGrp( vecGrp, res )
implicit none
type(t_group),intent(in) :: vecGrp
type(t_vector),intent(out) :: res
integer :: i
res%x = 0. ; res%y = 0. ; res%z = 0.
do i=1,size(vecGrp%vecs)
res%x = res%x + vecGrp%vecs(i)%x
res%y = res%y + vecGrp%vecs(i)%y
res%z = res%z + vecGrp%vecs(i)%z
enddo
end subroutine
subroutine sum_vecGrp_ptr( vecGrp, res )
implicit none
type(t_group),intent(in),target :: vecGrp
type(t_vector),intent(out) :: res
integer :: i
type(t_vector),pointer :: curVec, vecs(:)
res%x = 0. ; res%y = 0. ; res%z = 0.
vecs => vecGrp%vecs
do i=1,size(vecs)
curVec => vecs(i)
res%x = res%x + curVec%x
res%y = res%y + curVec%y
res%z = res%z + curVec%z
enddo
end subroutine
end module
program test
use omp_lib
use vec_mod
use,intrinsic :: ISO_Fortran_env
implicit none
type(t_vector),allocatable :: vecs(:)
type(t_vector) :: res
type(t_group) :: vecGrp
integer,parameter :: N=100000000
integer :: i, stat
real(REAL64) :: t1, t2
allocate( vecs(N), vecGrp%vecs(N), stat=stat )
if (stat /= 0) stop 'Cannot allocate memory'
do i=1,N
call random_number(vecs(i)%x)
call random_number(vecs(i)%y)
call random_number(vecs(i)%z)
enddo
print *,''
print *,'1 Level'
t1 = omp_get_wtime()
call sum_vec( vecs, res )
print *,res
t2 = omp_get_wtime()
print *,'Normal [s]:', t2-t1
t1 = omp_get_wtime()
call sum_vec_ptr( vecs, res )
print *,res
t2 = omp_get_wtime()
print *,'Pointer [s]:', t2-t1
print *,''
print *,'2 Levels'
vecGrp%vecs = vecs
t1 = omp_get_wtime()
call sum_vecGrp( vecGrp, res )
print *,res
t2 = omp_get_wtime()
print *,'Normal [s]:', t2-t1
t1 = omp_get_wtime()
call sum_vecGrp_ptr( vecGrp, res )
print *,res
t2 = omp_get_wtime()
print *,'Pointer [s]:', t2-t1
end program
Скомпилировано с параметрами по умолчанию (gfortran test.F90 -fopenmp
) три - небольшое преимущество от разыменования вручную, особенно для двух уровней разыменования:
OMP_NUM_THREADS=1 ./a.out
1 Level
16777216.0 16777216.0 16777216.0
Normal [s]: 0.69216769299237058
16777216.0 16777216.0 16777216.0
Pointer [s]: 0.67321390099823475
2 Levels
16777216.0 16777216.0 16777216.0
Normal [s]: 0.84902219301147852
16777216.0 16777216.0 16777216.0
Pointer [s]: 0.71247501399193425
После включения оптимизации (gfortran test.F90 -fopenmp -O3
), вы можете видеть, что компилятор фактически лучше выполняет свою работу автоматически:
OMP_NUM_THREADS=1 ./a.out
1 Level
16777216.0 16777216.0 16777216.0
Normal [s]: 0.13888958499592263
16777216.0 16777216.0 16777216.0
Pointer [s]: 0.19099253200693056
2 Levels
16777216.0 16777216.0 16777216.0
Normal [s]: 0.13436777899914887
16777216.0 16777216.0 16777216.0
Pointer [s]: 0.21104205500159878