mpi-forum / mpi-issues

Tickets for the MPI Forum
http://www.mpi-forum.org/
66 stars 7 forks source link

Fortran: MPI RMA integer interfaces for AINT arguments #528

Open jeffhammond opened 2 years ago

jeffhammond commented 2 years ago

Problem

RMA absolutely did the right thing with MPI_Aint size and displacement arguments.

In C, it is no problem to pass 32b integers to these arguments when AINT is 64b, because C type promotion rules just work.

In Fortran, I cannot do this, because an integer literal is not compatible with an AINT argument. I find it tedious to have to declare a variable just for this, or to explicitly cast with integer(100,MPI_ADDRESS_KIND).

Fortran

! USE mpi_f08
! MPI_Win_allocate(size, disp_unit, info, comm, baseptr, win, ierror)
!    USE, INTRINSIC :: ISO_C_BINDING, ONLY : C_PTR
!    INTEGER(KIND=MPI_ADDRESS_KIND), INTENT(IN) :: size
!    INTEGER, INTENT(IN) :: disp_unit
!    TYPE(MPI_Info), INTENT(IN) :: info
!    TYPE(MPI_Comm), INTENT(IN) :: comm
!    TYPE(C_PTR), INTENT(OUT) :: baseptr
!    TYPE(MPI_Win), INTENT(OUT) :: win
!    INTEGER, OPTIONAL, INTENT(OUT) :: ierror
program main
  use iso_fortran_env
  use mpi_f08
  implicit none
  integer(kind=MPI_ADDRESS_KIND) :: as = 100
  type(c_ptr) :: XA
  type(MPI_Win) :: WA
  TYPE(MPI_Comm) :: comm = MPI_COMM_WORLD
  call MPI_Win_allocate(as, 1, MPI_INFO_NULL, comm, XA, WA)
  call MPI_Win_allocate(100, 1, MPI_INFO_NULL, comm, XA, WA)
end program main
% mpifort y.F90
y.F90:20:60:
   20 |   call MPI_Win_allocate(100, 1, MPI_INFO_NULL, comm, XA, WA)
      |                                                            1
Error: There is no specific subroutine for the generic 'mpi_win_allocate' at (1)

C

#include <mpi.h>
int main(void)
{
  MPI_Aint as = 100;
  int * XA;
  MPI_Win WA;
  MPI_Win_allocate(as, 1, MPI_INFO_NULL, MPI_COMM_WORLD, XA, &WA);
  MPI_Win_allocate(100, 1, MPI_INFO_NULL, MPI_COMM_WORLD, XA, &WA);
}
% mpicc y.c && echo SUCCESS
SUCCESS

Proposal

Use Fortran polymorphism and add subroutine declarations for default integer arguments where appropriate.

Changes to the Text

Add the following interfaces.

The rule here is: for all INTENT(IN) instances of INTEGER(KIND=MPI_ADDRESS_KIND) that describe a count-like argument (size or displacement), add a default integer interface.

Obviously, there are cases where size and displacement need to larger than INTEGER but obviously the programmer is going to have to use an AINT there, so there is no potential for misuse. Any time the argument is an AINT, the proper interface will be used. Only when the argument fits into an INTEGER will that interface be used.

MPI_Alloc_mem

INTERFACE MPI_ALLOC_MEM 
    SUBROUTINE MPI_ALLOC_MEM_I(SIZE, INFO, BASEPTR, IERROR) 
        IMPORT ::  MPI_ADDRESS_KIND 
        INTEGER, INTENT(IN) :: SIZE
        TYPE(MPI_Info), INTENT(IN) :: info 
        INTEGER(KIND=MPI_ADDRESS_KIND), INTENT(OUT) :: BASEPTR 
        INTEGER, OPTIONAL, INTENT(OUT) :: IERROR
    END SUBROUTINE 
    SUBROUTINE MPI_ALLOC_MEM_CPTR_I(SIZE, INFO, BASEPTR, IERROR) 
        USE, INTRINSIC ::  ISO_C_BINDING, ONLY : C_PTR 
        INTEGER, INTENT(IN) ::  SIZE
        TYPE(MPI_Info), INTENT(IN) :: info 
        TYPE(C_PTR), INTENT(OUT) ::  BASEPTR 
        INTEGER, OPTIONAL, INTENT(OUT) :: IERROR
    END SUBROUTINE 
