diff --git a/docs/src/configuration.md b/docs/src/configuration.md index 125a20bcc..18aa2c8d6 100644 --- a/docs/src/configuration.md +++ b/docs/src/configuration.md @@ -177,7 +177,10 @@ that does not rely on MPItrampoline. ### Requirements MPI.jl requires a shared library installation of a C MPI library, supporting the MPI 3.0 -standard or later. The following MPI implementations should work out-of-the-box with MPI.jl: +standard or later. An implementation of MPI 4.0 or later can additionally handle messages of +more than `typemax(Cint)` elements, via the large-count entry points; see +[`MPI.API.HAS_LARGE_COUNT`](@ref). The following MPI implementations should work +out-of-the-box with MPI.jl: - [Open MPI](https://www.open-mpi.org/) - [MPICH](https://www.mpich.org/) (v3.1 or later) @@ -189,6 +192,8 @@ standard or later. The following MPI implementations should work out-of-the-box - [Fujitsu MPI](https://www.fujitsu.com/global/about/resources/publications/technicalreview/2020-03/article07.html#cap-03) - [HPE MPT/HMPT](https://support.hpe.com/hpesc/public/docDisplay?docLocale=en_US&docId=a00105727en_us) +Additionally, any MPI library implementing the MPI 5.0 **MPI ABI** is supported. + ### [Configuration](@id configure_system_binary) Run `MPIPreferences.use_system_binary()`. This will attempt to locate and to identify any available MPI implementation, and create a file called `LocalPreferences.toml` adjacent to the current `Project.toml`. diff --git a/docs/src/reference/buffers.md b/docs/src/reference/buffers.md index f6aab9c8c..d47b299a7 100644 --- a/docs/src/reference/buffers.md +++ b/docs/src/reference/buffers.md @@ -1,6 +1,15 @@ # Buffers -Buffers are used for sending and receiving data. MPI.jl provides the following buffer types: +Buffers are used for sending and receiving data. + +The buffers' element counts and displacements have the types [`MPI.API.Count`](@ref) and +[`MPI.API.Displ`](@ref). These are `MPI_Count` and `MPI_Aint` if the MPI library +provides the MPI 4.0 large-count entry points, and are `Cint` otherwise. That is, with a library +implementing MPI 4.0 or later, messages of more than `typemax(Cint)` elements work +without any special handling; with an older one, constructing such a buffer raises an +`InexactError`. (The count is never silently truncated.) + +MPI.jl provides the following buffer types: ```@docs MPI.IN_PLACE diff --git a/docs/src/reference/library.md b/docs/src/reference/library.md index 6b0e36758..5143e0cb2 100644 --- a/docs/src/reference/library.md +++ b/docs/src/reference/library.md @@ -9,6 +9,9 @@ MPI.MPI_LIBRARY_VERSION MPI.MPI_LIBRARY_VERSION_STRING ``` +Whether the library provides the MPI 4.0 large-count entry points is reported by +[`MPI.API.HAS_LARGE_COUNT`](@ref), and by [`MPI.versioninfo`](@ref). + ## Functions ```@docs diff --git a/ext/AMDGPUExt.jl b/ext/AMDGPUExt.jl index 3759c4c9c..29f94b628 100644 --- a/ext/AMDGPUExt.jl +++ b/ext/AMDGPUExt.jl @@ -4,7 +4,7 @@ using AMDGPU: AMDGPU using MPI: MPI, Buffer, Datatype function MPI.Buffer(arr::AMDGPU.ROCArray) - Buffer(arr, Cint(length(arr)), Datatype(eltype(arr))) + Buffer(arr, length(arr), Datatype(eltype(arr))) end end # AMDGPUExt diff --git a/ext/CUDAExt.jl b/ext/CUDAExt.jl index 3ea18f164..fd94bc69c 100644 --- a/ext/CUDAExt.jl +++ b/ext/CUDAExt.jl @@ -6,7 +6,7 @@ using MPI: MPI, Buffer, Datatype, mpi_ptr_type MPI.mpi_ptr_type(::CUDA.CuArray{T}) where T = CUDA.CuPtr{T} function MPI.Buffer(arr::CUDA.CuArray) - Buffer(arr, Cint(length(arr)), Datatype(eltype(arr))) + Buffer(arr, length(arr), Datatype(eltype(arr))) end end #CUDAExt diff --git a/ext/OneAPIExt.jl b/ext/OneAPIExt.jl index e3e530b0c..8b9d140e7 100644 --- a/ext/OneAPIExt.jl +++ b/ext/OneAPIExt.jl @@ -6,7 +6,7 @@ using MPI: MPI, Buffer, Datatype, mpi_ptr_type MPI.mpi_ptr_type(::oneAPI.oneArray{T}) where T = oneAPI.ZePtr{T} function MPI.Buffer(arr::oneAPI.oneArray) - Buffer(arr, Cint(length(arr)), Datatype(eltype(arr))) + Buffer(arr, length(arr), Datatype(eltype(arr))) end end # OneAPIExt diff --git a/src/buffers.jl b/src/buffers.jl index 85c7329fe..f6e4f3c8f 100644 --- a/src/buffers.jl +++ b/src/buffers.jl @@ -176,30 +176,30 @@ struct Buffer{A} """the number of elements of `datatype` in the buffer. Note that this may not correspond to the number of elements in the array if derived types are used.""" - count::Cint + count::API.Count """the [`MPI.Datatype`](@ref) stored in the buffer.""" datatype::Datatype end Buffer(buf::Buffer) = buf -Buffer(data, count::Integer, datatype::Datatype) = Buffer(data, Cint(count), datatype) +Buffer(data, count::Integer, datatype::Datatype) = Buffer(data, API.Count(count), datatype) function Buffer(arr::Array) - Buffer(arr, Cint(length(arr)), Datatype(eltype(arr))) + Buffer(arr, API.Count(length(arr)), Datatype(eltype(arr))) end function Buffer(ref::Ref) - Buffer(ref, Cint(1), Datatype(eltype(ref))) + Buffer(ref, API.Count(1), Datatype(eltype(ref))) end # SubArray function Buffer(sub::Base.FastContiguousSubArray) - Buffer(sub, Cint(length(sub)), Datatype(eltype(sub))) + Buffer(sub, API.Count(length(sub)), Datatype(eltype(sub))) end function Buffer(sub::Base.FastSubArray) datatype = Types.create_vector(length(sub), 1, sub.stride1, Datatype(eltype(sub))) Types.commit!(datatype) - Buffer(sub, Cint(1), datatype) + Buffer(sub, API.Count(1), datatype) end function Buffer(sub::SubArray{T,N,P,I,false}) where {T,N,P,I<:Tuple{Vararg{Union{Base.ScalarIndex, Base.Slice, AbstractUnitRange}}}} datatype = Types.create_subarray(size(parent(sub)), @@ -207,12 +207,12 @@ function Buffer(sub::SubArray{T,N,P,I,false}) where {T,N,P,I<:Tuple{Vararg{Union map(i -> first(i)-1, sub.indices), Datatype(eltype(sub))) Types.commit!(datatype) - Buffer(parent(sub), Cint(1), datatype) + Buffer(parent(sub), API.Count(1), datatype) end # NTuple: avoid creating a new datatype if possible function Buffer(data::Ref{NTuple{N,T}}) where {N,T} - Buffer(data, Cint(N), Datatype(T)) + Buffer(data, API.Count(N), Datatype(T)) end @@ -260,17 +260,18 @@ struct UBuffer{A} data::A """The number of elements of `datatype` in each chunk.""" - count::Cint + count::API.Count """The maximum number of chunks stored in the buffer. This is used only for - validation, and can be set to `nothing` to disable checks.""" - nchunks::Union{Nothing,Cint} + validation, and can be set to `nothing` to disable checks. It is never passed to MPI, + so it is a plain `Int` rather than an MPI count type.""" + nchunks::Union{Nothing,Int} """The [`MPI.Datatype`](@ref) stored in the buffer.""" datatype::Datatype end UBuffer(data, count::Integer, nchunks::Union{Integer, Nothing}, datatype::Datatype) = - UBuffer(data, Cint(count), nchunks isa Integer ? Cint(nchunks) : nothing, datatype) + UBuffer(data, API.Count(count), nchunks isa Integer ? Int(nchunks) : nothing, datatype) function UBuffer(arr::AbstractArray, count::Integer) @assert stride(arr, 1) == 1 @@ -312,25 +313,26 @@ struct VBuffer{A} data::A """An array containing the length of each chunk.""" - counts::Vector{Cint} + counts::Vector{API.Count} - """An array containing the (0-based) displacements of each chunk.""" - displs::Vector{Cint} + """An array containing the (0-based) displacements of each chunk. Note the element + type: MPI widens counts and displacements differently, see [`MPI.API.Displ`](@ref).""" + displs::Vector{API.Displ} """The [`MPI.Datatype`](@ref) stored in the buffer.""" datatype::Datatype end VBuffer(data, counts, displs, datatype::Datatype) = - VBuffer(data, convert(Vector{Cint}, counts), - convert(Vector{Cint}, displs), datatype) + VBuffer(data, convert(Vector{API.Count}, counts), + convert(Vector{API.Displ}, displs), datatype) VBuffer(data, counts, displs) = VBuffer(data, counts, displs, Datatype(eltype(data))) function VBuffer(arr::AbstractArray, counts) @assert stride(arr,1) == 1 - counts = convert(Vector{Cint}, counts) - displs = similar(counts) - d = zero(Cint) + counts = convert(Vector{API.Count}, counts) + displs = Vector{API.Displ}(undef, length(counts)) + d = zero(API.Displ) for i in eachindex(displs) displs[i] = d d += counts[i] @@ -339,8 +341,8 @@ function VBuffer(arr::AbstractArray, counts) VBuffer(arr, counts, displs, Datatype(eltype(arr))) end -VBuffer(::Nothing) = VBuffer(nothing, Cint[], Cint[], DATATYPE_NULL) -VBuffer(::InPlace) = VBuffer(IN_PLACE, Cint[], Cint[], DATATYPE_NULL) +VBuffer(::Nothing) = VBuffer(nothing, API.Count[], API.Displ[], DATATYPE_NULL) +VBuffer(::InPlace) = VBuffer(IN_PLACE, API.Count[], API.Displ[], DATATYPE_NULL) """ @@ -376,14 +378,14 @@ struct RBuffer{S,R} """the number of elements of `datatype` in the buffer. Note that this may not correspond to the number of elements in the array if derived types are used.""" - count::Cint + count::API.Count """the [`MPI.Datatype`](@ref) stored in the buffer.""" datatype::Datatype end RBuffer(senddata, recvdata, count::Integer, datatype::Datatype) = - RBuffer(senddata, recvdata, Cint(count), datatype) + RBuffer(senddata, recvdata, API.Count(count), datatype) function RBuffer(senddata::AbstractArray{T}, recvdata::AbstractArray{T}) where {T} count = length(senddata) diff --git a/src/collective.jl b/src/collective.jl index 98d84ba13..9236ae1b0 100644 --- a/src/collective.jl +++ b/src/collective.jl @@ -53,7 +53,7 @@ Bcast!(buf, comm::Comm; root::Integer=Cint(0)) = function Bcast!(buf::Buffer, root::Integer, comm::Comm) # int MPI_Bcast(void* buffer, int count, MPI_Datatype datatype, int root, # MPI_Comm comm) - API.MPI_Bcast(buf.data, buf.count, buf.datatype, root, comm) + API.MPI_Bcast_c(buf.data, buf.count, buf.datatype, root, comm) return buf.data end function Bcast!(data, root::Integer, comm::Comm) @@ -87,7 +87,11 @@ bcast(obj, comm::Comm; root::Integer=Cint(0)) = bcast(obj, root, comm) function bcast(obj, root::Integer, comm::Comm) isroot = Comm_rank(comm) == root - count = Ref{Cint}() + # The length is itself broadcast as MPI payload, so its width is part of the wire + # format: `Int64` rather than an MPI count type, so that it does not depend on + # whether the library provides the large-count entry points. It was `Cint`, which + # capped a serialized object at 2 GiB. + count = Ref{Int64}() if isroot buf = MPI.serialize(obj) count[] = length(buf) @@ -140,8 +144,8 @@ function Scatter!(sendbuf::UBuffer, recvbuf::Buffer, root::Integer, comm::Comm) # int MPI_Scatter(const void* sendbuf, int sendcount, MPI_Datatype sendtype, # void* recvbuf, int recvcount, MPI_Datatype recvtype, int root, # MPI_Comm comm) - API.MPI_Scatter(sendbuf.data, sendbuf.count, sendbuf.datatype, - recvbuf.data, recvbuf.count, recvbuf.datatype, root, comm) + API.MPI_Scatter_c(sendbuf.data, sendbuf.count, sendbuf.datatype, + recvbuf.data, recvbuf.count, recvbuf.datatype, root, comm) return recvbuf.data end Scatter!(sendbuf::UBuffer, recvbuf, root::Integer, comm::Comm) = @@ -252,9 +256,9 @@ function Scatterv!(sendbuf::VBuffer, recvbuf::Buffer, root::Integer, comm::Comm) # int MPI_Scatterv(const void* sendbuf, const int sendcounts[], # const int displs[], MPI_Datatype sendtype, void* recvbuf, # int recvcount, MPI_Datatype recvtype, int root, MPI_Comm comm) - API.MPI_Scatterv(sendbuf.data, sendbuf.counts, sendbuf.displs, sendbuf.datatype, - recvbuf.data, recvbuf.count, recvbuf.datatype, - root, comm) + API.MPI_Scatterv_c(sendbuf.data, sendbuf.counts, sendbuf.displs, sendbuf.datatype, + recvbuf.data, recvbuf.count, recvbuf.datatype, + root, comm) return recvbuf.data end Scatterv!(sendbuf::VBuffer, recvbuf, root::Integer, comm::Comm) = @@ -305,8 +309,8 @@ function Gather!(sendbuf::Buffer, recvbuf::UBuffer, root::Integer, comm::Comm) # int MPI_Gather(const void* sendbuf, int sendcount, MPI_Datatype sendtype, # void* recvbuf, int recvcount, MPI_Datatype recvtype, int root, # MPI_Comm comm) - API.MPI_Gather(sendbuf.data, sendbuf.count, sendbuf.datatype, - recvbuf.data, recvbuf.count, recvbuf.datatype, root, comm) + API.MPI_Gather_c(sendbuf.data, sendbuf.count, sendbuf.datatype, + recvbuf.data, recvbuf.count, recvbuf.datatype, root, comm) return recvbuf.data end Gather!(sendbuf, recvbuf::UBuffer, root::Integer, comm::Comm) = @@ -417,8 +421,8 @@ function Gatherv!(sendbuf::Buffer, recvbuf::VBuffer, root::Integer, comm::Comm) # int MPI_Gatherv(const void* sendbuf, int sendcount, MPI_Datatype sendtype, # void* recvbuf, const int recvcounts[], const int displs[], # MPI_Datatype recvtype, int root, MPI_Comm comm) - API.MPI_Gatherv(sendbuf.data, sendbuf.count, sendbuf.datatype, - recvbuf.data, recvbuf.counts, recvbuf.displs, recvbuf.datatype, root, comm) + API.MPI_Gatherv_c(sendbuf.data, sendbuf.count, sendbuf.datatype, + recvbuf.data, recvbuf.counts, recvbuf.displs, recvbuf.datatype, root, comm) return recvbuf.data end Gatherv!(sendbuf, recvbuf::VBuffer, root::Integer, comm::Comm) = @@ -460,8 +464,8 @@ function Allgather!(sendbuf::Buffer, recvbuf::UBuffer, comm::Comm) # int MPI_Allgather(const void* sendbuf, int sendcount, # MPI_Datatype sendtype, void* recvbuf, int recvcount, # MPI_Datatype recvtype, MPI_Comm comm) - API.MPI_Allgather(sendbuf.data, sendbuf.count, sendbuf.datatype, - recvbuf.data, recvbuf.count, recvbuf.datatype, comm) + API.MPI_Allgather_c(sendbuf.data, sendbuf.count, sendbuf.datatype, + recvbuf.data, recvbuf.count, recvbuf.datatype, comm) return recvbuf.data end Allgather!(sendbuf, recvbuf::UBuffer, comm::Comm) = @@ -522,9 +526,9 @@ function Allgatherv!(sendbuf::Buffer, recvbuf::VBuffer, comm::Comm) # int MPI_Allgatherv(const void* sendbuf, int sendcount, # MPI_Datatype sendtype, void* recvbuf, const int recvcounts[], # const int displs[], MPI_Datatype recvtype, MPI_Comm comm) - API.MPI_Allgatherv(sendbuf.data, sendbuf.count, sendbuf.datatype, - recvbuf.data, recvbuf.counts, recvbuf.displs, recvbuf.datatype, - comm) + API.MPI_Allgatherv_c(sendbuf.data, sendbuf.count, sendbuf.datatype, + recvbuf.data, recvbuf.counts, recvbuf.displs, recvbuf.datatype, + comm) return recvbuf.data end Allgatherv!(sendbuf, recvbuf::VBuffer, comm::Comm) = @@ -569,9 +573,9 @@ function Alltoall!(sendbuf::UBuffer, recvbuf::UBuffer, comm::Comm) # int MPI_Alltoall(const void* sendbuf, int sendcount, MPI_Datatype sendtype, # void* recvbuf, int recvcount, MPI_Datatype recvtype, # MPI_Comm comm) - API.MPI_Alltoall(sendbuf.data, sendbuf.count, sendbuf.datatype, - recvbuf.data, recvbuf.count, recvbuf.datatype, - comm) + API.MPI_Alltoall_c(sendbuf.data, sendbuf.count, sendbuf.datatype, + recvbuf.data, recvbuf.count, recvbuf.datatype, + comm) return recvbuf.data end Alltoall!(sendbuf::InPlace, recvbuf::UBuffer, comm::Comm) = @@ -628,9 +632,9 @@ function Alltoallv!(sendbuf::VBuffer, recvbuf::VBuffer, comm::Comm) # const int sdispls[], MPI_Datatype sendtype, void* recvbuf, # const int recvcounts[], const int rdispls[], # MPI_Datatype recvtype, MPI_Comm comm) - API.MPI_Alltoallv(sendbuf.data, sendbuf.counts, sendbuf.displs, sendbuf.datatype, - recvbuf.data, recvbuf.counts, recvbuf.displs, recvbuf.datatype, - comm) + API.MPI_Alltoallv_c(sendbuf.data, sendbuf.counts, sendbuf.displs, sendbuf.datatype, + recvbuf.data, recvbuf.counts, recvbuf.displs, recvbuf.datatype, + comm) return recvbuf.data end @@ -672,7 +676,7 @@ Reduce!(sendbuf, recvbuf, op, comm::Comm; root::Integer=Cint(0)) = function Reduce!(rbuf::RBuffer, op::Union{Op,MPI_Op}, root::Integer, comm::Comm) # int MPI_Reduce(const void* sendbuf, void* recvbuf, int count, # MPI_Datatype datatype, MPI_Op op, int root, MPI_Comm comm) - API.MPI_Reduce(rbuf.senddata, rbuf.recvdata, rbuf.count, rbuf.datatype, op, root, comm) + API.MPI_Reduce_c(rbuf.senddata, rbuf.recvdata, rbuf.count, rbuf.datatype, op, root, comm) return rbuf.recvdata end @@ -753,7 +757,7 @@ $(_doc_external("MPI_Allreduce")) function Allreduce!(rbuf::RBuffer, op::Union{Op,MPI_Op}, comm::Comm) # int MPI_Allreduce(const void* sendbuf, void* recvbuf, int count, # MPI_Datatype datatype, MPI_Op op, MPI_Comm comm) - API.MPI_Allreduce(rbuf.senddata, rbuf.recvdata, rbuf.count, rbuf.datatype, op, comm) + API.MPI_Allreduce_c(rbuf.senddata, rbuf.recvdata, rbuf.count, rbuf.datatype, op, comm) rbuf.recvdata end Allreduce!(rbuf::RBuffer, op, comm::Comm) = @@ -810,7 +814,7 @@ $(_doc_external("MPI_Scan")) function Scan!(rbuf::RBuffer, op::Union{Op,MPI_Op}, comm::Comm) # int MPI_Scan(const void* sendbuf, void* recvbuf, int count, # MPI_Datatype datatype, MPI_Op op, MPI_Comm comm) - API.MPI_Scan(rbuf.senddata, rbuf.recvdata, rbuf.count, rbuf.datatype, op, comm) + API.MPI_Scan_c(rbuf.senddata, rbuf.recvdata, rbuf.count, rbuf.datatype, op, comm) rbuf.recvdata end Scan!(rbuf::RBuffer, op, comm::Comm) = Scan!(rbuf, Op(op, eltype(rbuf)), comm) @@ -867,7 +871,7 @@ $(_doc_external("MPI_Exscan")) function Exscan!(rbuf::RBuffer, op::Union{Op,MPI_Op}, comm::Comm) # int MPI_Exscan(const void* sendbuf, void* recvbuf, int count, # MPI_Datatype datatype, MPI_Op op, MPI_Comm comm) - API.MPI_Exscan(rbuf.senddata, rbuf.recvdata, rbuf.count, rbuf.datatype, op, comm) + API.MPI_Exscan_c(rbuf.senddata, rbuf.recvdata, rbuf.count, rbuf.datatype, op, comm) rbuf.recvdata end Exscan!(rbuf::RBuffer, op, comm::Comm) = Exscan!(rbuf, Op(op, eltype(rbuf)), comm) @@ -910,9 +914,9 @@ $(_doc_external("MPI_Neighbor_alltoall")) function Neighbor_alltoall!(sendbuf::UBuffer, recvbuf::UBuffer, graph_comm::Comm) # int MPI_Neighbor_alltoall(const void *sendbuf, int sendcount, MPI_Datatype sendtype, void *recvbuf, # int recvcount, MPI_Datatype recvtype, MPI_Comm graph_comm) - API.MPI_Neighbor_alltoall(sendbuf.data, sendbuf.count, sendbuf.datatype, - recvbuf.data, recvbuf.count, recvbuf.datatype, - graph_comm) + API.MPI_Neighbor_alltoall_c(sendbuf.data, sendbuf.count, sendbuf.datatype, + recvbuf.data, recvbuf.count, recvbuf.datatype, + graph_comm) return recvbuf.data end @@ -941,9 +945,9 @@ function Neighbor_alltoallv!(sendbuf::VBuffer, recvbuf::VBuffer, graph_comm::Com # const int sdispls[], MPI_Datatype sendtype, void* recvbuf, # const int recvcounts[], const int rdispls[], # MPI_Datatype recvtype, MPI_Comm comm) - API.MPI_Neighbor_alltoallv(sendbuf.data, sendbuf.counts, sendbuf.displs, sendbuf.datatype, - recvbuf.data, recvbuf.counts, recvbuf.displs, recvbuf.datatype, - graph_comm) + API.MPI_Neighbor_alltoallv_c(sendbuf.data, sendbuf.counts, sendbuf.displs, sendbuf.datatype, + recvbuf.data, recvbuf.counts, recvbuf.displs, recvbuf.datatype, + graph_comm) return recvbuf.data end @@ -961,8 +965,8 @@ function Neighbor_allgather!(sendbuf::Buffer, recvbuf::UBuffer, graph_comm::Comm # int MPI_Neighbor_allgather(const void* sendbuf, int sendcount, # MPI_Datatype sendtype, void* recvbuf, int recvcount, # MPI_Datatype recvtype, MPI_Comm comm) - API.MPI_Neighbor_allgather(sendbuf.data, sendbuf.count, sendbuf.datatype, - recvbuf.data, recvbuf.count, recvbuf.datatype, graph_comm) + API.MPI_Neighbor_allgather_c(sendbuf.data, sendbuf.count, sendbuf.datatype, + recvbuf.data, recvbuf.count, recvbuf.datatype, graph_comm) return recvbuf.data end Neighbor_allgather!(sendbuf, recvbuf::UBuffer, graph_comm::Comm) = @@ -994,8 +998,8 @@ function Neighbor_allgatherv!(sendbuf::Buffer, recvbuf::VBuffer, graph_comm::Com # int MPI_Neighbor_allgatherv(const void *sendbuf, int sendcount, MPI_Datatype sendtype, # void *recvbuf, const int recvcounts[], const int displs[], # MPI_Datatype recvtype, MPI_Comm comm) - API.MPI_Neighbor_allgatherv(sendbuf.data, sendbuf.count, sendbuf.datatype, - recvbuf.data, recvbuf.counts, recvbuf.displs, recvbuf.datatype, graph_comm) + API.MPI_Neighbor_allgatherv_c(sendbuf.data, sendbuf.count, sendbuf.datatype, + recvbuf.data, recvbuf.counts, recvbuf.displs, recvbuf.datatype, graph_comm) return recvbuf.data end Neighbor_allgatherv!(sendbuf, recvbuf::VBuffer, graph_comm::Comm) = diff --git a/src/datatypes.jl b/src/datatypes.jl index 6540dafb5..2deed1258 100644 --- a/src/datatypes.jl +++ b/src/datatypes.jl @@ -213,8 +213,8 @@ import MPI import MPI: API, _doc_external, Datatype, MPI_Datatype, MPI_Aint, free function size(dt::Datatype) - dtsize = Ref{Cint}() - API.MPI_Type_size(dt, dtsize) + dtsize = Ref{API.Count}() + API.MPI_Type_size_c(dt, dtsize) return Int(dtsize[]) end @@ -227,12 +227,14 @@ Gets the lowerbound `lb` and the extent `extent` in bytes. $(_doc_external("MPI_Type_get_extent")) """ function extent(dt::Datatype) - lb = Ref{MPI_Aint}() - extent = Ref{MPI_Aint}() - # int MPI_Type_get_extent(MPI_Datatype datatype, MPI_Aint *lb, - # MPI_Aint *extent) - API.MPI_Type_get_extent(dt, lb, extent) - return lb[], extent[] + lb = Ref{API.TypeDispl}() + extent = Ref{API.TypeDispl}() + # int MPI_Type_get_extent_c(MPI_Datatype datatype, MPI_Count *lb, + # MPI_Count *extent) + API.MPI_Type_get_extent_c(dt, lb, extent) + # `Int` rather than the raw type, so that the return type does not depend on which + # entry point was used + return Int(lb[]), Int(extent[]) end """ @@ -251,7 +253,7 @@ function create_contiguous(count::Integer, oldtype::Datatype) end function create_contiguous!(newtype::Datatype, count::Integer, oldtype::Datatype) - API.MPI_Type_contiguous(count, oldtype, newtype) + API.MPI_Type_contiguous_c(count, oldtype, newtype) return newtype end @@ -295,7 +297,7 @@ end function create_vector!(newtype::Datatype, count::Integer, blocklength::Integer, stride::Integer, oldtype::Datatype) # int MPI_Type_vector(int count, int blocklength, int stride, # MPI_Datatype oldtype, MPI_Datatype *newtype) - API.MPI_Type_vector(count, blocklength, stride, oldtype, newtype) + API.MPI_Type_vector_c(count, blocklength, stride, oldtype, newtype) return newtype end @@ -322,9 +324,12 @@ function create_hvector(count::Integer, blocklength::Integer, stride::Integer, o finalizer(free, create_hvector!(Datatype(), count, blocklength, stride, oldtype)) end function create_hvector!(newtype::Datatype, count::Integer, blocklength::Integer, stride::Integer, oldtype::Datatype) - # int MPI_Type_create_hvector(int count, int blocklength, MPI_Aint stride, - # MPI_Datatype oldtype, MPI_Datatype *newtype) - API.MPI_Type_create_hvector(count, blocklength, MPI_Aint(stride), oldtype, newtype) + # int MPI_Type_create_hvector_c(MPI_Count count, MPI_Count blocklength, + # MPI_Count stride, MPI_Datatype oldtype, + # MPI_Datatype *newtype) + # `stride` is left unconverted so that `ccall` widens it to whichever of `MPI_Count` + # and `MPI_Aint` the entry point in use expects. + API.MPI_Type_create_hvector_c(count, blocklength, stride, oldtype, newtype) return newtype end @@ -352,9 +357,9 @@ end function create_subarray!(newtype::Datatype, sizes, subsizes, offset, oldtype::Datatype; rowmajor=false) @assert (N = length(sizes)) == length(subsizes) == length(offset) - sizes = sizes isa Vector{Cint} ? sizes : Cint[s for s in sizes] - subsizes = subsizes isa Vector{Cint} ? subsizes : Cint[s for s in subsizes] - offset = offset isa Vector{Cint} ? offset : Cint[s for s in offset] + sizes = sizes isa Vector{API.Count} ? sizes : API.Count[s for s in sizes] + subsizes = subsizes isa Vector{API.Count} ? subsizes : API.Count[s for s in subsizes] + offset = offset isa Vector{API.Count} ? offset : API.Count[s for s in offset] # int MPI_Type_create_subarray(int ndims, # const int array_of_sizes[], # const int array_of_subsizes[], @@ -362,9 +367,9 @@ function create_subarray!(newtype::Datatype, sizes, subsizes, offset, oldtype::D # int order, # MPI_Datatype oldtype, # MPI_Datatype *newtype) - API.MPI_Type_create_subarray(N, sizes, subsizes, offset, - rowmajor ? MPI.API.MPI_ORDER_C[] : MPI.API.MPI_ORDER_FORTRAN[], - oldtype, newtype) + API.MPI_Type_create_subarray_c(N, sizes, subsizes, offset, + rowmajor ? MPI.API.MPI_ORDER_C[] : MPI.API.MPI_ORDER_FORTRAN[], + oldtype, newtype) return newtype end @@ -384,8 +389,10 @@ function create_struct(blocklengths, displacements, types) end function create_struct!(newtype::Datatype, blocklengths, displacements, types) @assert (N = length(blocklengths)) == length(displacements) == length(types) - blocklengths = blocklengths isa Vector{Cint} ? blocklengths : Cint[s for s in blocklengths] - displacements = displacements isa Vector{MPI_Aint} ? displacements : MPI_Aint[s for s in displacements] + blocklengths = blocklengths isa Vector{API.Count} ? blocklengths : API.Count[s for s in blocklengths] + # note the type: MPI_Type_create_struct_c widens byte displacements to MPI_Count, + # unlike the "v" collectives which widen theirs only to MPI_Aint + displacements = displacements isa Vector{API.TypeDispl} ? displacements : API.TypeDispl[s for s in displacements] # int MPI_Type_create_struct(int count, # const int array_of_blocklengths[], # const MPI_Aint array_of_displacements[], @@ -393,7 +400,7 @@ function create_struct!(newtype::Datatype, blocklengths, displacements, types) # MPI_Datatype *newtype) GC.@preserve types begin mpi_types = [t.val for t in types] - API.MPI_Type_create_struct(N, blocklengths, displacements, mpi_types, newtype) + API.MPI_Type_create_struct_c(N, blocklengths, displacements, mpi_types, newtype) end return newtype end @@ -422,7 +429,7 @@ end function create_resized!(newtype::Datatype, oldtype::Datatype, lb::Integer, extent::Integer) # int MPI_Type_create_resized(MPI_Datatype oldtype, MPI_Aint lb, # MPI_Aint extent, MPI_Datatype *newtype) - API.MPI_Type_create_resized(oldtype, lb, extent, newtype) + API.MPI_Type_create_resized_c(oldtype, lb, extent, newtype) return newtype end @@ -458,8 +465,8 @@ end function create!(newtype::Datatype, ::Type{T}) where {T} isbitstype(T) || throw(ArgumentError("Type must be isbitstype")) - blocklengths = Cint[] - displacements = MPI_Aint[] + blocklengths = API.Count[] + displacements = API.TypeDispl[] types = Datatype[] if isprimitivetype(T) diff --git a/src/implementations.jl b/src/implementations.jl index 625fdbe84..07d4b39b3 100644 --- a/src/implementations.jl +++ b/src/implementations.jl @@ -114,6 +114,7 @@ function versioninfo(io::IO=stdout) println(io, " libmpi: ", API.libmpi) println(io, " libmpi dlpath: ", Libdl.dlpath(API.libmpi)) println(io, " MPI version: ", Get_version()) + println(io, " Large counts: ", API.HAS_LARGE_COUNT ? "yes (MPI_Count = $(API.Count))" : "no (counts are $(API.Count))") println(io, " Library version: ") for line in split(Get_library_version(), '\n') println(io, " ", line) diff --git a/src/io.jl b/src/io.jl index 826fd626e..03cf86528 100644 --- a/src/io.jl +++ b/src/io.jl @@ -164,7 +164,7 @@ function read!(file::FileHandle, buf::Buffer) stat_ref = Ref(MPI.STATUS_ZERO) # int MPI_File_read(MPI_File fh, void *buf, # int count, MPI_Datatype datatype, MPI_Status *status) - API.MPI_File_read(file, buf.data, buf.count, buf.datatype, stat_ref) + API.MPI_File_read_c(file, buf.data, buf.count, buf.datatype, stat_ref) return stat_ref[] end read!(file::FileHandle, data) = read!(file, Buffer(data)) @@ -187,7 +187,7 @@ function read_all!(file::FileHandle, buf::Buffer) stat_ref = Ref(MPI.STATUS_ZERO) # int MPI_File_read_all(MPI_File fh, void *buf, # int count, MPI_Datatype datatype, MPI_Status *status) - API.MPI_File_read_all(file, buf.data, buf.count, buf.datatype, stat_ref) + API.MPI_File_read_all_c(file, buf.data, buf.count, buf.datatype, stat_ref) return stat_ref[] end read_all!(file::FileHandle, data) = read_all!(file, Buffer(data)) @@ -209,7 +209,7 @@ function write(file::FileHandle, buf::Buffer) stat_ref = Ref(MPI.STATUS_ZERO) # int MPI_File_write(MPI_File fh, const void *buf, # int count, MPI_Datatype datatype, MPI_Status *status) - API.MPI_File_write(file, buf.data, buf.count, buf.datatype, stat_ref) + API.MPI_File_write_c(file, buf.data, buf.count, buf.datatype, stat_ref) return stat_ref[] end write(file::FileHandle, data) = write(file, Buffer_send(data)) @@ -232,7 +232,7 @@ function write_all(file::FileHandle, buf::Buffer) stat_ref = Ref(MPI.STATUS_ZERO) # int MPI_File_write_all(MPI_File fh, const void *buf, # int count, MPI_Datatype datatype, MPI_Status *status) - API.MPI_File_write_all(file, buf.data, buf.count, buf.datatype, stat_ref) + API.MPI_File_write_all_c(file, buf.data, buf.count, buf.datatype, stat_ref) return stat_ref[] end write_all(file::FileHandle, data) = write_all(file, Buffer_send(data)) @@ -255,7 +255,7 @@ function read_at!(file::FileHandle, offset::Integer, buf::Buffer) stat_ref = Ref(MPI.STATUS_ZERO) # int MPI_File_read_at(MPI_File fh, MPI_Offset offset, void *buf, int count, # MPI_Datatype datatype, MPI_Status *status) - API.MPI_File_read_at(file, offset, buf.data, buf.count, buf.datatype, stat_ref) + API.MPI_File_read_at_c(file, offset, buf.data, buf.count, buf.datatype, stat_ref) return stat_ref[] end read_at!(file::FileHandle, offset::Integer, data) = read_at!(file, offset, Buffer(data)) @@ -278,7 +278,7 @@ function read_at_all!(file::FileHandle, offset::Integer, buf::Buffer) # int MPI_File_read_at_all(MPI_File fh, MPI_Offset offset, void *buf, # int count, MPI_Datatype datatype, MPI_Status *status) - API.MPI_File_read_at_all(file, offset, buf.data, buf.count, buf.datatype, stat_ref) + API.MPI_File_read_at_all_c(file, offset, buf.data, buf.count, buf.datatype, stat_ref) return stat_ref[] end read_at_all!(file::FileHandle, offset::Integer, data) = read_at_all!(file, offset, Buffer(data)) @@ -299,7 +299,7 @@ function write_at(file::FileHandle, offset::Integer, buf::Buffer) stat_ref = Ref(MPI.STATUS_ZERO) # int MPI_File_write_at(MPI_File fh, MPI_Offset offset, const void *buf, # int count, MPI_Datatype datatype, MPI_Status *status) - API.MPI_File_write_at(file, offset, buf.data, buf.count, buf.datatype, stat_ref) + API.MPI_File_write_at_c(file, offset, buf.data, buf.count, buf.datatype, stat_ref) return stat_ref[] end write_at(file::FileHandle, offset::Integer, data) = write_at(file, offset, Buffer_send(data)) @@ -321,7 +321,7 @@ function write_at_all(file::FileHandle, offset::Integer, buf::Buffer) stat_ref = Ref(MPI.STATUS_ZERO) # int MPI_File_write_at_all(MPI_File fh, MPI_Offset offset, const void *buf, # int count, MPI_Datatype datatype, MPI_Status *status) - API.MPI_File_write_at_all(file, offset, buf.data, buf.count, buf.datatype, stat_ref) + API.MPI_File_write_at_all_c(file, offset, buf.data, buf.count, buf.datatype, stat_ref) return stat_ref[] end write_at_all(file::FileHandle, offset::Integer, data) = write_at_all(file, offset, Buffer_send(data)) @@ -344,7 +344,7 @@ function read_shared!(file::FileHandle, buf::Buffer) stat_ref = Ref(MPI.STATUS_ZERO) # int MPI_File_read_shared(MPI_File fh, void *buf, int count, # MPI_Datatype datatype, MPI_Status *status) - API.MPI_File_read_shared(file, buf.data, buf.count, buf.datatype, stat_ref) + API.MPI_File_read_shared_c(file, buf.data, buf.count, buf.datatype, stat_ref) return stat_ref[] end read_shared!(file::FileHandle, data) = read_shared!(file, Buffer(data)) @@ -365,7 +365,7 @@ function write_shared(file::FileHandle, buf::Buffer) stat_ref = Ref(MPI.STATUS_ZERO) # int MPI_File_write_shared(MPI_File fh, const void *buf, int count, # MPI_Datatype datatype, MPI_Status *status) - API.MPI_File_write_shared(file, buf.data, buf.count, buf.datatype, stat_ref) + API.MPI_File_write_shared_c(file, buf.data, buf.count, buf.datatype, stat_ref) return stat_ref[] end write_shared(file::FileHandle, buf) = write_shared(file, Buffer_send(buf)) @@ -389,7 +389,7 @@ function read_ordered!(file::FileHandle, buf::Buffer) stat_ref = Ref(MPI.STATUS_ZERO) # int MPI_File_read_ordered(MPI_File fh, void *buf, int count, # MPI_Datatype datatype, MPI_Status *status) - API.MPI_File_read_ordered(file, buf.data, buf.count, buf.datatype, stat_ref) + API.MPI_File_read_ordered_c(file, buf.data, buf.count, buf.datatype, stat_ref) return stat_ref[] end read_ordered!(file::FileHandle, data) = read_ordered!(file, Buffer(data)) @@ -412,7 +412,7 @@ function write_ordered(file::FileHandle, buf::Buffer) stat_ref = Ref(MPI.STATUS_ZERO) # int MPI_File_write_ordered(MPI_File fh, const void *buf, int count, # MPI_Datatype datatype, MPI_Status *status) - API.MPI_File_write_ordered(file, buf.data, buf.count, buf.datatype, stat_ref) + API.MPI_File_write_ordered_c(file, buf.data, buf.count, buf.datatype, stat_ref) return stat_ref[] end write_ordered(file::FileHandle, buf) = write_ordered(file, Buffer_send(buf)) diff --git a/src/nonblocking.jl b/src/nonblocking.jl index 64bddd422..d3ca8b9ae 100644 --- a/src/nonblocking.jl +++ b/src/nonblocking.jl @@ -423,8 +423,8 @@ parameter, or the number of bytes received is not a whole multiple of the size o $(_doc_external("MPI_Get_count")) """ function Get_count(stat::Status, datatype::Datatype) - count = Ref{Cint}() - API.MPI_Get_count(Ref(stat), datatype, count) + count = Ref{API.Count}() + API.MPI_Get_count_c(Ref(stat), datatype, count) # `MPI_UNDEFINED` is not a count; return `nothing` rather than let the # sentinel escape as an ordinary integer (as `Waitany` and friends do). count[] == API.MPI_UNDEFINED[] && return nothing diff --git a/src/onesided.jl b/src/onesided.jl index 74337e4fb..47df2321a 100644 --- a/src/onesided.jl +++ b/src/onesided.jl @@ -56,7 +56,7 @@ function Win_create(base, size::Integer, disp_unit::Integer, comm::Comm; infokws win = Win() # int MPI_Win_create(void *base, MPI_Aint size, int disp_unit, MPI_Info info, # MPI_Comm comm, MPI_Win *win) - API.MPI_Win_create(base, size, disp_unit, Info(infokws...), comm, win) + API.MPI_Win_create_c(base, size, disp_unit, Info(infokws...), comm, win) win.object = base finalizer(free, win) return win @@ -92,7 +92,7 @@ function Win_allocate_shared(::Type{Ptr{T}}, len::Integer, comm::Comm; kwargs... out_baseptr = Ref{Ptr{T}}() # int MPI_Win_allocate_shared(MPI_Aint size, int disp_unit, MPI_Info info, # MPI_Comm comm, void *baseptr, MPI_Win *win) - API.MPI_Win_allocate_shared(len*sizeof(T), sizeof(T), Info(kwargs...), comm, out_baseptr, win) + API.MPI_Win_allocate_shared_c(len*sizeof(T), sizeof(T), Info(kwargs...), comm, out_baseptr, win) finalizer(free, win) return win, out_baseptr[] end @@ -118,13 +118,17 @@ Win_shared_query(::Type{Array{T}}, dims, win::Win; rank) where {T} = Win_shared_query(Array{T}, dims, win, rank) function Win_shared_query(::Type{Ptr{T}}, win::Win, owner_rank::Integer) where T - out_len = Ref{Cptrdiff_t}() - out_sizeT = Ref{Cint}() + out_len = Ref{MPI_Aint}() + # `disp_unit` is an `int` in MPI_Win_shared_query but an `MPI_Aint` in the large-count + # MPI_Win_shared_query_c -- the same widening as the "v" collectives' displacements, + # hence `API.Displ`. It has to match the entry point actually in use: a `Ref` of the + # wrong width would have MPI fill only part of it. + out_sizeT = Ref{API.Displ}() out_baseptr = Ref{Ptr{T}}() - # int MPI_Win_shared_query(MPI_Win win, int rank, MPI_Aint *size, - # int *disp_unit, void *baseptr) - API.MPI_Win_shared_query(win, owner_rank, out_len, out_sizeT, out_baseptr) - out_len[], out_sizeT[], out_baseptr[] + # int MPI_Win_shared_query_c(MPI_Win win, int rank, MPI_Aint *size, + # MPI_Aint *disp_unit, void *baseptr) + API.MPI_Win_shared_query_c(win, owner_rank, out_len, out_sizeT, out_baseptr) + Int(out_len[]), Int(out_sizeT[]), out_baseptr[] end function Win_shared_query(::Type{Array{T}}, win::Win, owner_rank::Integer) where T len, sizeT, ptr = Win_shared_query(Ptr{T}, win, owner_rank) @@ -259,8 +263,8 @@ function Get!(origin_buf::Buffer, target_rank::Integer, target_disp::Integer, wi # MPI_Datatype origin_datatype, int target_rank, # MPI_Aint target_disp, int target_count, # MPI_Datatype target_datatype, MPI_Win win) - API.MPI_Get(origin_buf.data, origin_buf.count, origin_buf.datatype, - target_rank, Cptrdiff_t(target_disp), origin_buf.count, origin_buf.datatype, win) + API.MPI_Get_c(origin_buf.data, origin_buf.count, origin_buf.datatype, + target_rank, Cptrdiff_t(target_disp), origin_buf.count, origin_buf.datatype, win) end Get!(origin, target_rank::Integer, target_disp::Integer, win::Win) = Get!(Buffer(origin), target_rank, target_disp, win) @@ -285,8 +289,8 @@ function Put!(origin_buf::Buffer, target_rank::Integer, target_disp::Integer, wi # MPI_Datatype origin_datatype, int target_rank, # MPI_Aint target_disp, int target_count, # MPI_Datatype target_datatype, MPI_Win win) - API.MPI_Put(origin_buf.data, origin_buf.count, origin_buf.datatype, - target_rank, Cptrdiff_t(target_disp), origin_buf.count, origin_buf.datatype, win) + API.MPI_Put_c(origin_buf.data, origin_buf.count, origin_buf.datatype, + target_rank, Cptrdiff_t(target_disp), origin_buf.count, origin_buf.datatype, win) end Put!(origin, target_rank::Integer, target_disp::Integer, win::Win) = Put!(Buffer_send(origin), target_rank, target_disp, win) @@ -323,8 +327,8 @@ function Accumulate!(origin_buf::Buffer, target_rank::Integer, target_disp::Inte # MPI_Datatype origin_datatype, int target_rank, # MPI_Aint target_disp, int target_count, # MPI_Datatype target_datatype, MPI_Op op, MPI_Win win) - API.MPI_Accumulate(origin_buf.data, origin_buf.count, origin_buf.datatype, - target_rank, Cptrdiff_t(target_disp), origin_buf.count, origin_buf.datatype, op, win) + API.MPI_Accumulate_c(origin_buf.data, origin_buf.count, origin_buf.datatype, + target_rank, Cptrdiff_t(target_disp), origin_buf.count, origin_buf.datatype, op, win) end Accumulate!(origin, target_rank::Integer, target_disp::Integer, op::Op, win::Win) = Accumulate!(Buffer_send(origin), target_rank, target_disp, op, win) @@ -351,9 +355,9 @@ function Get_accumulate!(origin_buf::Buffer, result_buf::Buffer, target_rank::In # int result_count, MPI_Datatype result_datatype, # int target_rank, MPI_Aint target_disp, int target_count, # MPI_Datatype target_datatype, MPI_Op op, MPI_Win win) - API.MPI_Get_accumulate(origin_buf.data, origin_buf.count, origin_buf.datatype, - result_buf.data, result_buf.count, result_buf.datatype, - target_rank, Cptrdiff_t(target_disp), origin_buf.count, origin_buf.datatype, op, win) + API.MPI_Get_accumulate_c(origin_buf.data, origin_buf.count, origin_buf.datatype, + result_buf.data, result_buf.count, result_buf.datatype, + target_rank, Cptrdiff_t(target_disp), origin_buf.count, origin_buf.datatype, op, win) end Get_accumulate!(origin, result, target_rank::Integer, target_disp::Integer, op::Op, win::Win) = Get_accumulate!(Buffer_send(origin), Buffer(result), target_rank, target_disp, op, win) diff --git a/src/pointtopoint.jl b/src/pointtopoint.jl index 110ac5f9f..a8b8ad29a 100644 --- a/src/pointtopoint.jl +++ b/src/pointtopoint.jl @@ -20,7 +20,7 @@ Send(data, comm::Comm; dest::Integer, tag::Integer=Cint(0)) = function Send(buf::Buffer, dest::Integer, tag::Integer, comm::Comm) # int MPI_Send(const void* buf, int count, MPI_Datatype datatype, int dest, # int tag, MPI_Comm comm) - API.MPI_Send(buf.data, buf.count, buf.datatype, dest, tag, comm) + API.MPI_Send_c(buf.data, buf.count, buf.datatype, dest, tag, comm) return nothing end Send(arr::Union{Ref,AbstractArray}, dest::Integer, tag::Integer, comm::Comm) = @@ -63,7 +63,7 @@ function Isend(buf::Buffer, dest::Integer, tag::Integer, comm::Comm, req::Abstra @assert isnull(req) # int MPI_Isend(const void* buf, int count, MPI_Datatype datatype, int dest, # int tag, MPI_Comm comm, MPI_Request *request) - API.MPI_Isend(buf.data, buf.count, buf.datatype, dest, tag, comm, req) + API.MPI_Isend_c(buf.data, buf.count, buf.datatype, dest, tag, comm, req) setbuffer!(req, buf) return req end @@ -111,7 +111,7 @@ Recv!(recvbuf, comm::Comm, status=nothing; source=API.MPI_ANY_SOURCE[], tag=API. function Recv!(recvbuf::Buffer, source::Integer, tag::Integer, comm::Comm, status::Union{Ref{Status},Nothing}) # int MPI_Recv(void* buf, int count, MPI_Datatype datatype, int source, # int tag, MPI_Comm comm, MPI_Status *status) - API.MPI_Recv(recvbuf.data, recvbuf.count, recvbuf.datatype, source, tag, comm, something(status, API.MPI_STATUS_IGNORE[])) + API.MPI_Recv_c(recvbuf.data, recvbuf.count, recvbuf.datatype, source, tag, comm, something(status, API.MPI_STATUS_IGNORE[])) return recvbuf.data end Recv!(recvbuf, source::Integer, tag::Integer, comm::Comm, status::Union{Ref{Status},Nothing}) = @@ -207,7 +207,7 @@ function Irecv!(buf::Buffer, source::Integer, tag::Integer, comm::Comm, req::Abs @assert isnull(req) # int MPI_Irecv(void* buf, int count, MPI_Datatype datatype, int source, # int tag, MPI_Comm comm, MPI_Request *request) - API.MPI_Irecv(buf.data, buf.count, buf.datatype, source, tag, comm, req) + API.MPI_Irecv_c(buf.data, buf.count, buf.datatype, source, tag, comm, req) setbuffer!(req, buf) return req end @@ -237,9 +237,9 @@ function Sendrecv!(sendbuf::Buffer, dest::Integer, sendtag::Integer, # int MPI_Sendrecv(const void *sendbuf, int sendcount, MPI_Datatype sendtype, int dest, int sendtag, # void *recvbuf, int recvcount, MPI_Datatype recvtype, int source, int recvtag, # MPI_Comm comm, MPI_Status *status) - API.MPI_Sendrecv(sendbuf.data, sendbuf.count, sendbuf.datatype, dest, sendtag, - recvbuf.data, recvbuf.count, recvbuf.datatype, source, recvtag, - comm, something(status, API.MPI_STATUS_IGNORE[])) + API.MPI_Sendrecv_c(sendbuf.data, sendbuf.count, sendbuf.datatype, dest, sendtag, + recvbuf.data, recvbuf.count, recvbuf.datatype, source, recvtag, + comm, something(status, API.MPI_STATUS_IGNORE[])) return recvbuf.data end Sendrecv!(sendbuf, dest::Integer, sendtag::Integer, recvbuf, source::Integer, recvtag::Integer, comm::Comm, status::Union{Ref{Status}, Nothing}) = @@ -267,7 +267,7 @@ Send_init(buf, comm::Comm, req::AbstractRequest=Request(); dest::Integer, tag::I Send_init(buf, dest, tag, comm, req) function Send_init(buf::Buffer, dest::Integer, tag::Integer, comm::Comm, req::AbstractRequest=Request()) @assert isnull(req) - API.MPI_Send_init(buf.data, buf.count, buf.datatype, dest, tag, comm, req) + API.MPI_Send_init_c(buf.data, buf.count, buf.datatype, dest, tag, comm, req) setbuffer!(req, buf) return req end @@ -289,7 +289,7 @@ Recv_init(buf, comm::Comm, req::AbstractRequest=Request(); source=API.MPI_ANY_SO Recv_init(buf, source, tag, comm, req) function Recv_init(buf::Buffer, source::Integer, tag::Integer, comm::Comm, req::AbstractRequest=Request()) @assert isnull(req) - API.MPI_Recv_init(buf.data, buf.count, buf.datatype, source, tag, comm, req) + API.MPI_Recv_init_c(buf.data, buf.count, buf.datatype, source, tag, comm, req) setbuffer!(req, buf) return req end @@ -424,7 +424,7 @@ Optionally returns the [`Status`](@ref) object of the receive. $(_doc_external("MPI_Mrecv")) """ function Mrecv!(recvbuf::Buffer, msg::Message, status::Union{Ref{Status},Nothing}=nothing) - API.MPI_Mrecv(recvbuf.data, recvbuf.count, recvbuf.datatype, msg, something(status, API.MPI_STATUS_IGNORE[])) + API.MPI_Mrecv_c(recvbuf.data, recvbuf.count, recvbuf.datatype, msg, something(status, API.MPI_STATUS_IGNORE[])) return recvbuf.data end Mrecv!(recvbuf, msg::Message, status::Union{Ref{Status},Nothing}=nothing) = @@ -450,7 +450,7 @@ $(_doc_external("MPI_Imrecv")) """ function Imrecv!(buf::Buffer, msg::Message, req::AbstractRequest=Request()) @assert isnull(req) - API.MPI_Imrecv(buf.data, buf.count, buf.datatype, msg, req) + API.MPI_Imrecv_c(buf.data, buf.count, buf.datatype, msg, req) setbuffer!(req, buf) return req end diff --git a/test/test_largecount.jl b/test/test_largecount.jl new file mode 100644 index 000000000..90554a808 --- /dev/null +++ b/test/test_largecount.jl @@ -0,0 +1,153 @@ +include("common.jl") +using Libdl + +MPI.Init() + +comm = MPI.COMM_WORLD +rank = MPI.Comm_rank(comm) +nprocs = MPI.Comm_size(comm) + +const BIG = Int64(typemax(Cint)) + 1 # one past what the narrow interface can express + +# A large count is only *usable* where addresses are wide enough to describe the data. +# `MPI_Aint` is pointer-sized, so on a 32-bit platform neither a 2 GiB datatype extent nor +# a 2 GiB displacement can exist, whatever `MPI_Count` says -- MPICH rejects such a +# datatype outright with "The input argument count is too big to fit for internal +# routines". So `HAS_LARGE_COUNT` alone is not enough to reach for a count this size. +const BIG_ADDRESSABLE = MPI.API.HAS_LARGE_COUNT && BIG <= typemax(MPI.API.MPI_Aint) + +# Our CI declares whether large counts are supported in the tested MPI library. +# This allows us to test whether our auto-detection is working. +if haskey(ENV, "JULIA_MPI_TEST_LARGE_COUNT") + @test MPI.API.HAS_LARGE_COUNT == (ENV["JULIA_MPI_TEST_LARGE_COUNT"] == "true") +end + +# Large-count support has to be all-or-nothing. Intel MPI 2021.11 provides `MPI_Send_c` +# but not `MPI_Type_size_c`, and since the count types are one choice for the whole +# package, deciding per function let a `Ref{MPI_Count}` reach a `Ptr{Cint}`. +@testset "large-count support is all-or-nothing" begin + absent = filter(collect(MPI.API.LARGE_COUNT_SYMBOLS)) do sym + isnothing(Libdl.dlsym(MPI.API.libmpi_handle, sym; throw_error=false)) + end + @test MPI.API.HAS_LARGE_COUNT == isempty(absent) +end + +@testset "count types" begin + @test MPI.API.Count <: Integer + @test MPI.API.Displ <: Integer + @test MPI.API.TypeDispl <: Integer + if MPI.API.HAS_LARGE_COUNT + @test typemax(MPI.API.Count) > typemax(Cint) + # `Displ` and `TypeDispl` need to hold byte offsets, so at least a pointer's worth + @test sizeof(MPI.API.Displ) >= sizeof(Ptr{Cvoid}) + @test sizeof(MPI.API.TypeDispl) >= sizeof(Ptr{Cvoid}) + else + @test MPI.API.Count === Cint + end + # the buffer types must follow + @test fieldtype(MPI.Buffer{Vector{Int}}, :count) === MPI.API.Count + @test fieldtype(MPI.RBuffer{Vector{Int},Vector{Int}}, :count) === MPI.API.Count + @test fieldtype(MPI.VBuffer{Vector{Int}}, :counts) === Vector{MPI.API.Count} + @test fieldtype(MPI.VBuffer{Vector{Int}}, :displs) === Vector{MPI.API.Displ} +end + +# Derived datatypes let us exercise a count larger than `typemax(Cint)` without allocating +# anything: only the type's description is large, not a buffer. +@testset "large-count datatypes" begin + if BIG_ADDRESSABLE + dt = MPI.Types.create_contiguous(BIG, MPI.BYTE) + MPI.Types.commit!(dt) + @test MPI.Types.size(dt) == BIG + @test MPI.Types.extent(dt) == (0, BIG) + MPI.free(dt) + elseif MPI.API.HAS_LARGE_COUNT + @test_throws MPI.MPIError MPI.Types.create_contiguous(BIG, MPI.BYTE) + else + # The narrow entry point cannot express this, and must throw an exception. + @test_throws InexactError MPI.Types.create_contiguous(BIG, MPI.BYTE) + end +end + +@testset "large counts in Buffer" begin + if MPI.API.HAS_LARGE_COUNT + buf = MPI.Buffer(Ptr{UInt8}(0), BIG, MPI.BYTE) + @test buf.count == BIG + if BIG_ADDRESSABLE + vbuf = MPI.VBuffer(Ptr{UInt8}(0), [BIG, BIG], [0, BIG], MPI.BYTE) + @test vbuf.counts == [BIG, BIG] + @test vbuf.displs == [0, BIG] + else + # The displacements are `MPI_Aint`, which cannot hold this on a 32-bit build + @test_throws InexactError MPI.VBuffer(Ptr{UInt8}(0), [BIG, BIG], [0, BIG], MPI.BYTE) + end + else + @test_throws InexactError MPI.Buffer(Ptr{UInt8}(0), BIG, MPI.BYTE) + end +end + +# Counts and displacements are allowed to be different widths (they are, under 32-bit +# MPICH), so make sure a VBuffer built the usual way still lines up. +@testset "VBuffer count/displ widths" begin + counts = [1, 2, 3] + vbuf = MPI.VBuffer(zeros(Int, sum(counts)), counts) + @test eltype(vbuf.counts) === MPI.API.Count + @test eltype(vbuf.displs) === MPI.API.Displ + @test vbuf.displs == [0, 1, 3] + # a Vector{Cint} argument must still be accepted + vbuf = MPI.VBuffer(zeros(Int, 6), Cint[1, 2, 3], Cint[0, 1, 3]) + @test vbuf.counts == [1, 2, 3] +end + +# Actually running a test that handles more than 2 GiB is opt-in: +# This needs a couple of GiB of RAM per rank, which is more than a shared CI runner can handle. +if get(ENV, "JULIA_MPI_TEST_LARGECOUNT", "") == "1" + if !BIG_ADDRESSABLE + @info "JULIA_MPI_TEST_LARGECOUNT set but this build cannot address a 2 GiB buffer; skipping" + else + @testset "2 GiB point-to-point" begin + n = BIG + 1 + if rank == 0 + data = fill(Int8(7), n) + MPI.Send(data, 1, 0, comm) + data = nothing + elseif rank == 1 + data = zeros(Int8, n) + stat = MPI.Recv!(data, 0, 0, comm) + @test MPI.Get_count(stat, Int8) == n + @test all(==(Int8(7)), data) + data = nothing + end + GC.gc() + end + MPI.Barrier(comm) + + @testset "2 GiB Bcast!" begin + n = BIG + 1 + data = rank == 0 ? fill(Int8(3), n) : zeros(Int8, n) + MPI.Bcast!(data, 0, comm) + @test all(==(Int8(3)), data) + data = nothing + GC.gc() + end + MPI.Barrier(comm) + + # A user-defined `Op` is created with the narrow `MPI_Op_create`, whose callback + # takes `int *len`. This checks the assumption that an MPI library chunks a + # large-count reduction into calls which the narrow callback can express, rather than + # handing it a length it cannot represent. + @testset "2 GiB Allreduce! with a custom Op" begin + n = BIG + 1 + data = fill(Int8(1), n) + MPI.Allreduce!(data, (a, b) -> max(a, b), comm) + @test all(==(Int8(1)), data) + data = nothing + GC.gc() + end + MPI.Barrier(comm) + end +end + +MPI.Barrier(comm) +GC.gc() +MPI.Finalize() +@test MPI.Finalized()