Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Y-partition and XY-partition tests #3338

Merged
merged 127 commits into from
Oct 25, 2023
Merged

Conversation

simone-silvestri
Copy link
Collaborator

@simone-silvestri simone-silvestri commented Oct 13, 2023

The code should allow all types of partitioning but there is a bug preventing the tests from passing for y- and xy-partitions.

This PR aims to fix the bug (and consequently add the excluded y- and xy-partitioning tests)

rework fill_halo_regions! to split out non-communicating and communicating boundary conditions such that the latter are executed alone.

To give an example, for

julia> boundary_conditions = FieldBoundaryConditions(west = NoFluxBoundaryCondition(), east = NoFluxBoundaryCondition(), south = ImpenetrableBoundaryCondition(), north = DistributedCommunicationBoundaryCondition(), bottom = nothing, top = nothing)
Oceananigans.FieldBoundaryConditions, with boundary conditions
├── west: FluxBoundaryCondition: Nothing
├── east: FluxBoundaryCondition: Nothing
├── south: OpenBoundaryCondition: Nothing
├── north: DistributedBoundaryCondition: Nothing
├── bottom: Nothing
├── top: Nothing
└── immersed: DefaultBoundaryCondition (FluxBoundaryCondition: Nothing)

in main:

julia> halo_tuple = permute_boundary_conditions(boundary_conditions);

julia> for i in 1:length(halo_tuple[1])
              @info "operation $(halo_tuple[1][i]) with bcs $((halo_tuple[2][i], halo_tuple[3][i]))"
       end
[ Info: operation fill_bottom_and_top_halo! with bcs (nothing, nothing)
[ Info: operation fill_west_and_east_halo! with bcs (FluxBoundaryCondition: Nothing, FluxBoundaryCondition: Nothing)
[ Info: operation fill_south_and_north_halo! with bcs (OpenBoundaryCondition: Nothing, BoundaryCondition{Oceananigans.BoundaryConditions.DistributedCommunication, Nothing})

in this PR

julia> halo_tuple = permute_boundary_conditions(boundary_conditions);

julia> for i in 1:length(halo_tuple[1])
              @info "operation $(halo_tuple[1][i]) with bcs $(halo_tuple[2][i])"
       end
[ Info: operation fill_bottom_and_top_halo! with bcs (nothing, nothing)
[ Info: operation fill_south_halo! with bcs (OpenBoundaryCondition: Nothing,)
[ Info: operation fill_west_and_east_halo! with bcs (FluxBoundaryCondition: Nothing, FluxBoundaryCondition: Nothing)
[ Info: operation fill_north_halo! with bcs (DistributedBoundaryCondition: Nothing,)


@inline extract_bc(bc, ::Val{:west_and_east}) = (extract_west_bc(bc), extract_east_bc(bc))
@inline extract_bc(bc, ::Val{:south_and_north}) = (extract_south_bc(bc), extract_north_bc(bc))
@inline extract_bc(bc, ::Val{:bottom_and_top}) = (extract_bottom_bc(bc), extract_top_bc(bc))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's all this for?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in case we have a tuple of FieldBoundaryConditions instead of just one FieldBoundaryConditions where we can just do (bc.west, bc.east)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh okay, so this will extract a "tuple of west bcs" and a "tuple of east bcs".

Can you write that in a comment?


# Calculate size and offset of the fill_halo kernel
size = fill_halo_size(c, fill_halo!, indices, bc_left, loc, grid)
size = fill_halo_size(c, fill_halo!, indices, bcs[1], loc, grid)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't there an assumption hidden in using bcs[1]? What is that assumption? Document your assumption with a comment.

Comment on lines 147 to 148
# Distributed halos have to be filled last because of
# buffered communication.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn't explain it to me completely. Why would buffered communication require halos to be filled last? What do buffers have to do with it? Is that point that you have to complete other halo filling tasks before communicating, if you want to avoid filling the halos again after communication?

It seems like we could communicate first before filling halos, right?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will update the comment. If you want asynchronous communication there are two options:

  1. Fill all local halos - fill the buffers and start the communication - perform any required computation - complete the communication.
  2. Fill the buffers and start the communication - perform any required computation - complete the communication - Fill any local halos.

with the first option, nothing changes in the case of non-distributed fields so I went in that direction in the #3125.

Copy link
Member

@glwagner glwagner Oct 23, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

By "complete the communication" you mean "wait until communication finishes" right? Eg there is not a separate action associated with "completing" the communication; it's just that it may not be finished at the time that you are done performing the preliminary computations. Or does "completing" mean something else?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

completing the communication requires waiting until the communication is finished and then filling the relevant halos from the received buffer. In practice, we can think about it as waiting until the communication is finished (assuming that filling the relevant halos is part of the communication process).

Copy link
Member

@glwagner glwagner Oct 23, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay so the first step is to initiate communication, and the second step is to wait for communication to finish and then fill halos from the buffer. The code is misleading, because the initiation of communication is currently called "fill halo regions!". But halo filling is actually occurring in the second step, right?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in asynchronous communication fill_halo_regions!(f; async = true) yes, the communication is completed in the synchronize_communication!(f) otherwise everything is completed in the fill_halo_regions! function

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps finish_fill_halo_regions!(f) or something would be helpful for the second step

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And probably neesd a comment in the code to indicate that the halo regions are not actually filled into the second step in the case of a distributed computation

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We assume that the second step completes everything: i.e. wait for communication to complete and fill the halos

synchronize_communication!(field)
complete the halo passing of `field` among processors.
"""
function synchronize_communication!(field)
arch = architecture(field.grid)
# Wait for outstanding requests
if !isempty(arch.mpi_requests)
cooperative_waitall!(arch.mpi_requests)
# Reset MPI tag
arch.mpi_tag[] -= arch.mpi_tag[]
# Reset MPI requests
empty!(arch.mpi_requests)
end
recv_from_buffers!(field.data, field.boundary_buffers, field.grid)
return nothing
end

@@ -101,11 +101,11 @@ function fill_halo_regions!(c::OffsetArray, bcs, indices, loc, grid::Distributed
arch = architecture(grid)
halo_tuple = permute_boundary_conditions(bcs)

for task = 1:3
for task = 1:length(halo_tuple[1])
Copy link
Member

@glwagner glwagner Oct 23, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the meaning of "length(halo_tuple[1])"?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

changed to number_of_tasks

@simone-silvestri
Copy link
Collaborator Author

I think I should have addressed all comments

@simone-silvestri simone-silvestri merged commit dcc0677 into main Oct 25, 2023
48 checks passed
@simone-silvestri simone-silvestri deleted the ss/distributed_tests branch October 25, 2023 23:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
distributed 🕸️ Our plan for total cluster domination testing 🧪 Tests get priority in case of emergency evacuation
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants