Skip to content

Commit 019cd25

Browse files
dennisYatuninclaude
andcommitted
Make the unified DataLayouts API fast and correct on CPUs and GPUs [perf]
Verifies and refines the DataLayouts rewrite across CPUs, GPUs, docs, tests, and downstream packages. - Point indexing: getindex/setindex!/view constant-fold the Cartesian-to-linear conversion and use a constant-stride linear index into the parent of a property-view SubArray, bypassing Base's per-access column-major arithmetic and its linear-to-Cartesian reindex (div/rem, SignedMultiplicativeInverse). This bounds the deeply-inlined finite-difference stencil body that compile-killed the ClimaAtmos EDMF Larcform1 GPU job (OOM): implicit_tendency! compile ~110s -> ~32s, with column FD-op runtime gains and bit-identical values. The offset folds on device via unrolled_reduce (no InvalidIRError). - Property views / IndexStyle: build slice and property SubArrays from Colons (Base.Slice) so VIJHF views are fast-linear (IndexLinear) while VIJFH views stay IndexCartesian; IndexStyle defers to the parent, so pointwise broadcasts never linear-index an IndexCartesian SubArray. - GPU support: DataScopes map onto the CUDA execution hierarchy, loops and reductions launch through auto_launch! with occupancy-based configurations, and Utilities.stable_view keeps slice/property views inference-stable (kernel arguments and closures follow isbits rules). Each view is adapted to a compact device view (Int32 offsets instead of a SubArray) so large EDMF broadcasts fit the sm_60 4 KiB kernel-parameter limit, and DataScopes combine by pairwise recursion so inference does not widen scopes to Any. - CPU runtime matches main: unmasked point loops vectorize under @simd with an inlined point function, nested loops avoid closure allocations, and GPU point loops iterate each thread's strided CartesianIndices subset through an indexable isbits wrapper, keeping kernel launch latency at main's level. - Compile time: @maybe_propagate_inbounds requests inlining of stencil expression nodes only when check-bounds is off, taking the FCT advection examples from 40-58 minutes to about a minute in CI. - Reductions and masks: order-insensitive pairwise safe_mapreduce (no linear-indexing assumption), masks are keyword arguments, equality ignores padding, and field2array / the distributed HDF5 writer handle all layout shapes. Fix the GPU MPI DSS exchange buffer index that overlapped items when Nv > 1 and Nf > 1, and apply the QuasiMonotoneLimiter through one scalar view per component (avoiding an uncompilable reshape of a device SubArray). - Compatibility, docs, tests: deprecation aliases (DataLayouts/deprecated.jl, Fields.ColumnField) and the universal CartesianIndex{5} convention keep downstream packages working; zero-size fields stay hidden from propertynames; docs and NEWS are expanded and Aqua ambiguities resolved; tests restore check_basetype, benchmark_fill, and the VIJFH-F64 stencil set, and add DataLayouts mask/reduction jobs, device-aware dss tests, and latency baselines at their original tolerance. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent 31c1013 commit 019cd25

113 files changed

Lines changed: 3064 additions & 1523 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/perf/pipeline.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ steps:
2626
# Instantiate and dev install ClimaCore
2727
- julia --project=$COUPLER_PATH/experiments/AMIP/ -e 'using Pkg; Pkg.instantiate(;verbose=true)'
2828
- julia --project=$COUPLER_PATH/experiments/AMIP/ -e 'using Pkg; Pkg.develop(path=".")'
29+
- julia --project=$COUPLER_PATH/experiments/AMIP/ -e 'using Pkg; Pkg.develop(path="lib/ClimaCoreMakie")'
2930
- julia --project=$COUPLER_PATH/experiments/AMIP/ -e 'using Pkg; Pkg.add("MPI")'
3031
- julia --project=$COUPLER_PATH/experiments/AMIP/ -e 'using Pkg; Pkg.precompile()'
3132
- julia --project=$COUPLER_PATH/experiments/AMIP/ -e 'using Pkg; Pkg.status()'

.buildkite/pipeline.yml

Lines changed: 26 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
@@ -1568,6 +1578,22 @@ steps:
15681578
- group: "Perf: DataLayouts"
15691579
steps:
15701580

