Skip to content

Commit 3f5dc1c

Browse files
dennisYatuninclaude
andcommitted
Propagate unified DataLayouts API and refine it after review
Updates all modules, the CUDA extension, and the test suite for the new DataLayout types, 4-D (v, i, j, h) index convention, and loop primitives introduced by the previous commit, and applies the revisions from review: - Add Utilities.nested_view, which prevents unbounded nesting of SubArray and ReshapedArray wrappers from repeated views and reshapes; use it for slice views and struct field views to avoid type-depth widening and per-view allocations - Use Base's pairwise mapreduce over linear positions for reductions, and re-evaluate impure scalar broadcasts at every point - Simplify BroadcastStyle combination, getproperty, view via rebuild, and the Style{Tuple} conversion; restore kwarg-based layout constructors - Restrict the recursion_relation overrides to Core.kwcall methods, which keeps nested loop primitives allocation-free and cuts the compilation time of several unit tests by more than an order of magnitude - Use fieldoffset for struct field views (prefix-tuple sizeof includes trailing padding) and branch-free thread-pool sizes for JET cleanliness - Add unit tests for nested loops, reduction accuracy, masked reductions, 0-dimensional broadcasts, scalar broadcast allocations, and nested_view Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 parent f6e16a5 commit 3f5dc1c

86 files changed

Lines changed: 1330 additions & 698 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.buildkite/Manifest.toml

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

.buildkite/Project.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ ClimaCore = "d414da3d-4745-48bb-8d80-42e94e092884"
1212
ClimaCorePlots = "cf7c7e5a-b407-4c48-9047-11a94a308626"
1313
ClimaCoreTempestRemap = "d934ef94-cdd4-4710-83d6-720549644b70"
1414
ClimaCoreVTK = "c8b6d40d-e815-466f-95ae-c48aefa668fa"
15+
ClimaInterpolations = "dd0f122e-fa3b-47f3-bcf0-93bbc60d885e"
1516
ClimaParams = "5c42b081-d73a-476f-9059-fd94b934656c"
1617
ClimaTimeSteppers = "595c0a79-7f3d-439a-bc5a-b232dc3bde79"
1718
Colors = "5ae59095-9a9b-59fe-a467-6f913c188581"

.buildkite/pipeline.yml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,16 @@ steps:
114114
retry: *retry_policy
115115
command: "julia --threads=4 --color=yes --check-bounds=yes --project=.buildkite test/DataLayouts/unit_fill_and_copyto.jl"
116116

117+
- label: "Unit: data loops (1 thread)"
118+
key: unit_data_loops
119+
retry: *retry_policy
120+
command: "julia --color=yes --check-bounds=yes --project=.buildkite test/DataLayouts/unit_loops.jl"
121+
122+
- label: "Unit: data loops (4 threads)"
123+
key: threaded_unit_data_loops
124+
retry: *retry_policy
125+
command: "julia --threads=4 --color=yes --check-bounds=yes --project=.buildkite test/DataLayouts/unit_loops.jl"
126+
117127
- label: "Unit: mapreduce (1 thread)"
118128
key: unit_data_mapreduce
119129
retry: *retry_policy

