To host functions within a threaded subroutine - multithreading

I encountered a problem when I port my Fortran project to OpenMP. In my original code, there are two functions named add and mpy being passed to a threaded subroutine submodel that throws respective function into another subroutine defined in a module toolbox.
Now, for my new code, I am wondering whether there is a way to produce exactly the same outcome as with my original code but with a tiny twist that moves the two functions add and mpy to be hosted (i.e., contained) within the subroutine submodel.
Thanks.
Lee
--- My original code consists of four files: MAIN.F90, MODEL.F90, VARIABLE.F90, and TOOLBOX.F90
OUTPUT:
--- addition ---
3 7 11 15
--- multiplication ---
2 12 30 56
Press any key to continue . . .
MAIN.F90
program main
use model
implicit none
call sandbox()
end program main
MODEL.F90
module model
use omp_lib
use variable
implicit none
contains
subroutine submodel(func,x,y)
implicit none
interface
function func(z)
implicit none
integer :: z,func
end function func
end interface
integer :: x,y
call tool(func,x,y)
end subroutine submodel
function add(a)
implicit none
integer :: a,add
add=a+thread_private
end function add
function mpy(m)
implicit none
integer :: m,mpy
mpy=m*thread_private
end function mpy
subroutine sandbox()
implicit none
integer :: a(4),b(4),c(4),i
a=[((i),i=1,7,2)]
b=[((i),i=2,8,2)]
!$omp parallel do
do i=1,4
thread_private=b(i)
call submodel(add,a(i),c(i))
enddo
!$omp end parallel do
write(6,'(a)') '--- addition ---'
write(6,'(4(i5))') c
!$omp parallel do
do i=1,4
thread_private=b(i)
call submodel(mpy,a(i),c(i))
enddo
!$omp end parallel do
write(6,'(a)') '--- multiplication ---'
write(6,'(4(i5))') c
end subroutine sandbox
end module model
TOOLBOX.F90
module toolbox
implicit none
contains
subroutine tool(funct,input,output)
implicit none
interface
function funct(x)
implicit none
integer :: x,funct
end function funct
end interface
integer :: input,output
output = funct(input)
end subroutine tool
end module toolbox
VARIABLE.F90
module variable
use toolbox
implicit none
integer :: thread_private
!$omp threadprivate(thread_private)
end module variable
Is it possible to simply rearrange them in this way? (I have tried and apparently it failed):
subroutine submodel(func,x,y)
implicit none
interface
function func(z)
implicit none
integer :: z,func
end function func
end interface
integer :: x,y
call tool(func,x,y)
contains
function add(a)
implicit none
integer :: a,add
add=a+thread_private
end function add
function mpy(m)
implicit none
integer :: m,mpy
mpy=m*thread_private
end function mpy
end subroutine submodel

You can make the two procedures internal to the subroutine submodel exactly as you did in your last code snippet. The problem is you cannot pass these two subroutines as actual arguments from outside of the subroutine, because you have no access to them there.
Even if you have procedure pointers to them stored somewhere, these would be invalid as soon as the original run of submodel that could have created them ended.
I would think about using some switch:
subroutine submodel(switch,x,y)
implicit none
integer :: switch,x,y
select case(switch)
case(USE_ADD)
call tool(add,x,y)
case(USE_MPY)
call tool(mpy,x,y)
case default
stop "unknown switch value"
end select
contains
function add(a)
implicit none
integer :: a,add
add=a+thread_private
end function add
function mpy(m)
implicit none
integer :: m,mpy
mpy=m*thread_private
end function mpy
end subroutine submodel
Another option is to keep your original design.

Related

Calling fftw routines from pure subroutines in Fortran90

Multithreaded FFTW can be implemented as in this from FFTW homepage. Instead, we want to call the serial FFTW routines within OpenMP parallel environment using multiple threads. We want variable and fourier_variable to be thread-safe. This could be done by using PURE subroutines and declaring variable and fourier_variable inside it. The question here is related to calling FFTW routines like fftw_execute_dft_r2c from within a PURE subroutine.
A stripped-down version of the code is presented here just for reference (the full code is an optimisation solver involving many FFTW calls).
PROGRAM main
USE fft_call
REAL(8), DIMENSION(1:N, 1:N) :: variable
COMPLEX(C_DOUBLE_COMPLEX), DIMENSION(1:N/2+1, 1:N) :: fourier_variable
INTEGER :: JJ
!$OMP PARALLEL
!$OMP DO
DO JJ = 1, 5
call fourier_to_physical(fourier_variable, variable)
END DO
!$OMP END DO
!$OMP END PARALLEL
END PROGRAM main
MODULE fft_call
contains
PURE SUBROUTINE fourier_to_physical( fourier_variable, variable)
IMPLICIT NONE
REAL(8), DIMENSION(1:N, 1:N) :: variable
COMPLEX(C_DOUBLE_COMPLEX), DIMENSION(1:N/2+1, 1:N), INTENT(OUT) :: fourier_variable
CALL fftw_execute_dft_r2c (plan_fwd, variable, fourier_variable)
END SUBROUTINE fourier_to_physical
END MODULE fft_call
The error while calling fftw_plan_dft_r2c_2d from the PURE subroutine fourier_to_physical:
Error: Function reference to 'fftw_plan_dft_r2c' at (1) is to a non-PURE procedure within a PURE procedure
The question: is there a way to call FFTW routines like fftw_execute_dft_r2c from within a PURE subroutine in Fortran90?
Or, in other words, are their PURE versions of fftw_execute_dft_r2c such that we can call them from PURE procedures? We are beginners to OpenMP.

