Launch_nd: Abstraction to switch between flat and nested loops - #2021
Launch_nd: Abstraction to switch between flat and nested loops#2021artv3 wants to merge 26 commits into
Conversation
|
I am concerned that people will use this without knowing that its doing expensive div/mod calculations and see that it performs more poorly than a native cuda/hip 2d or 3d kernel. Would it make sense to have policies that allow mapping to 2d or 3d kernels. |
I think that can be addressed in the RAJA cookbook or examples explaining when this would be performant. In the case of @tomstitt and I, it comes up when the threads per block are not high enough to saturate the GPU and using |
I think my ideal is an interface where there is a choice of policy. We have 2d/3d kernels on 1d iteration spaces, using mod/div like Arturo said, to expose more parallelism. When we switch some of those to using our "true" 2d/3d grid launcher we lose performance because our block (16x16 , 8x8x8) doesn't map well onto the grid (because we just idle threads). It's of course not always true that our div/mod approach is going to be better, like Jason said, and if we had an easy way to pick we could put both behind our abstraction and correctly dispatch |
@tomstitt , okay -- Let's get that imagination soaring, and cook up some ideas! #RAJA! |
| RAJA::launch_nd(res, policy, RAJA::segments(cells, comps), | ||
| [=] RAJA_HOST_DEVICE(int cell, int comp) { | ||
| const int idx = comp + num_comp * cell; | ||
| values_ptr[idx] = 1000 * cell + comp; | ||
| }); |
There was a problem hiding this comment.
@tomstitt , @MrBurmark I invite you to take a look at this example, I think this may be what we are looking for.
You certainly can use launch or teams to get some level of parallelism and then take those indices and do your own calculations with them. If we had the multiloop abstractions with a variety of policies that could take us a fair amount of the way. |
|
@llnl/raja-core , I received an approval from my project. Opening up the PR for review from the squad. |
|
@artv3 I merged in latest develop and ran clang-format so GHA CI check run. Is this ready to be merged when approved? |
Not yet, I think we want @llnl/raja-core to take another look. We had a discussed a while potentially changing the name of the abstraction |
| stay written in logical multi-dimensional indices, but the best GPU mapping is | ||
| not known from the source alone. |
There was a problem hiding this comment.
The GPU is part of this, but wouldn't this also be useful for doing things like collapsed openmp loops, or reordered sequential loops?
There was a problem hiding this comment.
Underneath we just map to regular nested loops in launch, I think we would have to leave that for when we do the expanded version of this. One thing to explore is transitioning between flat loops to nested loops using threads in a GPU setting. We also would have to figure out the backend on how that would look like for OpenMP
There was a problem hiding this comment.
I suppose it could, I haven't thought about that yet though actually, I think that could be a neat expansion. I think that goes beyond the targeted scope of this PR though, but something to definitely revisit as we expand capabilities. I would be interested in testing it some kernels in RAJAPerf
| The example source defines backend-specific policy aliases. CUDA uses direct | ||
| global loop policies for the true grid mapping: |
There was a problem hiding this comment.
It is confusing to have direct and loop in close proximity here.
There was a problem hiding this comment.
good call, I updated the example
| The flattened policy uses a forall-style execution policy such as | ||
| ``RAJA::cuda_exec<block_size_1d>`` or ``RAJA::hip_exec<block_size_1d>``. The | ||
| grid policy uses ``RAJA::LaunchPolicy`` and direct global loop policies such as | ||
| ``RAJA::cuda_global_y_direct`` and ``RAJA::cuda_global_x_direct``. |
There was a problem hiding this comment.
Could you use RAJA::cuda_global_yx_direct?
There was a problem hiding this comment.
If people want to do more complicated things like mixing direct and loop policies they could always use launch itself.
There was a problem hiding this comment.
I agree actually, that would good but we would need to first update formalize support “multi-dim per-loop” policies. Something I've played around with but have not yet formalized
| ``RAJA::launch_nd`` currently supports ``RAJA::TypedRangeSegment`` packs created | ||
| with ``RAJA::segments``. The grid mapping supports 2-D and 3-D loops and | ||
| requires one loop policy per segment. The flattened mapping uses | ||
| ``RAJA::layout_right`` by default, or ``RAJA::layout_left`` when that layout tag | ||
| is supplied, to control which logical index is unit stride in the flattened | ||
| space. |
There was a problem hiding this comment.
Why not just a tuple of segments?
The layouts in the policy are a way to reorder the loops?
There was a problem hiding this comment.
In some sense it already is, struct TypedRangeSegmentPack { camp::tuple<RAJA::TypedRangeSegment<IdxTs>...> data; }; , the RAJA::Segments just enforces that we use pass in a RAJA::Segment type? Are you thinking of support for non-RAJA types?
Second question:
For the flatten policy it controls the linearization order (which logical index
varies fastest / is unit-stride)
For the grid policy:
it’s determined by the segment + loop-
policy order (one policy per segment)
| #if defined(RAJA_GPU_ACTIVE) | ||
| template<typename PolicyList> | ||
| RAJA_INLINE auto make_launch_nd_context(RAJA::resources::Resource resource, | ||
| std::string&& kernel_name) | ||
| { | ||
| return resource.get_platform() == RAJA::Platform::host | ||
| ? util::make_context<typename PolicyList::host_policy_t>( | ||
| std::move(kernel_name)) |
There was a problem hiding this comment.
If we are checking for RAJA::Platform::host here, does this block need to be in RAJA_GPU_ACTIVE?
There was a problem hiding this comment.
yes, because in GPU builds we can still dispatch to the CPU at run time.
| { | ||
|
|
||
| template<typename... IdxTs> | ||
| struct TypedRangeSegmentPack |
There was a problem hiding this comment.
Do we want TypedRangeSegmentPack to be in the outward facing RAJA API?
There was a problem hiding this comment.
good, point that should be internal, since it is specific to this API
| }; | ||
|
|
||
| template<typename... IdxTs> | ||
| RAJA_INLINE auto segments(RAJA::TypedRangeSegment<IdxTs> const&... segs) |
There was a problem hiding this comment.
Should we rename RAJA::segments to something else closer to its launch_nd-only usage? Or hide it better in a deeper namespace so that people don't accidentally use segments?
There was a problem hiding this comment.
Agreed, renamed to RAJA::nd_segments(...) and updated docs/tests/examples accordingly
| case ExecPlace::HOST: | ||
| return RAJA::launch_nd( | ||
| resource, launch_nd_flattened_policy<HostExecPolicy, LayoutTag> {}, |
There was a problem hiding this comment.
Do we need to validate that the resource matches the ExecPlace?
There was a problem hiding this comment.
yes, good call, added a helper to check
| template<typename HostExecPolicy, | ||
| typename DeviceExecPolicy, | ||
| typename LayoutTag = RAJA::layout_right> | ||
| struct launch_nd_flattened_place_policy | ||
| { | ||
| using host_exec_policy = HostExecPolicy; | ||
| using device_exec_policy = DeviceExecPolicy; | ||
| using layout_tag = LayoutTag; | ||
| }; | ||
|
|
||
| /*! | ||
| * General runtime host/device launch_nd policy container. | ||
| * | ||
| * This allows callers to select different launch_nd policy *kinds* for host and | ||
| * device (e.g., a grid policy on host for nested loops and a flattened policy | ||
| * on device for a 1D mapping). | ||
| */ | ||
| template<typename HostPolicy, typename DevicePolicy> | ||
| struct launch_nd_place_policy | ||
| { | ||
| HostPolicy host; | ||
| DevicePolicy device; | ||
| }; |
There was a problem hiding this comment.
I know these serve two different overloads of launch_nd() below, but could these be combined for simplification? Maybe just have launch_nd_flattened_place_policy?
There was a problem hiding this comment.
good idea, I reduced duplication by making launch_nd_flattened_place_policy just a convenience alias of the general launch_nd_place_policy, and removed the now-
redundant launch_nd(...) overloads.
|
Thanks @rchen20 , I addressed your review comments |
| { | ||
| switch (place) |
There was a problem hiding this comment.
We probably need validate_launch... here too. Would you mind checking where else we need to call it?
I along with @tomstitt work an application where we have been exploring using a 1D GPU index for multi-level loops and have found that may be more performant than using hierarchical parallelism. This PR uses concepts from RAJA to create the RAJA::forall_nd convenience function.
Cases in which this approach is more performant:
it comes up when the threads per block are not high enough to saturate the GPU and using
RAJA::forall + mods + divsallows us to increase the threads per block which ends up being more performant.