docs/src/APIs/datalayouts_api.md

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,85 @@
44
CurrentModule = ClimaCore
55
```
66

7+
## Data layouts
8+
79
```@docs
810
DataLayouts
11+
DataLayouts.DataLayout
912
DataLayouts.DataF
13+
DataLayouts.VIJHWithF
1014
DataLayouts.VIJFH
1115
DataLayouts.VIJHF
1216
DataLayouts.VIH1
1317
DataLayouts.IH1JH2
18+
```
19+
20+
## Layout properties
21+
22+
```@docs
23+
DataLayouts.layout_type
24+
DataLayouts.parent_type
25+
DataLayouts.f_dim
26+
DataLayouts.shape_params
27+
DataLayouts.inferred_size
28+
DataLayouts.has_inferred_size
29+
DataLayouts.vijh_params
30+
DataLayouts.nlevels
31+
DataLayouts.nquadpoints
32+
DataLayouts.nelems
33+
DataLayouts.ncomponents
34+
DataLayouts.layout_constructor
35+
DataLayouts.rebuild
36+
DataLayouts.reassign
37+
```
38+
39+
## Data scopes
40+
41+
```@docs
42+
DataLayouts.DataScope
43+
DataLayouts.ThisThread
44+
DataLayouts.ThisThreadPool
45+
DataLayouts.partition
46+
DataLayouts.is_subscope
47+
DataLayouts.num_threads
48+
DataLayouts.num_partitions
49+
DataLayouts.thread_rank
50+
DataLayouts.partition_rank
51+
DataLayouts.parallelize_over
52+
DataLayouts.synchronize
53+
DataLayouts.scoped_array
54+
DataLayouts.scoped_static_array
55+
DataLayouts.strided_access
56+
DataLayouts.subscope_indices
57+
```
58+
59+
## Loops and reductions
60+
61+
```@docs
62+
DataLayouts.each_slice_index
63+
DataLayouts.slice_subscope
64+
DataLayouts.foreach_slice
65+
DataLayouts.foreach_point
66+
DataLayouts.foreach_level
67+
DataLayouts.foreach_slab
68+
DataLayouts.foreach_column
69+
DataLayouts.reduce_points
70+
DataLayouts.column_reduce!
71+
```
72+
73+
## Masks
74+
75+
```@docs
76+
DataLayouts.DataMask
77+
DataLayouts.NoMask
78+
DataLayouts.IJHMask
79+
DataLayouts.set_mask_maps!
80+
DataLayouts.should_compute
81+
```
82+
83+
## Struct storage
84+
85+
```@docs
1486
DataLayouts.bitcast_struct
1587
DataLayouts.default_basetype
1688
DataLayouts.check_basetype
@@ -19,4 +91,14 @@ DataLayouts.num_basetypes
1991
DataLayouts.struct_field_view
2092
DataLayouts.set_struct!
2193
DataLayouts.get_struct
94+
DataLayouts.view_struct
95+
```
96+
97+
## Broadcasting
98+
99+
```@docs
100+
DataLayouts.DataStyle
101+
DataLayouts.LazyDataLayout
102+
DataLayouts.layout_args
103+
DataLayouts.modify_args
22104
```

docs/src/APIs/utilities_api.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ Utilities.is_inferred_type
1313
Utilities.return_type
1414
Utilities.unsafe_eltype
1515
Utilities.safe_eltype
16+
Utilities.nested_view
1617
```
1718

1819
## Utilities.PlusHalf

examples/hybrid/driver.jl

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -91,16 +91,16 @@ if haskey(ENV, "RESTART_FILE")
9191
ᶠlocal_geometry = Fields.local_geometry_field(Y.f)
9292
else
9393
t_start = FT(0)
94-
horizontal_layout_types = Dict()
95-
horizontal_layout_types["VIJFH"] = DataLayouts.VIJFH
96-
horizontal_layout_types["VIJHF"] = DataLayouts.VIJHF
97-
horizontal_layout_type =
98-
horizontal_layout_types[get(ENV, "horizontal_layout_type", "VIJFH")]
94+
VIJHs = Dict()
95+
VIJHs["VIJFH"] = DataLayouts.VIJFH
96+
VIJHs["VIJHF"] = DataLayouts.VIJHF
97+
VIJH =
98+
VIJHs[get(ENV, "horizontal_layout_type", "VIJFH")]
9999
h_space = make_horizontal_space(
100100
horizontal_mesh,
101101
npoly,
102102
comms_ctx,
103-
horizontal_layout_type,
103+
VIJH,
104104
)
105105
center_space, face_space =
106106
make_hybrid_spaces(h_space, z_max, z_elem; z_stretch)

