Add distribution of thread blocks to chiplets in old codegen - #2527
Add distribution of thread blocks to chiplets in old codegen#2527iomaganaris wants to merge 16 commits into
Conversation
…plet_old_codegen_updateddace
Drop two changes that came along with the chiplet commits but are unrelated to the chiplet mapping and active regardless of it: * The `threadIdx.y` -> `threadIdx.z` remap for 2D thread-block maps. Block sizes are never remapped by the chiplet patch, so the second index of a 2D thread-block map would be read from `threadIdx.z`, which is 0 for a (bx, by, 1) block. Since `AddThreadBlockMap` is now applied everywhere, this fired on nearly every kernel. * The `/*block*/` and `/*threads*/` markers in the generated code. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GdEk8Xdh2bd53mE3tf5Thy
… generator Distributing the thread-blocks of a kernel over the chiplets of the GPU moves the second dimension of the grid to `blockIdx.z`, which the merged implementation did not account for in every case: * Grids using all three dimensions had their third dimension silently dropped, or were folded into an index expression asking for a fourth `blockIdx`, which raises. The distribution is now only applied when the third grid dimension is 1, and kernels that cannot use it keep their grid and report why. * Nested device maps and dynamic thread-block maps read `blockIdx` outside of `generate_kernel_scope`, where the chiplet ID would be taken for a work index. Both are excluded as well. * The first grid dimension is padded to a multiple of the number of chiplets, but the condition masking out the padded thread-blocks was elided whenever the map divided evenly by the block size, and was not generated at all for kernels with an inner thread-block map. It is now always generated. The default of `compiler.cuda.chiplet_number` is 1, i.e. the grid is left alone unless the distribution is asked for (`DACE_compiler_cuda_chiplet_number=6` on MI300A). The generated code with the default is identical to the code generated before the merge. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GdEk8Xdh2bd53mE3tf5Thy
|
The assignment of thread-blocks to physical locations is a matter of codegen, yet right now the choice is too implicit. It is also not providing the user the option to choose between options. (This change would force all kernels to be generated either the old way or the new chiplet-aware way) I think it would make more sense to have a map-level attribute (to also not break the existing assumption of "GPU_Device" is a GPU kernel and no a new schedule type to update everything) I see that, through the tests the existence of tblock maps it should be fine. For this pattern (and new codegen enforces it): Codegen only changes the tblocks are assigned to chiplets so it seems fine to me. |
|
For test quality we should have a test where we check numerical equivalence between the two strategies (e.g. maybe a very small icon loopnest snippet written using the python frontend?). Also please add a test involving persistent schedule (so that we dont break it unknowingly) |
…ight cases are tested
ThrudPrimrose
left a comment
There was a problem hiding this comment.
All previous comments are addressed.
The codegen is opt-in and I like it. We also have enough numerical correctness checks.
Only one question? Would it make sense, and justify the effort to auto-detect chiplet count if a value like 0 is provided?
We could have a simple dictionary between compute arch/model to chiplet count to make it simpler to run on multiple amd devises? What do you think?
Thank you very much for the review. |
| default=True, | ||
| desc="Allow the thread-blocks of this kernel to be distributed over the chiplets of the GPU " | ||
| "(see the `compiler.cuda.chiplet_number` configuration entry)", | ||
| serialize_if=lambda m: m.schedule in dtypes.GPU_SCHEDULES) |
There was a problem hiding this comment.
this should only be for GPU_Device and GPU_Persistent, right? not GPU_Threadblock[Dynamic]
There was a problem hiding this comment.
If GPU_Persistent is enabled then based on the chiplet_count function nothing happens. I thought it would actually make sense probably to set this for the GPU_Threadblock schedule maybe?
| # contiguous chunk of ``ceil(grid_size[0] / chiplets)`` blocks of the first dimension, | ||
| # together with the full second dimension, which moves to ``blockIdx.z``. | ||
| original_grid_size = grid_size | ||
| grid_size = [self._kernel_chiplet_count, int_ceil(grid_size[0], self._kernel_chiplet_count), grid_size[1]] |
There was a problem hiding this comment.
I am not sure why we should change the grid size; that means that the grid sizes are effectively minimized because blockdim x is the one that can be the largest of the three.
I'd consider a straightforward code generation approach (changing the blocks based on a modulo operator rather than changing the actual map dimensions.
There was a problem hiding this comment.
I deliberately wanted to avoid the module operations and the thread ID translation because they add extra resource usage and complicates further the generated code. Unless there is actual need to support 3D maps I would keep this simpler approach
There was a problem hiding this comment.
Since I realized that different grid dimensions have different allowed ranges I have updated the logic to be codegen only with % and / usage
Co-authored-by: Tal Ben-Nun <tbennun@gmail.com>
| # together with the full second dimension, which moves to ``blockIdx.z``. | ||
| original_grid_size = grid_size | ||
| grid_size = [self._kernel_chiplet_count, int_ceil(grid_size[0], self._kernel_chiplet_count), grid_size[1]] | ||
| warnings.warn(f'Distributing the grid of kernel "{kernelmap_entry.map.label}" over ' |
There was a problem hiding this comment.
This warning prints a lot of warnings like:
/capstor/scratch/cscs/ioannmag/cycle38/icon4py-benchmarks/venv_mi300/lib/python3.12/site-packages/dace/codegen/targets/cuda.py:2321: UserWarning: Distributing the grid of kernel "map_0_fieldop" over 6 chiplets, adjusting its size from [649, 1, 1] to [6, 109, 1].
when the option is enabled.
I am not really sure if this is beneficial or not
The goal of this PR is to distribute the thread blocks of the GPU backend to AMD GPU chiplets in such way that the X dimension of the grid is divided by the number of chiplets, so each one of them gets a continuous range of the domain. The domains that execute on the same chiplet can use then the same L2 cache, improving cache hits when there are data that can be reused between thread blocks in the same region or data that live in one dimension in two dimensional kernels (i.e. ICON neighbor tables and other vertical level independent fields).
This change has a ~7% performance improvement in the
icon4pydycore.We have already discussed about the necessity of this solution with @ThrudPrimrose I was interested to implement it in the old codegen though because we still haven't switched to the new one