Binary Search algorithm random array

I don't understand why the recursive function always gives me zero result, even if I put values inside the array.
it seems that size (a) == 0
recursive function binarySearch_R (a, value) result (bsresult)
real, intent(in) :: a(6), value
integer :: bsresult, mid
mid = size(a)/2 + 1
if (size(a) == 0) then
bsresult = 0 ! not found
else if (a(mid) > value) then
bsresult= binarySearch_R(a(:mid-1), value)
else if (a(mid) < value) then
bsresult = binarySearch_R(a(mid+1:), value)
if (bsresult /= 0) then
bsresult = mid + bsresult
end if
else
bsresult = mid ! SUCCESS!!
end if
end function binarySearch_R
program hji
read*, a
read*, value
print*, binarySearch_R
end program hji
Chapter 1: The dangers of implicit typing
The first thing I strongly recommend you do is to include the line
implicit none
after the program line. This will suppress implicit typing, and the resulting errors will give you some useful insight into what is happening.
If you did that, you'd get an error message:
$ gfortran -o binsearch binsearch.f90
binsearch.f90:23:12:
read*, a
1
Error: Symbol ‘a’ at (1) has no IMPLICIT type
binsearch.f90:27:25:
print*,binarySearch_R
1
Error: Symbol ‘binarysearch_r’ at (1) has no IMPLICIT type
binsearch.f90:24:16:
read*, value
1
Error: Symbol ‘value’ at (1) has no IMPLICIT type
It doesn't matter that a, value, and binarySearch_R were defined in the function. As the function is not part of the program block, the program doesn't know what these are.
With implicit typing active, it simply assumed that all three are simple real variables. (The type depends on the first letter of the variable name, i through n are integer, everything else is real)
Because this implicit typing can so easily hide coding errors, it's strongly, strongly suggested to always switch it off.
Which also means that we have to declare the variables a and value in the program:
program hji
implicit none
real :: a(6), value
...
end program hji
Chapter 2: How to introduce a function to the program?
So how does the program get access to the function? There are four ways:
The best way: Use a module
module mod_binsearch
implicit none
contains
recursive function binarySearch_R (a, value) result (bsresult)
...
end function binarySearch_R
end module mod_binsearch
program hji
use mod_binsearch
implicit none
real :: a(6), value
...
end program hji
Note that the use statement has to be before the implicit none.
This method leaves the function separate, but callable.
It automatically checks that the parameters (that's something we'll be coming to in a bit) are correct.
Have the function contained in the program.
Between the final line of code of the program and the end program statement, add the keyword contains, followed by the function code (everything from recursive function ... to end function ...).
This is the quick-and-dirty method. You have to be careful with this method as the function will automatically have access to the program's variables unless there's a new variable with that name declared inside the function.
The convoluted way: Interfaces
Create an interface block in the declaration section of your program's source code,
and repeat the interface information in there.
This still allows the compiler to check whether the function is invoked correctly, but it's up to you to ensure that this interface block is correct and matches the actual implementation.
The really, really ugly way: Declare it like a variable, invoke it like a function.
Please don't do that.
Chapter 3: Calling a function
When you call a function, you have to use the parentheses and give it all the parameters that it expects. In your case, you need to type
print *, binarySearch_r(a, value)
Chapter 4: Dynamic arrays as dummy parameters
In the successive recursive calls to the function, the array gets smaller and smaller.
But the dummy parameter is always the same size (6). Not only will this interfere with your algorithm, but this can also lead to dangerously undefined memory access.
Fortunately, specially for intent(in) dummy parameters, you can use dynamic arrays:
recursive function binarySearch_R(a, value)
real, intent(in) :: a(:), value
The single colon tells the compiler to expect a one-dimensional array, but not the length of it. Since you're already using size(a), it should automatically work.
Too long for a comment, but not an answer (and to any Fortran experts reading this, yes, there are one or two places where I gloss over some details because I think they are unimportant at this stage) ...
The way the code is written does not allow the compiler to help you. As far as the compiler is concerned there is no connection between the function and the program. As far as the program is concerned a is, because you haven't told the compiler otherwise, assumed to be a real scalar value. The a in the program is not the same thing as the a in the function - there is no connection between the function and the program.
The same is true for value.
The same is true for binarysearch_r - and if you don't believe this delete the function definition from the source code and recompile the program.
So, what must you do to fix the code ?
First step: modify your source code so that it looks like this:
program hji
... program code goes here ...
contains
recursive function binarySearch_R (a, value) result (bsresult)
... function code goes here ...
end function binarySearch_R
end program hji
This first step allows the compiler to see the connection between the program and the function.
Second step: insert the line implicit none immediately after the line program hji. This second step allows the compiler to spot any errors you make with the types (real or integer, etc) and ranks (scalar, array, etc) of the variables you declare.
Third step: recompile and start dealing with the errors the compiler identifies. One of them will be that you do not pass the arguments to the function so the line
print*, binarySearch_R
in the program will have to change to
print*, binarySearch_R(a, value)