END INTERFACE

MPI_Win_create

INTERFACE MPI_WIN_CREATE
     SUBROUTINE MPI_Win_create_I(base, size, disp_unit, info, comm, win, ierror)
          TYPE(*), DIMENSION(..), ASYNCHRONOUS, INTENT(IN) :: base
          INTEGER INTENT(IN) :: size, disp_unit
          TYPE(MPI_Info), INTENT(IN) :: info
          TYPE(MPI_Comm), INTENT(IN) :: comm
          TYPE(MPI_Win), INTENT(OUT) :: win
          INTEGER, OPTIONAL, INTENT(OUT) :: ierror
    END SUBROUTINE 
END INTERFACE

MPI_Win_allocate

INTERFACE MPI_WIN_ALLOCATE 
    SUBROUTINE MPI_WIN_ALLOCATE_I(SIZE, DISP_UNIT, INFO, COMM, BASEPTR, & 
                                  WIN, IERROR) 
        IMPORT ::  MPI_ADDRESS_KIND 
        INTEGER INTENT(IN) :: size, disp_unit
        TYPE(MPI_Info), INTENT(IN) :: info
        TYPE(MPI_Comm), INTENT(IN) :: comm
        TYPE(MPI_Win), INTENT(OUT) :: win
        INTEGER(KIND=MPI_ADDRESS_KIND), INTENT(OUT) :: BASEPTR 
        INTEGER, OPTIONAL, INTENT(OUT) :: ierror
    END SUBROUTINE 
    SUBROUTINE MPI_WIN_ALLOCATE_CPTR_I(SIZE, DISP_UNIT, INFO, COMM, BASEPTR, & 
                                       WIN, IERROR) 
        USE, INTRINSIC ::  ISO_C_BINDING, ONLY : C_PTR 
        INTEGER, INTENT(IN) ::  SIZE, DISP_UNIT
        TYPE(MPI_Info), INTENT(IN) :: info
        TYPE(MPI_Comm), INTENT(IN) :: comm
        TYPE(MPI_Win), INTENT(OUT) :: win
        TYPE(C_PTR), INTENT(OUT) ::  BASEPTR 
        INTEGER, OPTIONAL, INTENT(OUT) :: IERROR
    END SUBROUTINE 
END INTERFACE 

MPI_Win_allocate_shared

INTERFACE MPI_WIN_ALLOCATE_SHARED 
    SUBROUTINE MPI_WIN_ALLOCATE_SHARED_I(SIZE, DISP_UNIT, INFO, COMM, & 
                                         BASEPTR, WIN, IERROR) 
        IMPORT ::  MPI_ADDRESS_KIND 
        INTEGER, INTENT(IN) ::  SIZE, DISP_UNIT
        TYPE(MPI_Info), INTENT(IN) :: info
        TYPE(MPI_Comm), INTENT(IN) :: comm
        TYPE(MPI_Win), INTENT(OUT) :: win
        TYPE(C_PTR), INTENT(OUT) ::  BASEPTR 
        INTEGER, OPTIONAL, INTENT(OUT) :: IERROR
    END SUBROUTINE 
    SUBROUTINE MPI_WIN_ALLOCATE_SHARED_CPTR_I(SIZE, DISP_UNIT, INFO, COMM, & 
                                              BASEPTR, WIN, IERROR) 
        USE, INTRINSIC ::  ISO_C_BINDING, ONLY : C_PTR 
        INTEGER, INTENT(IN) ::  SIZE, DISP_UNIT
        TYPE(MPI_Info), INTENT(IN) :: info
        TYPE(MPI_Comm), INTENT(IN) :: comm
        TYPE(MPI_Win), INTENT(OUT) :: win
        TYPE(C_PTR), INTENT(OUT) ::  BASEPTR 
        INTEGER, OPTIONAL, INTENT(OUT) :: IERROR
    END SUBROUTINE 
