When to use the "asynchronous" attribute and why?

While I was learning the usage of Forpy (a very nice library to call Python directly from Fortran!), I found that the user is advised to attach the asynchronous attribute when making a Numpy ndarray wrapper to an existing Fortran array. For example, a code sample contains this line:

real, asynchronous :: matrix(NROWS, NCOLS)

where an ndarray wrapper is created for matrix. The manual page says:

Since the Fortran array is used as underlying buffer for the ndarray, it can be indirectly modified by changing the ndarray. To avoid bugs related to certain compiler optimizations, declare the Fortran array with the ‚Äėasynchronous‚Äô attribute.

Because I had never used the asynchronous attribute before, I checked Modern Fortran Explained (Sec.17.4, see the bottom of this question), but I could not understand the description very well…

So I am wondering:

  • Is this attribute possibly designed for some parallel calculations in mind
    (based on the attribute name)?
  • Or, was it introduced as a hint to suppress some compiler optimization or even bugs?
  • Most importantly, when are we users expected to use this keyword in actual programming? Is it used mainly for interactions with foreign languages (as in Forpy)? :thinking:

I would really appreciate any advice / comments, and thanks in advance! :slight_smile:

PS. The above question is a crosspost to comp.lang.fortran. Because the community could be somewhat different, I thought it would be useful to post the same question for both. But if this is not very good, please let me know so that I can delete one of them.

Below is some excerpts from MFE, Sec.17.4:

The asynchronous attribute for a variable has been introduced to warn the compiler that optimizations involving movement of code across wait statements (or other statements that cause wait operations) might lead to incorrect results. If a variable appears in an executable statement or a specification expression in a scoping unit and any statement of the scoping unit is executed while the variable is an affector, it must have the asynchronous attribute in the scoping unit.

There are restrictions that avoid any copying of an actual argument when the corresponding dummy argument has the asynchronous attribute: the actual argument must not be an array section with a vector subscript; if the actual argument is an array section or an assumed-shape array, the dummy argument must be an assumed-shape array; and if the actual argument is a pointer array, the dummy argument must be an assumed-shape or pointer array.

1 Like

Hi @septc, the following explanation is how I understand it, and not necessarily 100% correct. Therefore, consider it with caution.

The attribute asynchronous declares that the receiving variable can, potentially, be used in an asynchronous I/O operation.
An asynchronous I/O operation may be performed with write or read followed by (unit, asynchronous='yes',...), by default asynchronous='no'.
Note, that the attribute asynchronous in the variable declaration can be omitted, and if later the variable is used in asynchronous IO then it will be implicitly applied to the said variable.

Working example with both formatted and unformatted(commented) IO:

program test
    use iso_fortran_env, only: stdout=>output_unit
    implicit none
    integer :: funit, i
    integer, asynchronous :: array(50) 
    !open (newunit=funit,file='asynch.dat',asynchronous='YES', form='unformatted') ! works with unformatted too
    open (newunit=funit,file='asynch.dat',asynchronous='YES')
    !write (funit)(i,i=1,50) ! unformatted write ! unformatted write
    write (funit, fmt='(10(i3,1x))') (i,i=1,50)
    rewind (funit)
    !read (funit,asynchronous='YES') array ! unformatted read
    read (funit,asynchronous='YES',fmt='(10(i3,1x))')array
    write (stdout,*)array(1:10)
end program test

In the above example note the wait() this is (in my understanding) something similar to sync all as used in Fortran coarrays or !$omp barrier.

However, there is something dubious with derived types:
The following will not compile:

type :: async_io
    integer, asynchronous :: array(50) 
end type
6 |         integer, asynchronous :: array(50)
  |                            1
Error: Attribute at (1) is not allowed in a TYPE definition

You can overcome this with the following:

type :: async_io
    integer :: array(50) 
end type
type(async_io), asynchronous :: asio


type :: async_io
    integer :: array(50) 
end type
type(async_io) :: asio
asynchronous :: asio

but not with:

type :: async_io
    integer :: array(50) 
end type
type(async_io) :: asio
asynchronous :: asio%array ! syntax error
1 Like

Thanks very much for your info and example code! I’ve run the code with gfortran-9.3 and it finished as expected (so the feature seems already supported). As I wrote in my question, I imagined the asynchronous keyword to be some sort of parallel things, but now I understand it is something totally different… XD

RE asynchronous I/O, it might be useful, e.g., for writing trajectory snapshots to files, such that CPU will be able to keep the time evolution (until the wait() statement). I will try that possibility later.

RE the restriction of asynchronous not allowed for type components, I am not sure about the reason either, but I thought it is a bit similar to the restriction of the target attribute (which cannot be attached to type components, but only to a type variable as a whole). The restriction might come from a similar reason if they are related to some internal memory management (by the compiler), but again, not sure … :laughing:

I have never used it myself, neither have I seen it used… but I think it is a safety attribute. From the previous example integer, asynchronous :: array(50) if we know that array is to be used eg. in MPI_SEND(array) immediately followed by write(array), asynchronous will ensure that there will be no IO unless mpi is completed? But as I have already admitted, I am no expert and if anyone has actually used it or knows more please correct me.

1 Like

Originally, ASYNCHRONOUS was only for asynchronous I/O. In Fortran 2018 the attribute can be more generally applied to ‚Äúasynchronous communications‚ÄĚ (just what that means is unspecified). ASYNCHRONOUS refers only to the data possibly being changed or referenced in ways the compiler can‚Äôt see, whereas VOLATILE permits many more changes (deallocations, pointer redefinitions, etc.) VOLATILE tells the compiler that it cannot make ANY assumptions (such as storing the address of a variable in a register), whereas ASYNCHRONOUS just means that the value may change unexpectedly.