How to really implement vectorization in Fortran? [closed] - fortran

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 1 year ago.
Improve this question
All the below code are using -O3 -xHost (or -Ofast -march=native in gfortran) optimization.
example1
integer, parameter :: r8=selected_real_kind(15,9)
subroutine f(a,b,c)
real(kind=r8) :: a(1000), b(1000), c(1000)
c(:)=a(:)+b(:)
return
end
The above code should be vectorized,right? The real is real 8 type, it should took 64 bit in the ram. For AVX2 it is with 256 bit register or something, The above code is like doing c(1:4) = a(1:4) + b(1:4) in one clock cycle or something, and 4 is because 256/64=4.
But why do I frequently find that actually doing the loop is faster? see example 2,
example2
integer, parameter :: r8=selected_real_kind(15,9)
subroutine f(a,b,c)
real(kind=r8) :: a(1000), b(1000), c(1000)
integer :: i
do i=1,1000
c(i)=a(i)+b(i)
endddo
return
end
I notice that the compiler in example1 may complain that the array are not aligned or something. Therefore actually example2 is faster than example1.
But I mean in principle both example1 and 2 should be the same speed right? In example2, the compiler should be smart enough to vectorize the code correspondingly.
Finally, in the below code, example3, is there any vectorization?
example3
integer, parameter :: r8=selected_real_kind(15,9)
subroutine f(a,b,c)
real(kind=r8) :: a(1000), b(1000), c(1000)
c(:) = exp(a(:)) + log(b(:))
return
end
or slightly complicated things like, example4
example4
integer, parameter :: r8=selected_real_kind(15,9)
subroutine f(a,b,c)
real(kind=r8) :: a(1000), b(1000), c(1000)
c(:) = exp(a(:)) + log(b(:)) + exp(a(:)) * log(b(:))
return
end
I mean is vectorization is only for those very basic operatons like, op = + - * /
a(:) = b(:) op c(:)
However, for something more complicated, like
a(:) = log(b(:) + c(:)*exp(a(:))*ln(b(:)))
Then vectorization may not work?
It seems in many case using a do loop is faster than writing things like a(:)=b(:)+c(:). It seem the compiler are doing very good job or highly optimized in just doing the do loops.

Related

When passing function name as argument, the function does not take array as input [duplicate]

This question already has answers here:
How to pass array to a procedure which is passed as an argument to another procedure using Fortran
(2 answers)
Closed 1 year ago.
I'm writing a subroutine that can take function names are argument.
In my example, it is call call_test(ga), where ga is a function ga(x).
My old practice works fine if x is a scalar.
The problem is the program failed if x is an array.
The minimal sample that can reproduce the problem is the code below:
module fun
implicit none
private
public :: ga, call_test
contains
subroutine call_test(fn)
double precision,external::fn
double precision::f
double precision,dimension(0:2)::x0
x0 = 0.111d0
print*,'Input x0=',x0
print*,'sze x0:',size(x0)
f = fn(x0)
print*,'fn(x0)=',f
end subroutine call_test
function ga(x) result(f)
double precision,dimension(0:) :: x
double precision::f
print*,'size x inside ga:',size(x)
print*,'x in ga is:=',x
f = sum(x)
end function ga
end module
program main
use fun
call call_test(ga)
end program main
Using latest ifort, my execution result is:
Input x0= 0.111000000000000 0.111000000000000 0.111000000000000
sze x0: 3
size x inside ga: 140732712329264
forrtl: severe (174): SIGSEGV, segmentation fault occurred
Image PC Routine Line Source
a.out 000000010C6EC584 Unknown Unknown Unknown
libsystem_platfor 00007FFF20610D7D Unknown Unknown Unknown
a.out 000000010C6C62B2 _MAIN__ 32 main.f90
a.out 000000010C6C5FEE Unknown Unknown Unknown
Using gfortran the result is
Input x0= 0.11100000000000000 0.11100000000000000 0.11100000000000000
sze x0: 3
size x inside ga: 0
x in ga is:=
fn(x0)= 0.0000000000000000
My question is why is this, and how to make it work. A functional code solution based on my code is highly appreciated.
#IanBush is right in his comment. You need an explicit interface as the function argument takes an assumed-shape dummy argument. Since you have some other deprecated features in your code, here is a modern reimplementation of it in the hope of improving Fortran community coding practice,
module fun
use iso_fortran_env, only: RK => real64
implicit none
private
public :: ga, call_test
abstract interface
function fn_proc(x) result(f)
import RK
real(RK), intent(in) :: x(0:)
real(RK) :: f
end function fn_proc
end interface
contains
subroutine call_test(fn)
implicit none
procedure(fn_proc) :: fn
real(RK) :: f
real(RK) :: x0(0:2)
x0 = 0.111_RK
write(*,"(*(g0,:,' '))") 'Input x0 =',x0
write(*,"(*(g0,:,' '))") 'sze x0:',size(x0)
f = fn(x0)
write(*,"(*(g0,:,' '))") 'fn(x0) =', f
end subroutine call_test
function ga(x) result(f)
real(RK), intent(in) :: x(0:)
real(RK) :: f
write(*,"(*(g0,:,' '))") 'size x inside ga:',size(x)
write(*,"(*(g0,:,' '))") 'x in ga is:',x
f = sum(x)
end function ga
end module
program main
use fun
call call_test(ga)
end program main
Note the use of the abstract interface. The import statement merely imports the symbol RK for usage within the abstract. Without it, you will have to use iso_fortran_env to declare RK as real64. Do not use double precision, it is deprecated and does not necessarily mean 64bit real number. Also, note the conversion of 0.111d0 to 0.111_RK. Also, I strongly recommend not using single-letter variable names. They are ugly, error-prone, and non-informative. Modern Fortran allows variable names up to 63 characters long. There is no harm in having long but descriptive variable names. Here is the code output,
$main
Input x0 = 0.11100000000000000 0.11100000000000000 0.11100000000000000
sze x0: 3
size x inside ga: 3
x in ga is: 0.11100000000000000 0.11100000000000000 0.11100000000000000
fn(x0) = 0.33300000000000002