Testing for memory leak in Fortran (using pFUnit)

I've wrote my first program using allocatable. It works as expected. But, does it really? And more importantly, how can I set up a unit-test to catch memory leaks?
The idea behind the program is to allocate a chunck of storage room for my list of objects in the first place. And every time I add one more element more to the list than the allocated size, I double the allocation. I do this to reduce the number of allocations and subsequent copying of data from the old allocated memory, to the newly allocated memory.
I might over complicate this, but I'd like to spend some time now understanding the pitfalls, rather than falling head first into them a year or two down into the project.
The ode is compiled with gfortran 8.3.0 on linux. And using pFUnit 4.1. The code below is an extract to test only the allocation part.
Heres my test-program:
program test_realloc
use class_test_malloc
integer :: i
real :: x, y
type(tmalloc) :: myobject
call myobject%initialize()
do i=1, 100
x = i * i
y = sqrt(x)
call myobject%add_nbcell(i, x, y)
end do
call myobject%dump()
end program test_realloc
array_reallocation.f:
!
! Simple test to see if my understanding of dynamicly allocation
! of arrays is correct.
!
module class_test_malloc
use testinglistobj
implicit none
type tmalloc
integer :: numnbcells, maxnbcells
type(listobj), allocatable :: nbcells(:)
contains
procedure, public :: initialize => init
procedure, public :: add_nbcell ! Might be private?
procedure, private :: expand_nbcells
procedure, public :: dump
end type tmalloc
contains
subroutine init(this)
class(tmalloc), intent(inout) :: this
this%numnbcells = 0
this%maxnbcells = 4
allocate (this%nbcells(this%maxnbcells))
end subroutine init
subroutine add_nbcell(this, idx, x, y)
class(tmalloc), intent(inout) :: this
integer, intent(in) :: idx
real, intent(in) :: x, y
type(listobj) :: nbcell
if(this%numnbcells .eq. this%maxnbcells) then
call this%expand_nbcells()
print *,"Expanding"
endif
this%numnbcells = this%numnbcells + 1
nbcell%idx = idx
nbcell%x = x
nbcell%y = y
this%nbcells(this%numnbcells) = nbcell
print *,"Adding"
end subroutine add_nbcell
subroutine expand_nbcells(this)
class(tmalloc), intent(inout) :: this
type(listobj), allocatable :: tmpnbcells(:)
integer :: size
size = this%maxnbcells *2
allocate (tmpnbcells(size))
tmpnbcells(1:this%maxnbcells) = this%nbcells
call move_alloc( from=tmpnbcells, to=this%nbcells)
this%maxnbcells = size
end subroutine
subroutine dump(this)
class(tmalloc), intent(inout) :: this
integer :: i
do i=1, this%numnbcells
print*, this%nbcells(i)%x, this%nbcells(i)%y
end do
end subroutine
end module
listobj.f:
module testinglistobj
type listobj
integer :: idx
real :: x
real :: y
end type
end module testinglistobj
You will not get any memory leaks with this code. The reason is, and this is fundamental to the understanding of allocatable arrays, is that in Fortran 95 onwards it is required that allocatable arrays without the save attribute automatically get deallocated when they go out of scope. The nett result of this is that memory leaks for such arrays are impossible. This is one very good reason why you should prefer allocatable arrays to pointers. Related is the general software engineering principle of keeping the scope of variables as limited as possible, so that arrays are in memory for as short a period as possible.
Note this does not mean that you should never deallocate them as an array may remain in scope long after it is actually useful. Here "manual" deallocation may be of use. But it is not a memory leak.

Fortran function that returns scalar OR array depending on input