END INTERFACE 

MPI_Win_attach

INTERFACE MPI_WIN_ATTACH
    SUBROUTINE MPI_WIN_ATTACH_I(WIN, BASE, SIZE, IERROR)
        TYPE(MPI_Win), INTENT(IN) :: WIN
        TYPE(*), DIMENSION(..), ASYNCHRONOUS :: BASE
        INTEGER, INTENT(IN) :: SIZE
        INTEGER, OPTIONAL, INTENT(OUT) :: IERROR
    END SUBROUTINE 
    SUBROUTINE MPI_WIN_ATTACH_I(WIN, BASE, SIZE, IERROR)
        TYPE(MPI_Win), INTENT(IN) :: win
        <type> :: BASE(*)
        INTEGER, INTENT(IN) ::  SIZE
        INTEGER, OPTIONAL, INTENT(OUT) :: IERROR
    END SUBROUTINE 
END INTERFACE 

MPI_Put

INTERFACE MPI_PUT
    SUBROUTINE MPI_Put_I(origin_addr, origin_count, origin_datatype, &
                       target_rank, target_disp, target_count, target_datatype, win, ierror)
        TYPE(*), DIMENSION(..), INTENT(IN), ASYNCHRONOUS :: origin_addr
        INTEGER, INTENT(IN) :: origin_count, target_rank, target_disp, target_count
        TYPE(MPI_Datatype), INTENT(IN) :: origin_datatype, target_datatype
        TYPE(MPI_Win), INTENT(IN) :: win
        INTEGER, OPTIONAL, INTENT(OUT) :: ierror
    END SUBROUTINE 
END INTERFACE 

And so forth for all other RMA communication operations where TARGET_DISP is INTEGER(KIND=MPI_ADDRESS_KIND).

Impact on Implementations

Fortran 2008 modules in MPI implementation will need to add these interfaces. One hopes the interfaces will be automatically generated, such that the effort required here is minimal.

Impact on Users

This makes RMA easy to use because applications can call RMA functions with integer literals and default integer arguments, rather than having to do explicit casting or create variables just to hold integers in the right type.

This also reduces the mental overhead of switching from C/C++ to Fortran, because no overloads are required in C/C++ due to the way integer casting works (and because scalars are passed by value rather than reference).

References and Pull Requests

No pull request. This can be automatically generated by someone who understands who the interface generation stuff works.

jeffhammond commented 2 years ago

@wgropp made the following comment on email:

I agree with Jeff here. This is a good enhancement; I believe the only reason that we hadn’t done this before is that we’ve stuck closely to the original Fortran look-and-feel - which was pre-Fortran 90. We could have done this earlier, and now that there are explicit interfaces that are only available in Fortran08, there is no reason not to do this.

wrwilliams commented 2 years ago

