From b8f9864fead1800eb814906583407bbbae280ede Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mos=C3=A8=20Giordano?= Date: Thu, 12 Sep 2024 20:48:25 +0100 Subject: [PATCH 1/2] Always ensure consistency of new MPI datatypes Also: * skip test of custom primitive type of size 80 on 32-bit systems in Julia v1.12+, as the MPI extent doesn't match Julia's aligned size * add a test for a new primitive datatype larger than 64 bits which should work on 32-bit systems --- src/datatypes.jl | 11 ++++++++++- test/test_datatype.jl | 14 ++++++++------ 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/src/datatypes.jl b/src/datatypes.jl index 9dd721fcc..bbb8ccb47 100644 --- a/src/datatypes.jl +++ b/src/datatypes.jl @@ -149,7 +149,7 @@ end function Datatype(::Type{T}) where {T} global created_datatypes - get!(created_datatypes, T) do + datatype = get!(created_datatypes, T) do datatype = Datatype() # lazily initialize so that it can be safely precompiled function init() @@ -162,6 +162,15 @@ function Datatype(::Type{T}) where {T} init() datatype end + + # Make sure the "aligned" size of the type matches the MPI "extent". + sz = sizeof(T) + al = Base.datatype_alignment(T) + mpi_extent = Types.extent(datatype) + aligned_size = (0, cld(sz,al)*al) + @assert mpi_extent == aligned_size "The MPI extent of type $(T) ($(mpi_extent[2])) does not match the size expected by Julia ($(aligned_size[2]))" + + return datatype end function Base.show(io::IO, datatype::Datatype) diff --git a/test/test_datatype.jl b/test/test_datatype.jl index c966a9678..97f77c6aa 100644 --- a/test/test_datatype.jl +++ b/test/test_datatype.jl @@ -87,17 +87,19 @@ end primitive type Primitive16 16 end primitive type Primitive24 24 end primitive type Primitive80 80 end +primitive type Primitive104 104 end + +@testset for PrimitiveType in (Primitive16, Primitive24, Primitive80, Primitive104) + if PrimitiveType == Primitive80 && VERSION >= v"1.12-" && Sys.WORD_SIZE == 32 + # LLVM alignment on 32-bit systems doesn't necessarily agree with MPI + # extent for all types larger than 64 bits. + continue + end -@testset for PrimitiveType in (Primitive16, Primitive24, Primitive80) sz = sizeof(PrimitiveType) al = Base.datatype_alignment(PrimitiveType) @test MPI.Types.extent(MPI.Datatype(PrimitiveType)) == (0, cld(sz,al)*al) - if VERSION < v"1.3" && PrimitiveType == Primitive80 - # alignment is broken on earlier Julia versions - continue - end - arr = [Core.Intrinsics.trunc_int(PrimitiveType, UInt128(comm_rank + i)) for i = 1:4] arr_recv = Array{PrimitiveType}(undef,4) From c472b9158acda0977ad85ff418b5bbf4df507b94 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mos=C3=A8=20Giordano?= Date: Fri, 13 Sep 2024 11:27:25 +0100 Subject: [PATCH 2/2] Create a datatype with size a multiple of its alignment This should help ensure memory allocations of the MPI datatype have the same alignment as the Julia counterpart. --- src/datatypes.jl | 6 ++++-- test/test_datatype.jl | 15 ++++++--------- 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/src/datatypes.jl b/src/datatypes.jl index bbb8ccb47..cefe5aa92 100644 --- a/src/datatypes.jl +++ b/src/datatypes.jl @@ -446,8 +446,10 @@ function create!(newtype::Datatype, ::Type{T}) where {T} types = Datatype[] if isprimitivetype(T) - # primitive type - szrem = sz = sizeof(T) + # This is a primitive type. Create a type which has size an integer multiple of its + # alignment on the Julia side: . + al = Base.datatype_alignment(T) + szrem = sz = cld(sizeof(T), al) * al disp = 0 for (i,basetype) in (8 => Datatype(UInt64), 4 => Datatype(UInt32), 2 => Datatype(UInt16), 1 => Datatype(UInt8)) if sz == i diff --git a/test/test_datatype.jl b/test/test_datatype.jl index 97f77c6aa..ab8385d75 100644 --- a/test/test_datatype.jl +++ b/test/test_datatype.jl @@ -88,19 +88,16 @@ primitive type Primitive16 16 end primitive type Primitive24 24 end primitive type Primitive80 80 end primitive type Primitive104 104 end +primitive type Primitive136 136 end -@testset for PrimitiveType in (Primitive16, Primitive24, Primitive80, Primitive104) - if PrimitiveType == Primitive80 && VERSION >= v"1.12-" && Sys.WORD_SIZE == 32 - # LLVM alignment on 32-bit systems doesn't necessarily agree with MPI - # extent for all types larger than 64 bits. - continue - end - +@testset for PrimitiveType in (Primitive16, Primitive24, Primitive80, Primitive104, Primitive136) sz = sizeof(PrimitiveType) al = Base.datatype_alignment(PrimitiveType) @test MPI.Types.extent(MPI.Datatype(PrimitiveType)) == (0, cld(sz,al)*al) - arr = [Core.Intrinsics.trunc_int(PrimitiveType, UInt128(comm_rank + i)) for i = 1:4] + conv = sizeof(PrimitiveType) <= sizeof(UInt128) ? Core.Intrinsics.trunc_int : Core.Intrinsics.sext_int + + arr = [conv(PrimitiveType, UInt128(comm_rank + i)) for i = 1:4] arr_recv = Array{PrimitiveType}(undef,4) recv_req = MPI.Irecv!(arr_recv, src, 2, MPI.COMM_WORLD) @@ -108,7 +105,7 @@ primitive type Primitive104 104 end MPI.Waitall([recv_req, send_req]) - @test arr_recv == [Core.Intrinsics.trunc_int(PrimitiveType, UInt128(src + i)) for i = 1:4] + @test arr_recv == [conv(PrimitiveType, UInt128(src + i)) for i = 1:4] end @testset "packed non-aligned tuples" begin