I'm trying to crate a function in Fortran (95) that that will have as input a string (test) and a character (class). The function will compare each character of test with the character class and return a logical that is .true. if they are of the same class1 and .false. otherwise.
The function (and the program to run it) is defined below:
!====== WRAPPER MODULE ======!
module that_has_function
implicit none
public
contains
!====== THE ACTUAL FUNCTION ======!
function isa(test ,class )
implicit none
logical, allocatable, dimension(:) :: isa
character*(*) :: test
character :: class
integer :: lt
character(len=:), allocatable :: both
integer, allocatable, dimension(:) :: intcls
integer :: i
lt = len_trim(test)
allocate(isa(lt))
allocate(intcls(lt+1))
allocate(character(len=lt+1) :: both)
isa = .false.
both = class//trim(test)
do i = 1,lt+1
select case (both(i:i))
case ('A':'Z'); intcls(i) = 1! uppercase alphabetic
case ('a':'a'); intcls(i) = 2! lowercase alphabetic
case ('0':'9'); intcls(i) = 3! numeral
case default; intcls(i) = 99! checks if they are equal
end select
end do
isa = intcls(1).eq.intcls(2:)
return
end function isa
end module that_has_function
!====== CALLER PROGRAM ======!
program that_uses_module
use that_has_function
implicit none
integer :: i
i = 65
! Reducing the result of "isa" to a scalar with "all" works:
! V-V
do while (all(isa(achar(i),'A')))
print*, achar(i)
i = i + 1
end do
! Without the reduction it doesn''t:
!do while (isa(achar(i),'A'))
! print*, achar(i)
! i = i + 1
!end do
end program that_uses_module
I would like to use this function in do while loops, for example, as it is showed in the code above.
The problem is that, for example, when I use two scalars (rank 0) as input the function still returns the result as an array (rank 1), so to make it work as the condition of a do while loop I have to reduce the result to a scalar with all, for example.
My question is: can I make the function conditionally return a scalar? If not, then is it possible to make the function work with vector and scalar inputs and return, respectively, vector and scalar outputs?
1. What I call class here is, for example, uppercase or lowercase letters, or numbers, etc. ↩
You can not make the function conditionally return a scalar or a vector.
But you guessed right, there is a solution. You will use a generic function.
You write 2 functions, one that takes scalar and return scalar isas, the 2nd one takes vector and return vector isav.
From outside of the module you will be able to call them with the same name: isa. You only need to write its interface at the beginning of the module:
module that_has_function
implicit none
public
interface isa
module procedure isas, isav
end interface isa
contains
...
When isa is called, the compiler will know which one to use thanks to the type of the arguments.
The rank of a function result cannot be conditional on the flow of execution. This includes selection by evaluating an expression.
If reduction of a scalar result is too much, then you'll probably be horrified to see what can be done instead. I think, for instance, of derived types and defined operations.
However, I'd consider it bad design in general for the function reference to be unclear in its rank. My answer, then, is: no you can't, but that's fine because you don't really want to.
Regarding the example of minval, a few things.1 As noted in the comment, minval may take a dim argument. So
integer :: X(5,4) = ...
print *, MINVAL(X) ! Result a scalar
print *, MINVAL(X,dim=1) ! Result a rank-1 array
is in keeping with the desire of the question.
However, the rank of the function result is still "known" at the time of referencing the function. Simply having a dim argument means that the result is an array of rank one less than the input array rather than a scalar. The rank of the result doesn't depend on the value of the dim argument.
As noted in the other answer, you can have similar functionality with a generic interface. Again, the resolved specific function (whichever is chosen) will have a result of known rank at the time of reference.
1 The comment was actually about minloc but minval seems more fitting to the topic.

Private and public variables inside a module and a a subroutine in OpenMP

I am trying to parallelize a fairly complicated simulation code used for oil field calculations.
My question is, if I can declare a variable or some allocatable arrays and a few subroutines in a module, then use this module in another module/subroutine which contains the parallel region, will those variables and arrays be considered private to each thread (i.e. they will have separate copies of those variables and changes made to a variables in a thread won't be seen by other threads) or they'll be shared?
Like this:
module m2
implicit none
integer :: global
contains
subroutine s2()
implicit none
integer :: tid
tid = ! Some calculation
end subroutine s2
end module m2
module m1
use m2
implicit none
contains
subroutine s1
!$omp parallel do
do i=1, 9
call s2()
end do
!$omp end parallel do
end subroutine s1
end module m1
Will tid and global be private or shared?
Any help is greatly appreciated!
Module variables are always shared in OpenMP unless you use the threadprivate directive. See Difference between OpenMP threadprivate and private for detailed description of threadprivate. So global will be shared.
The local variable tid is declared in the subroutine and called from the parallel region. Therefore it will be private unless it has the save attribute.
(Note that initialization like integer :: tid = 0 also adds the save implicitly, so be careful.)

Resources