@jeffhammond Is the goal here purely high-level syntactic sugar that redirects to one canonical interface and does not broaden the PMPI interface? If so, there should be appropriate language somewhere that specifies this behavior (and a pointer to that language on every relevant function overload, IMO). Otherwise we force tools to err on the side of caution and account for both F08 overloads (probably unnecessarily, but if the standard doesn't specify...)

So at least for the purpose of normative text specifying how this should behave with respect to PMPI, I would like to see a pull request here.

jeffhammond commented 2 years ago

Did we add such language for large count or did tools using PMPI figure it out?

wrwilliams commented 2 years ago

Did we add such language for large count or did tools using PMPI figure it out?

We added a great deal of language for large count that specified exactly how the new interfaces, including ones based on Fortran polymorphism, mapped to non-polymorphic names. We also broadened the PMPI interface via the _c C interfaces and the _c_ts etc. F08 interfaces. I believe we've only ever taken this approach to Fortran overloads in the standard thus far. If your intent here is that we not broaden the PMPI interface, then there should be language to that effect instead. (Note that I favor not broadening PMPI here, I just think we should be explicit about it.)

jeffhammond commented 2 years ago

@RolfRabenseifner @hritzdorf could one of you look at this and let me know if there are any reasons why we should not continue working on it?

RolfRabenseifner commented 2 years ago

The proposal does not tell, wether it is for mpi_f08, mpi or mpif.h?

I expect, it is only for mpi_f08, because it is only a proposal for new software and that should use mpi_f08.

The proposal is technically wrong, because it does not include a concept for new specific procedure names, see MPI-4.0 Sect. 19.1.5, Table 19.1 on page 799 plus the "_c" specific name portions for large count versions as described in MPI-4.0 Sect. 19.2, page 839 line - p.840 line 13.

The proposal should show the several interfaces with the same Fortran interface name, as you can see in MPI-4.0 page 571 in the Fortran 2008 binding for MPI_Put.

Similar to _c and the comment !(_c), you should specify _i and !(_i) for these integer versions and a new section corresponding to section 19.2.

My personal opinion: This proposal is not needed and only complicating the implementation of MPI libraries and tools. Fortran users may know that 0_MPI_ADDRESS_KIND and integer(0,MPI_ADDRESS_KIND) is such a long integer constant with value 0. If the value of a variable should be translated into more or less bytes, then integer(my_variable,MPI_ADDRESS_KIND) is also not too complicated. To have the same amount of interfaces in C and Fortran is not too bad.

VictorEijkhout commented 2 years ago

This proposal is not needed and only complicating the implementation of MPI libraries and tools.

Maybe not needed, but “sorely wanted”.

Fortran users may know that 0_MPI_ADDRESS_KIND and integer(0,MPI_ADDRESS_KIND) is such a long integer constant with value 0.

I’d have to see what my compiler says about a literal zero, but I seem to recall that it took me quite a bit of searching the first time I ran into this. Fortran users may indeed know, but I fear they may not. So why not make life a little more logical for them?

Looking at it purely from a user perspective.

Victor.

RolfRabenseifner commented 2 years ago

It would be cheaper to add nice examples at prominent places, for example on MPI_Put.

Or/and you may add two Advice to users in 2.6.2 (mentioning the 0_MPI_ADDRESS_KIND notation) and 2.6.3 (mentioning the automatic cast for call-by-value-arguments).

You may look at my MPI course, Slide 357 "MPI–One-sided Exercise 2: additional hints" at https://www.hlrs.de/training/par-prog-ws/MPI-course-material --> mpi_3.1_rab.pdf or just https://fs.hlrs.de/projects/par/par_prog_ws/pdf/mpi_3.1_rab.pdf#page=357

jeffhammond commented 2 years ago

@RolfRabenseifner So you would rather add non-normative text to the standard to explain Fortran casting rules, which most users will never read, than add text to the standard that requires implementers to make this "just work" in the same way that C does, without them having to know anything?

How many other new user-friendly features will you reject because we can instead provide an example of how to solve the problem by writing more code?

jeffhammond commented 2 years ago

In any case, @RolfRabenseifner, it already says this feature only applies to mpi_f08.mod here:

Fortran 2008 modules in MPI implementation will need to add these interfaces.

jeffhammond commented 2 years ago

Also, the difference between 0 and integer(0,MPI_ADDRESS_KIND) is nontrivial in column-limited environments. This is actually going to increase the line count of my MPI Fortran programs, which is a pain in the ass for a bunch of reasons (e.g. multi-line grep is tedious).

RolfRabenseifner commented 2 years ago

If all MPI implementors and all tools implementors say that they are happy with implementing this enhancement, I have no problem with it, as long as this non-trivial enhancement is correctly added to the MPI standard, see my hints above. Because it is not trivial, a reference implementations would be need to be 100% sure that the amended MPI standard is consistent. And same sentence for the tools side. I'm not against.

RolfRabenseifner commented 2 years ago

Also, the difference between 0 and integer(0,MPI_ADDRESS_KIND) is nontrivial in column-limited environments. This is actually going to increase the line count of my MPI Fortran programs, which is a pain in the ass for a bunch of reasons (e.g. multi-line grep is tedious).

As long as MPI does not provide a solution, and as long as it is mainly about such long zeros, you may define a very short constant, e.g. ZL, which is of type integer(kind=MPI_ADDRESS_KIND) and with value 0.

certik commented 2 years ago

This is an example of the kinds of issues that any Fortran library is facing, in order to make the Fortran API as easy and as natural to use in Fortran.

Speaking as a Fortran user, the most natural for a foundational library such as MPI is to provide the same interface as any other "native" Fortran functions, such as sin(x). Those functions are overloaded for all real kinds, so you can simply do sin(1.0_sp) or sin(1.0_dp) (for the appropriately defined sp and dp kinds) and things will just work. Specifically, you do not do sin(real(1.0_sp, dp)).

We face the same issues with Fortran's stdlib, we handle it by auto generating such interfaces, see an example here: https://github.com/fortran-lang/stdlib/blob/6957436c1b3c99901ff5fefe0948453ad7a57c2b/src/stdlib_linalg.fypp, we autogenerate such functions for all kinds.

It's tedious from the library developer's perspective and that's unfortunate. We have started brainstorming some ways to improve the Fortran language itself to make this easier, see e.g.: https://github.com/j3-fortran/fortran_proposals/issues/128. If anybody here can help us figure out a good solution, that would be very highly appreciated.

Until then, the interfaces must be autogenerated.

As a user, that is what I would like. If users are forced to write things like integer(0,MPI_ADDRESS_KIND) that looks like the Fortran interface is not truly "native" for Fortran. Just a bare minimum to get things to work, but not designed in a way that Fortran users would like.

This brings a broader question whether MPI should design its Fortran interface to be as "native" and "natural" and to use "modern Fortran". As to me, I would say absolutely yes!

However, as shown above, it does add some extra work for the MPI implementations. If the answer is "we do not have the cycles to support a modern Fortran interface", then the answer might be "no". In that case, why not just provide a good C interface, and perhaps recommend some third party community maintained modern Fortran interface that builds on top of this C interface?

jeffhammond commented 2 years ago

@dholmes-epcc-ed-ac-uk correctly notes there is a similar inconsistency with MPI_Neighbor_alltoallw.

cniethammer commented 2 years ago

@dholmes-epcc-ed-ac-uk @jeffhammond Could you outline the inconsistency with MPI_Neighbor_alltoallw you see. Looking at the MPI 4.0 function description for MPI_Neighbor_alltoallw I cannot see a similarity. The in parameters of kind INTEGER(KIND=MPI_ADDRESS_KIND) for this function are sdispls(*) and rdispls(*). As those are arrays I would assume, that a user has to define those anyway?

Wee-Free-Scot commented 2 years ago

The principle that was (mostly) followed during the Large Count effort was: we provide exactly two overloads (separate procedures for C, specific procedures inside an interface for Fortran) -- one which has all small parameters and another that has all big parameters.

This principle, in the absence of precedent from MPI-3.1 (as before), should have meant that one of the overloads required an array of ints/INTEGERs (not MPI_AINTs/MPI_ADDRESS_KINDs) for sdispls and rdispls.

wgropp commented 2 years ago

My preference is to add interfaces that permit integer values. These don't require corresponding C routines - permitting shorter integer scalars is a convenience for users, not a new capability. They can be added following the same language used for the _c versions, though limited only to the integer scalars.

devreal commented 2 years ago

Notes from the WG meeting:

devreal commented 2 years ago

I will not be able to work on this for 4.1 due to a) a lack of cycles and b) not being sufficiently versed in Fortran to deliberate on its type system. If @VictorEijkhout wants to pick it up I'll be happy to help but I won't drive it.

jeffhammond commented 2 years ago

I can produce the text but don't know how to implement in latex.

wgropp commented 2 years ago

Send me the text and any notes on how you want it to appear.