1581+
- label: "Perf: DataLayouts fill!"
1582+
key: "cpu_datalayouts_fill"
1583+
retry: *retry_policy
1584+
command: "julia --color=yes --project=.buildkite test/DataLayouts/benchmark_fill.jl"
1585+
1586+
- label: "Perf: DataLayouts fill"
1587+
key: "gpu_datalayouts_fill"
1588+
retry: *retry_policy
1589+
command:
1590+
- "julia --project=.buildkite -e 'using CUDA; CUDA.versioninfo()'"
1591+
- "julia --color=yes --project=.buildkite test/DataLayouts/benchmark_fill.jl"
1592+
env:
1593+
CLIMACOMMS_DEVICE: "CUDA"
1594+
agents:
1595+
slurm_gpus: 1
1596+
15711597
- label: "Perf: DataLayouts copyto!"
15721598
key: "cpu_datalayouts_copyto"
15731599
retry: *retry_policy

.github/workflows/docs.yml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,11 @@ jobs:
1818
- name: Install dependencies
1919
run: >
2020
julia --project=docs -e 'using Pkg; Pkg.develop(path=".");
21-
Pkg.develop(path="lib/ClimaCoreVTK");
21+
Pkg.develop(path="lib/ClimaCoreVTK");
22+
Pkg.develop(path="lib/ClimaCoreTempestRemap");
23+
Pkg.develop(path="lib/ClimaCoreMakie");
24+
Pkg.develop(path="lib/ClimaCorePlots");
25+
Pkg.develop(path="lib/ClimaCoreSpectra");
2226
Pkg.instantiate(;verbose=true)'
2327
- name: Build and deploy
2428
env:

NEWS.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,17 @@ ClimaCore.jl Release Notes
44
main
55
-------
66

7+
- Refactor DataLayouts module [2522](https://github.com/CliMA/ClimaCore.jl/pull/2522)
8+
- All data layout types are unified into a single `DataLayout` type, and all
9+
loops over data go through two communication primitives (`foreach_slice` and
10+
`reduce_points`) that work the same way on CPUs, multi-threaded CPUs, and
11+
GPUs. Runtime performance on CPUs and GPUs matches the previous release.
12+
- Data in a `Field` is now indexed in the order `[v, i, j, h]`, and every
13+
parent array has a 5-dimensional shape, with one axis used to store struct
14+
fields. Code that indexes into `parent(field)` arrays must be updated.
15+
- Several old names (`AbstractData`, `IJFH`, `IJHF`) are available as aliases,
16+
and `[i, j, f, v, h]` indexing is still allowed for backward compatibility.
17+
718
v0.14.55
819
-------
920

docs/src/APIs/datalayouts_api.md

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

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

docs/src/APIs/dss_api.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ Topologies.dss_transform
99
Topologies.dss_transform!
1010
Topologies.dss_untransform!
1111
Topologies.dss_untransform
12-
Topologies.dss_local_vertices!
1312
Topologies.dss_local!
1413
Topologies.dss_local_ghost!
1514
Topologies.dss_ghost!

docs/src/APIs/utilities_api.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ CurrentModule = ClimaCore
55
```
66

77
```@docs
8+
Utilities.stable_view
89
Utilities.unionall_type
910
Utilities.replace_type_parameter
1011
Utilities.fieldtype_vals
@@ -13,6 +14,7 @@ Utilities.is_inferred_type
1314
Utilities.return_type
1415
Utilities.unsafe_eltype
1516
Utilities.safe_eltype
17+
Utilities.safe_mapreduce
1618
```
1719

1820
## Utilities.PlusHalf

docs/src/debugging.md

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ function ClimaCore.DebugOnly.post_op_callback(result, args...; kwargs...)
6969
end
7070
7171
FT = Float64
72-
data = ClimaCore.DataLayouts.VIJFH{FT}(Array{FT}, zeros; Nv=5, Nij=2, Nh=2)
72+
data = ClimaCore.DataLayouts.VIJFH{FT, 5, 2, 2, 2}(Array{FT})
7373
@. data = NaN
7474
ClimaCore.DebugOnly.call_post_op_callback() = false # hide
7575
```
@@ -221,8 +221,10 @@ function ClimaCore.DebugOnly.post_op_callback(result, args...; kwargs...)
221221
end
222222

223223
FT = Float64
224-
data = ClimaCore.DataLayouts.VIJFH{FT}(Array{FT}, zeros; Nv=5, Nij=2, Nh=2)
225-
x = ClimaCore.DataLayouts.VIJFH{FT}(Array{FT}, zeros; Nv=5, Nij=2, Nh=2)
224+
data = ClimaCore.DataLayouts.VIJFH{FT, 5, 2, 2, 2}(Array{FT})
225+
x = ClimaCore.DataLayouts.VIJFH{FT, 5, 2, 2, 2}(Array{FT})
226+
fill!(parent(data), 0)
227+
fill!(parent(x), 0)
226228
parent(x)[1] = NaN # emulate incorrect initialization
227229
@. data = x + 1
228230
# Let's see what happened

0 commit comments

Comments
 (0)