ext/ClimaCoreCUDAExt.jl

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -18,19 +18,19 @@ import ClimaCore.Utilities: half, new, return_type
1818
include(joinpath("cuda", "adapt.jl"))
1919
include(joinpath("cuda", "cuda_utils.jl"))
2020
include(joinpath("cuda", "data_layouts.jl"))
21-
# include(joinpath("cuda", "fields.jl"))
21+
include(joinpath("cuda", "fields.jl"))
2222
include(joinpath("cuda", "topologies_dss.jl"))
23-
# include(joinpath("cuda", "operators_finite_difference.jl"))
24-
# include(joinpath("cuda", "remapping_distributed.jl"))
25-
# include(joinpath("cuda", "operators_integral.jl"))
26-
# include(joinpath("cuda", "remapping_interpolate_array.jl"))
27-
# include(joinpath("cuda", "limiters.jl"))
28-
# include(joinpath("cuda", "operators_sem_shmem.jl"))
29-
# include(joinpath("cuda", "operators_fd_shmem_common.jl"))
30-
# include(joinpath("cuda", "operators_fd_shmem.jl"))
31-
# include(joinpath("cuda", "operators_columnwise.jl"))
32-
# include(joinpath("cuda", "matrix_fields_single_field_solve.jl"))
33-
# include(joinpath("cuda", "matrix_fields_multiple_field_solve.jl"))
34-
# include(joinpath("cuda", "operators_spectral_element.jl"))
23+
include(joinpath("cuda", "operators_finite_difference.jl"))
24+
include(joinpath("cuda", "remapping_distributed.jl"))
25+
include(joinpath("cuda", "operators_integral.jl"))
26+
include(joinpath("cuda", "remapping_interpolate_array.jl"))
27+
include(joinpath("cuda", "limiters.jl"))
28+
include(joinpath("cuda", "operators_sem_shmem.jl"))
29+
include(joinpath("cuda", "operators_fd_shmem_common.jl"))
30+
include(joinpath("cuda", "operators_fd_shmem.jl"))
31+
include(joinpath("cuda", "operators_columnwise.jl"))
32+
include(joinpath("cuda", "matrix_fields_single_field_solve.jl"))
33+
include(joinpath("cuda", "matrix_fields_multiple_field_solve.jl"))
34+
include(joinpath("cuda", "operators_spectral_element.jl"))
3535

3636
end

ext/cuda/data_layouts.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,4 @@ import ClimaCore: DataLayouts
55

66
include("scopes.jl")
77
include("loops.jl")
8-
# include("data_layouts_threadblock.jl")
8+
include("data_layouts_threadblock.jl")

ext/cuda/data_layouts_threadblock.jl

Lines changed: 33 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -4,37 +4,41 @@ maximum_allowable_threads() = (
44
CUDA.attribute(CUDA.device(), CUDA.DEVICE_ATTRIBUTE_MAX_BLOCK_DIM_Z),
55
)
66

7+
# Wrappers for sizes and indices that are passed to kernels as type parameters.
8+
@inline unval(x) = x
9+
@inline unval(::Val{x}) where {x} = x
10+
711
"""
8-
universal_index(::DataLayout)
12+
linear_thread_idx()
913
10-
Returns a universal cartesian index,
11-
computed from CUDA's `threadIdx`,
12-
`blockIdx` and `blockDim`.
14+
Returns the linear index of the current thread across all blocks, computed from
15+
CUDA's `threadIdx`, `blockIdx` and `blockDim`.
1316
"""
14-
function universal_index end
17+
@inline linear_thread_idx() =
18+
(CUDA.blockIdx().x - Int32(1)) * CUDA.blockDim().x + CUDA.threadIdx().x
1519

1620
##### Masked
17-
@inline cartesian_indices_mask(us::DataLayouts.UniversalSize, mask::IJHMask) =
18-
cartesian_indices_mask(us, typeof(mask), mask.N[1])
21+
@inline cartesian_indices_mask(data, mask::IJHMask) =
22+
cartesian_indices_mask(data, typeof(mask), mask.N[1])
1923

2024
@inline function cartesian_indices_mask(
21-
us::DataLayouts.UniversalSize,
25+
data,
2226
::Type{<:IJHMask},
2327
n_active_columns::Integer,
2428
)
25-
(Ni, _, _, Nv, Nh) = DataLayouts.universal_size(us)
29+
Nv = size(data, 1)
2630
return CartesianIndices((1:Nv, 1:n_active_columns))
2731
end
28-
@inline masked_partition(mask::IJHMask, n_max_threads, us) =
29-
masked_partition(typeof(mask), mask.N[1], n_max_threads, us)
32+
@inline masked_partition(mask::IJHMask, n_max_threads, data) =
33+
masked_partition(typeof(mask), mask.N[1], n_max_threads, data)
3034

3135
@inline function masked_partition(
3236
::Type{<:IJHMask},
3337
n_active_columns,
3438
n_max_threads,
35-
us,
39+
data,
3640
)
37-
(_, _, _, Nv, _) = DataLayouts.universal_size(us)
41+
Nv = size(data, 1)
3842
nitems = n_active_columns * Nv
3943
return linear_partition(nitems, n_max_threads)
4044
end
@@ -60,39 +64,28 @@ end
6064
blocks = cld(nitems, threads)
6165
return (; threads, blocks)
6266
end
63-
@inline function cartesian_indices(us::UniversalSize)
64-
inds = DataLayouts.universal_size(us)
65-
return CartesianIndices(map(Base.OneTo, inds))
66-
end
67-
@inline linear_is_valid_index(i::Integer, us::UniversalSize) =
68-
1 i DataLayouts.get_N(us)
67+
@inline cartesian_indices(data) =
68+
CartesianIndices(map(Base.OneTo, size(data)))
69+
@inline linear_is_valid_index(i::Integer, data) = 1 i length(data)
6970