"A kind type parameter must be a compile-time constant." in Fortran 90

I try to introduce complex-valued array and variables in Fortran. For the following code
program
integer :: i , j !two dimensional real array
real(dp) :: m1(3,2)
complex(dp) :: a1(3,2),a2(3,2), c0, c1
!assigning some values to the array numbers
c0 = (1.0_dp, 0.0_dp)
c1 = (0.000000001_dp, 0.0_dp)
do i=1,3
do j = 1, 2
a1(i,j) = c0*i*j
end do
end do
do i=1,3
do j = 1, 2
a2(i,j) = c0*i*j + c1
end do
end do
do i=1,3
do j = 1, 2
if (dabs( dreal(a1(i,j)) - dreal(a2(i,j))) > 1.e-6 then
write (*,*), 'warning',i,j, a1(i,j), a2(i,j)
end if
end do
end do
write (*,*), a1(1,1), a2(1,1)
end program
ifort gives me
complex(dp) :: a1(3,2), a2(3,2)
-----------^
why complex(dp) requires compile-time constant and how to fix it? Thank you.
The kind parameter dp must be a constant, see Fortran - setting kind/precision of a variable at run time
However, you do not have the same problem as in the link, you did not try to define dp at all! First, you must use IMPLICIT NONE, it is absolutely necessary for safe programming and the biggest problem with your code. Then it will tell you that the type of dp is not declared.
You just define the kind constant in one of the usual ways, as a constant. The most simple is:
program main
!NECESSARY!
implicit none
integer, parameter :: dp = kind(1.d0)
Note that I named the program main, you have to name your program. Or just omit the program.
More about defining real kinds can be found at Fortran 90 kind parameter
Another note: Forget dabs() and dreal(). dabs() is an old remnant of Fortran 66 and dreal() is not standard Fortran at all. Just use abs and either dble() or better real( ,dp).

Fortran EQUIVALENCE statement with array length from subroutine input

I'm modernizing some old Fortran code and I cannot get rid of an equivalence statement somewhere (long story short: it's mixed use is so convoluted it'd take too much work to convert everything).
I need the length of the EQUIVALENCEd arrays to depend on some input, like the following code:
program test_equivalence
implicit none
type :: t1
integer :: len = 10
end type t1
type(t1) :: o1
call eqv_int(o1%len)
call eqv(o1)
return
contains
subroutine eqv_int(len)
integer, intent(in) :: len
integer :: iwork(len*2)
real(8) :: rwork(len)
equivalence(iwork,rwork)
print *, 'LEN = ',len
print *, 'SIZE(IWORK) = ',size(iwork)
print *, 'SIZE(RWORK) = ',size(rwork)
end subroutine eqv_int
subroutine eqv(o1)
type(t1), intent(in) :: o1
integer :: iwork(o1%len*2)
real(8) :: rwork(o1%len)
equivalence(iwork,rwork)
print *, 'LEN = ',o1%len
print *, 'SIZE(IWORK) = ',size(iwork)
print *, 'SIZE(RWORK) = ',size(rwork)
end subroutine eqv
end program test_equivalence
This program will create 0-length arrays with gfortran 9.2.0. This is the output:
LEN = 10
SIZE(IWORK) = 0
SIZE(RWORK) = 0
LEN = 10
SIZE(IWORK) = 0
SIZE(RWORK) = 0
The same code will return Array 'rwork' at (1) with non-constant bounds cannot be an EQUIVALENCE object when compiled with gfortran 5.3.0, the warning disappears since gfortran 6.2.0, but the size of the arrays is always 0. So maybe compiler bug?
The source code is indeed not a valid Fortran program. To be specific, it violates the numbered constraint C8106 of Fortran 2018:
An equivalence-object shall not be a designator with a base object that is .. an automatic data object ..
Being a numbered constraint, the compiler must be capable of detecting this violation. If hasn't such a capability this is a deficiency in the compiler (a bug). Being "capable" doesn't mean doing so by default, so please look carefully to see whether there are options which do lead to this detection. Someone familiar with the internals of GCC can give further detail here.
As the source isn't a valid Fortran program, the compiler is allowed to consider the arrays of size zero if it has skipped the violation detection.

Function type does not match the function definition

I am new to Fortran, writing some practice code with a function that returns Farenheit from Celsius
program Console1
implicit none
real, parameter :: ikind = selected_real_kind(p=15)
real (kind = ikind):: c,f,o,faren
print *, "enter a temperature in degrees celsius"
read *, c
write(*,10) "farenheit =", faren(c)
10 format(a,f10.8)
end program Console1
function faren(c)
real, parameter :: ikind = selected_real_kind(p=15)
real (kind = ikind):: c,f
faren = (9/5)*c + 32
end function faren
I get an error #7977 : The type of the function reference does not match the type of the function definition.
So with that if i change function faren(c) to real function faren(c)
I get the same error, but the types are the same?
Am i missing something? Do I have to define the function in the main program?
There are several issues in addition to the structural/code arrangement ones already noted.
First, KIND is an integer, so you want to change
real, parameter :: ikind = selected_real_kind(p=15)
to
integer, parameter :: ikind = selected_real_kind(p=15)
Ideally, you want to define that in only one place (i.e. in a module) and reference it from both your main program and the function, but the code should be fine as it is for test purposes.
A second issue that often trips up newcomers to Fortran (and Python2) is that real numbers and integers are distinct types and are not generally interchangeable.
faren = (9/5)*c + 32
simplifies to
faren = (1)*c + 32
because integer division has an integer result; 9/5 = 1
Fortran is picky about numerical values (that's sort of the whole point of the language) so what you probably want is:
faren = (9.0 / 5.0) * c + 32.0
Or more precisely, if faren is defined with a specific precision of ikind,
faren = (real(9.0,ikind) / real_(5.0,ikind)) * c + real(32.0,ikind)
or
faren = (9.0_ikind / 5.0_ikind) * c + 32.0_ikind
This syntax tends to make people's heads explode. Welcome to modern fortran ;)
The last issue deals with the horrors of Fortran I/O. From a design standpoint, you need to know what results the user expects and make sure the output format can display them. The legitimate range of input values for c is -273.15 (give or take) to some upper bound which relies on the use case for the code. If you're dealing with cooking temperatures, you probably won't exceed 400.0; if you're doing fusion research, you could be going much higher. Are 8 figures past the decimal useful or believable? In this case, we're just testing the code so we may not need a lot of precision in the output; you'll want to change the output format to something like:
10 format(a,es10.2)
or
10 format(a,g16.8)
You need to ensure the total field width (the number before the dot) can contain the decimal part (the number after the dot) along with the integer part of the number, plus the space needed to show sign and exponent. For scientific notation, four characters are eaten by mantissa sign, decimal point, 'E' and exponent sign. It may be safer just starting out to use an output format of *; it's frustrating to fight with numerics and formatting simultaneously.
That is a good effort and simple start to work through the nuance, so a good question.
Personally I would use reals for the math, rather the 9/5, and use a module. In this example you could pass in a real or a double to C2Faren and the interface/procedure will sort out whether to use the real or the double version. Then you have a few options in case you want different precision.
You could also use the ISO_C_BINDING if you do mixed language...
MODULE MyTEMPS
PRIVATE
DOUBLE PRECISION, PARAMETER :: C2F_ScaleFact = 1.8D0
DOUBLE PRECISION, PARAMETER :: F2C_ScaleFact = /(1.0D0 / 1.8D0)/
DOUBLE PRECISION, PARAMETER :: F2C_Offset = 32.0D0
PUBLIC Faren2C
INTERFACE C2Faren
MODULE PROCEDURE C2Faren_Real, C2Faren_DBL
END INTERFACE
CONTAINS
!========= REAL VERISON =========
REAL FUNCTION C2Faren_Real(c)
IMPLICIT NONE
real, INTENT(IN ) :: c
C2Faren_Real = ( C*F2C_ScaleFact ) + F2C_Offset
RETURN
END FUNCTION C2Faren_Real
!========= DOUBLE VERSION =========
DOUBLE PRECISION FUNCTION C2Faren_DBL(c)
IMPLICIT NONE
DOUBLE PRECISION , INTENT(IN ) :: c
C2Faren_DBL = ( C*F2C_ScaleFact ) + F2C_Offset
RETURN
END FUNCTION C2Faren_DBL
!========= REAL VERSION (Faren to Centigrade) =========
REAL FUNCTION faren2C(Faren)
IMPLICIT NONE
REAL, INTENT(IN ) :: Faren
faren2C = (faren - F2C_Offset) / F2C_ScaleFact
RETURN
END FUNCTION faren2C
END MODULE MyTEMPS
Then your program uses the module via USE n the second line...
program Console1
USE MyTEMPS !<== Here
implicit none
real :: c, f
DOUBLE PRECISION :: Dc, Df ! No way to get Df to C or DC in the module (yet)!
print *, "enter a temperature in degrees celsius"
read *, c
write(*,10) "farenheit =", C2faren(c)
10 format(a,f10.6)
Dc = C
write(*,12) "farenheit =", C2faren(Dc)
12 format("DBL:",A,f10.6)
F = Dc
write(*,14) "Centigrade =", faren2C(F)
14 format("DBL:",A,f10.6)
end program Console1
So/and the main advantage of the module is when you end up wanting to use this stuff in a variety of programs and test and sort out the module once... Usually people put this sort of stuff (lots of modules) in a library, when the module(s) have lot of functions.
You could also put just the real, parameter :: ikind = selected_real_kind(p=15) into a module and use that in both the program and the function and you would be there. You were real close, and it mostly a matter of style and utility.
For Intel Fortran you can use REAL(KIND=4) and REAL(KIND=8)... Which I do, but that is not portable to gfortran, so it is probably a better habit to use the ISO_C_BINDING or just use REAL and DOUBLE PRECISION.
Modules are great but if you have a very simple code another way to work is to put the subroutines and functions in your main program. The trick is to put them after the word contains:
program xxx
stuff
contains
subroutine yyy
function zzz
end program xxx
In this way the functions can see into the contents of the main program so you don't have to re-declare your parameters and you are likely to get more meaningful error messages.
Since you are new I have a great resource I learned a lot from to share:
http://www.uv.es/dogarcar/man/IntrFortran90.pdf

Functions operating on arbitrary array sizes in Fortran [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 7 years ago.
Improve this question
In Fortran 95, how would you write a function which takes an arbitrary matrix of known dimension as a argument ? I get the impression that not allocating the dimension doesn't work, even if the function only operates on matrices which have been previously allocated.
real, dimension (2, 4) :: mymatrix
test(mymatrix)
contains
function test(matrix)
real, dimension (: , :), intent(in) :: matrix
someothervariable = matrix(i, j)
return
end function test(matrix)
This works as long as you have an explicit interface. Putting the function as an internal procedure as you have it works, as does putting the procedure into a module. Consider this example:
program example
implicit none
real, dimension(2,4) :: matrixA
real, dimension(5,13) :: matrixB
integer :: matval
matval = test(matrixA)
print *, 'Test returned: ',matval
matval = test(matrixB)
print *, 'Test returned: ',matval
contains
function test(matrix)
implicit none
real, dimension(:,:), intent(in) :: matrix
integer :: test
print *, "matrix dimensions:"
print *, "i: ", lbound(matrix,1), ubound(matrix,1)
print *, "j: ", lbound(matrix,2), ubound(matrix,2)
test = ubound(matrix,1)*ubound(matrix,2)
end function test
end program example
The output of this program is:
matrix dimensions:
i: 1 2
j: 1 4
Test returned: 8
matrix dimensions:
i: 1 5
j: 1 13
Test returned: 65
You can see that none of the arrays are allocatable and arbitrary sized arrays can be passed to the function. The function is able to inspect the bounds of the array dimensions and can operate on the contents of the array. I had the function return a value based upon the array dimensions as an example.
The problems with your code fragment are that you are calling the function improperly and are not returning a value. Functions (in Fortran) must return values and the value must be assigned to a variable. See the differences in how I call the function in my example. If you do not want to return a value, use a subroutine instead of a function.