7071
##### Column-wise
71-
@inline function cartesian_indices_columnwise(us::DataLayouts.UniversalSize)
72-
(Ni, Nj, _, _, Nh) = DataLayouts.universal_size(us)
73-
inds = (Ni, Nj, Nh)
74-
return CartesianIndices(map(Base.OneTo, inds))
72+
@inline function cartesian_indices_columnwise(data)
73+
(_, Ni, Nj, Nh) = size(data)
74+
return CartesianIndices(map(Base.OneTo, (Ni, Nj, Nh)))
7575
end
7676

7777
##### Element-wise (e.g., limiters)
7878
# TODO
7979

8080
##### Multiple-field solve partition
81-
@inline function cartesian_indices_multiple_field_solve(
82-
us::DataLayouts.UniversalSize;
83-
Nnames,
84-
)
85-
(Ni, Nj, _, _, Nh) = DataLayouts.universal_size(us)
86-
inds = (Ni, Nj, Nh, Nnames)
87-
return CartesianIndices(map(Base.OneTo, inds))
81+
@inline function cartesian_indices_multiple_field_solve(data; Nnames)
82+
(_, Ni, Nj, Nh) = size(data)
83+
return CartesianIndices(map(Base.OneTo, (Ni, Nj, Nh, Nnames)))
8884
end
8985

9086
##### spectral kernel partition
91-
@inline function spectral_partition(
92-
us::DataLayouts.UniversalSize,
93-
n_max_threads::Integer = 256;
94-
)
95-
(Ni, Nj, _, Nv, Nh) = DataLayouts.universal_size(us)
87+
@inline function spectral_partition(data, n_max_threads::Integer = 256)
88+
(Nv, Ni, Nj, Nh) = size(data)
9689
Nvthreads = min(fld(n_max_threads, Ni * Nj), maximum_allowable_threads()[3])
9790
Nvblocks = cld(Nv, Nvthreads)
9891
@assert prod((Ni, Nj, Nvthreads)) n_max_threads "threads,n_max_threads=($(prod((Ni, Nj, Nvthreads))),$n_max_threads)"
@@ -121,11 +114,11 @@ end
121114

122115
##### shmem fd kernel partition
123116
@inline function fd_shmem_stencil_partition(
124-
us::DataLayouts.UniversalSize,
117+
data,
125118
n_face_levels::Integer,
126119
n_max_threads::Integer = 256;
127120
)
128-
(Ni, Nj, _, Nv, Nh) = DataLayouts.universal_size(us)
121+
(Nv, Ni, Nj, Nh) = size(data)
129122
Nvthreads = n_face_levels
130123
@assert Nvthreads <= maximum_allowable_threads()[1] "Number of vertical face levels cannot exceed $(maximum_allowable_threads()[1])"
131124
Nvblocks = cld(Nv, Nvthreads) # +1 may be needed to guarantee that shared memory is populated at the last cell face
@@ -135,19 +128,15 @@ end
135128
Nvthreads,
136129
)
137130
end
138-
@inline function fd_shmem_stencil_universal_index(
139-
space::Spaces.AbstractSpace,
140-
us,
141-
)
131+
@inline function fd_shmem_stencil_universal_index(space::Spaces.AbstractSpace, data)
142132
(tv,) = CUDA.threadIdx()
143133
(h, bv, ij) = CUDA.blockIdx()
144134
v = tv + (bv - 1) * CUDA.blockDim().x
145-
(Ni, Nj, _, _, _) = DataLayouts.universal_size(us)
135+
(_, Ni, Nj, _) = size(data)
146136
if Ni * Nj < ij
147137
return CartesianIndex((-1, -1, -1, -1))
148138
end
149139
@inbounds (i, j) = CartesianIndices((Ni, Nj))[ij].I
150140
return CartesianIndex((v, i, j, h))
151141
end
152-
@inline fd_shmem_stencil_is_valid_index(I, us::UniversalSize) =
153-
1 I[5] DataLayouts.get_Nh(us)
142+
@inline fd_shmem_stencil_is_valid_index(I, data) = 1 I[4] size(data, 4)

0 commit comments

Comments
 (0)