Skip to content

Gpu storage improvements - #712

Draft
ameligrana wants to merge 6 commits into
mainfrom
gpu-storage-improvements
Draft

Gpu storage improvements#712
ameligrana wants to merge 6 commits into
mainfrom
gpu-storage-improvements

Conversation

@ameligrana

Copy link
Copy Markdown
Member

No description provided.

- Allow pairing the back-end with a zero-based device ordinal
  (Storage{GPUVector{(:CUDA, 1)}}) or passing a device object
  (Storage(GPUVector{:CUDA}, CuDevice(1))) to pin a GPU storage
  to a specific device. Allocations and growth happen on that device.
- Route wrapper-level unsafe_copyto! between GPUVectors through the
  raw memory arrays (device-to-device) instead of the host.
- Check copyto! bounds against logical lengths, matching Base semantics.
- Use M(undef, 0) instead of M() in the empty GPUVector constructor.
GPU back-ends (CUDA, Metal, oneAPI, OpenCL) contribute their memory type
through a weak dependency extension. The World constructor is a generated
function, and generated bodies dispatch in the world in which Ark was
loaded, i.e. before any extension is available. Storage types that embed
the back-end memory type could therefore not be computed at
world-construction time, and constructing a world with a GPU storage
(e.g. World(P => Storage{GPUVector{:CUDA}})) failed for every GPU back-end.

Fix by not deriving the memory type at world-construction time at all:

- Storage types in the world schema now use the memory-type-erased
  GPUVector{B,T} for GPU back-ends (the :CPU back-end keeps its concrete
  type); GPUStructArray field vectors likewise.
- Concrete GPUVector{B,T,M} storages are constructed at runtime, where
  extension dispatch works.
- GPUVectorView always carries the erased parent type GPUVector{B,T}, so
  static and runtime view types are identical regardless of the back-end.

Also add a regression test using a back-end registered after Ark is
loaded, which reproduces the world-age failure without a GPU.
Storage modes are now specified with a function call, like
World(Position => Storage(StructArray)), instead of the type parameter
syntax Storage{StructArray}. Storage(x) returns the Storage{x} phantom
type, which remains an internal detail.

This unifies the API with device selection: Storage(GPUVector{:CUDA},
CuDevice(1)) and Storage(GPUStructArray{(:CUDA, 1)}) now compose with
World(...) exactly like every other storage mode.
@codecov

codecov Bot commented Aug 16, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@github-actions

github-actions Bot commented Aug 16, 2026

Copy link
Copy Markdown
Contributor

boxed = false

🚀 7 benchmark improvements detected!

⚠️ 2 benchmark regressions detected!

Click to expand benchmark results

Time is per entity/N, allocations are totals. Allocations are only shown for current.

N       Time main             Time curr           Factor         Allocs         Bytes    
query_create
1000 5.52ns 5.51ns 1.00 0 0
query_create_filter
1000 5.51ns 5.51ns 1.00 0 0
query_posvel_1k_arch
100 5.46ns 5.65ns 1.03 0 0
1000 6.85ns 7.26ns 1.07 0 0
10000 1.98ns 2.17ns 1.09 0 0
100000 0.84ns 0.88ns 1.06 0 0
1000000 0.86ns 1.07ns ⚠️ 1.22 0 0
query_posvel_1k_arch_cached
100 6.19ns 6.41ns 1.03 0 0
1000 7.77ns 7.98ns 1.04 0 0
10000 2.19ns 2.28ns 1.03 0 0
100000 0.86ns 0.84ns 0.99 0 0
1000000 0.88ns 1.23ns ⚠️ 1.40 0 0
query_posvel_32_arch
100 1.98ns 1.96ns 0.99 0 0
1000 0.67ns 0.66ns 0.99 0 0
10000 0.41ns 0.41ns 1.00 0 0
100000 0.50ns 0.45ns 0.92 0 0
1000000 0.58ns 0.57ns 0.98 0 0
query_posvel_cold
100 0.90ns 0.90ns 1.00 0 0
1000 0.37ns 0.37ns 1.00 0 0
10000 0.35ns 0.36ns 1.03 0 0
100000 0.53ns 0.55ns 1.05 0 0
1000000 0.74ns 0.72ns 0.98 0 0
query_posvel_fields
100 0.47ns 0.47ns 0.99 0 0
1000 0.23ns 0.23ns 0.99 0 0
10000 0.30ns 0.30ns 1.00 0 0
100000 0.45ns 0.39ns 🚀 0.88 0 0
1000000 0.55ns 0.55ns 1.00 0 0
query_posvel_fields_broadcast
100 1.60ns 1.60ns 1.00 0 0
1000 1.49ns 1.49ns 1.00 0 0
10000 1.59ns 1.57ns 0.99 0 0
100000 1.77ns 1.60ns 🚀 0.86 0 0
1000000 2.05ns 2.14ns 1.04 0 0
query_posvel_hot
100 0.61ns 0.58ns 0.95 0 0
1000 0.24ns 0.24ns 1.00 0 0
10000 0.30ns 0.30ns 1.00 0 0
100000 0.44ns 0.40ns 0.93 0 0
1000000 0.59ns 0.56ns 0.94 0 0
query_posvel_mean
100 0.95ns 0.95ns 0.99 0 0
100000 0.88ns 0.88ns 1.00 0 0
query_posvel_mean_readonly
100 0.95ns 0.95ns 1.00 0 0
100000 0.89ns 0.89ns 1.00 0 0
query_posvel_soa
100 1.38ns 1.34ns 0.97 0 0
1000 1.22ns 1.20ns 0.98 0 0
10000 1.23ns 1.22ns 0.99 0 0
100000 1.23ns 1.23ns 1.00 0 0
1000000 1.25ns 1.25ns 1.00 0 0
query_posvel_soa_unpack
100 0.46ns 0.46ns 1.00 0 0
1000 0.24ns 0.24ns 1.00 0 0
10000 0.31ns 0.30ns 0.99 0 0
100000 0.45ns 0.39ns 🚀 0.87 0 0
1000000 0.56ns 0.52ns 0.95 0 0
world_add_remove_1
100 31.28ns 32.12ns 1.03 0 0
10000 31.31ns 31.65ns 1.01 0 0
world_add_remove_1_batch
100 3.85ns 4.14ns 1.08 0 0
10000 2.46ns 2.50ns 1.01 0 0
world_add_remove_1_buffer
100 33.40ns 33.36ns 1.00 0 0
10000 33.89ns 33.78ns 1.00 0 0
world_add_remove_1_buffer_single
100 38.10ns 36.91ns 0.97 0 0
10000 37.38ns 36.43ns 0.97 0 0
world_add_remove_1_large
100 42.65ns 42.16ns 0.99 0 0
10000 43.30ns 42.97ns 0.99 0 0
world_add_remove_1_soa
100 34.44ns 34.52ns 1.00 0 0
10000 34.89ns 35.24ns 1.01 0 0
world_add_remove_8
100 52.14ns 52.64ns 1.01 0 0
10000 53.51ns 53.99ns 1.01 0 0
world_add_remove_8_large
100 67.77ns 67.47ns 1.00 0 0
10000 69.06ns 68.69ns 1.00 0 0
world_add_remove_8_soa
100 70.61ns 71.51ns 1.01 0 0
10000 72.41ns 73.54ns 1.02 0 0
world_copy_entity_5
100 26.55ns 24.59ns 0.93 0 0
10000 26.71ns 24.84ns 0.93 0 0
world_get_1
100 0.96ns 0.97ns 1.00 0 0
10000 0.88ns 0.89ns 1.01 0 0
world_get_1_soa
100 1.57ns 1.56ns 0.99 0 0
10000 1.49ns 1.49ns 1.00 0 0
world_get_5
100 2.69ns 2.69ns 1.00 0 0
10000 2.63ns 2.63ns 1.00 0 0
world_get_5_unchecked
100 1.66ns 1.66ns 1.00 0 0
10000 1.63ns 1.63ns 1.00 0 0
world_get_rel
100 0.94ns 0.95ns 1.01 0 0
10000 0.88ns 0.88ns 1.00 0 0
world_has_1
100 0.86ns 0.86ns 1.00 0 0
10000 0.79ns 0.79ns 1.00 0 0
world_has_5
100 0.96ns 0.96ns 1.00 0 0
10000 0.89ns 0.89ns 1.00 0 0
world_new_entities_1
100 3.01ns 3.00ns 1.00 0 0
10000 2.44ns 2.42ns 0.99 0 0
world_new_entities_1_def
100 3.01ns 3.01ns 1.00 0 0
10000 2.54ns 2.54ns 1.00 0 0
world_new_entities_5
100 4.86ns 4.86ns 1.00 0 0
10000 3.92ns 3.90ns 1.00 0 0
world_new_entities_5_def
100 4.71ns 4.71ns 1.00 0 0
10000 3.94ns 3.90ns 0.99 0 0
world_new_entity_1
100 15.38ns 11.27ns 🚀 0.80 0 0
10000 15.37ns 11.26ns 🚀 0.80 0 0
world_new_entity_1_rel
100 36.51ns 35.86ns 0.98 0 0
10000 36.67ns 35.82ns 0.98 0 0
world_new_entity_1_soa
100 16.82ns 12.83ns 🚀 0.82 0 0
10000 16.70ns 12.64ns 🚀 0.81 0 0
world_new_entity_5
100 21.99ns 19.73ns 0.91 0 0
10000 22.19ns 19.88ns 0.91 0 0
world_new_entity_5_rel
100 53.30ns 48.44ns 0.92 0 0
10000 53.71ns 49.01ns 0.92 0 0
world_new_entity_5_soa
100 30.55ns 28.25ns 0.94 0 0
10000 31.41ns 29.01ns 0.94 0 0
world_partition
100 15.08ns 15.23ns 1.01 0 0
10000 24.79ns 25.27ns 1.02 0 0
world_posvel
100 2.91ns 2.80ns 0.97 0 0
10000 3.05ns 2.96ns 0.98 0 0
world_remove_entities_5
100 1.91ns 1.96ns 1.02 0 0
10000 0.91ns 0.91ns 1.00 0 0
world_remove_entity_5
100 17.07ns 17.28ns 1.01 0 0
10000 16.87ns 16.92ns 1.00 0 0
world_resource
1 7.47ns 7.52ns 1.01 0 0
world_set_1
100 2.42ns 2.42ns 1.00 0 0
10000 2.52ns 2.52ns 1.00 0 0
world_set_1_soa
100 2.31ns 2.31ns 1.00 0 0
10000 2.40ns 2.40ns 1.00 0 0
world_set_5
100 10.22ns 10.22ns 1.00 0 0
10000 11.07ns 11.09ns 1.00 0 0
world_set_rel
100 39.27ns 39.12ns 1.00 0 0
10000 39.12ns 39.02ns 1.00 0 0
world_set_rel_batch
100 2.25ns 2.46ns 1.10 0 0
10000 0.67ns 0.67ns 1.00 0 0
world_shuffle
100 52.66ns 54.19ns 1.03 0 0
10000 68.38ns 68.97ns 1.01 0 0
world_sort
100 59.05ns 59.15ns 1.00 0 0
10000 211.03ns 208.78ns 0.99 0 0
world_update_1
100 2.89ns 3.10ns 1.05 0 0
10000 3.03ns 3.24ns 1.05 0 0
world_update_5
100 7.62ns 7.60ns 1.00 0 0
10000 8.18ns 8.17ns 1.00 0 0

boxed = true

⚠️ 9 benchmark regressions detected!

Click to expand benchmark results

Time is per entity/N, allocations are totals. Allocations are only shown for current.

N       Time main             Time curr           Factor         Allocs         Bytes    
query_create
1000 13.16ns 13.15ns 1.00 0 0
query_create_filter
1000 13.15ns 13.15ns 1.00 0 0
query_posvel_1k_arch
100 4.54ns 4.56ns 1.00 0 0
1000 4.90ns 4.95ns 1.01 0 0
10000 1.30ns 1.31ns 1.01 0 0
100000 1.04ns 1.05ns 1.02 0 0
1000000 0.99ns 0.97ns 0.98 0 0
query_posvel_1k_arch_cached
100 4.83ns 4.80ns 0.99 0 0
1000 5.28ns 5.20ns 0.98 0 0
10000 1.39ns 1.40ns 1.01 0 0
100000 1.06ns 1.05ns 0.99 0 0
1000000 1.02ns 1.01ns 0.99 0 0
query_posvel_32_arch
100 1.71ns 1.71ns 1.00 0 0
1000 0.54ns 0.54ns 1.00 0 0
10000 0.36ns 0.36ns 1.01 0 0
100000 0.70ns 0.69ns 0.99 0 0
1000000 0.79ns 0.83ns 1.06 0 0
query_posvel_cold
100 1.03ns 1.03ns 1.00 0 0
1000 0.39ns 0.39ns 1.00 0 0
10000 0.32ns 0.32ns 1.01 0 0
100000 0.73ns 0.78ns ⚠️ 1.11 0 0
1000000 1.13ns 1.20ns 1.09 0 0
query_posvel_fields
100 0.41ns 0.42ns 1.02 0 0
1000 0.17ns 0.16ns 0.99 0 0
10000 0.30ns 0.30ns 1.00 0 0
100000 0.66ns 0.67ns 1.01 0 0
1000000 0.75ns 0.82ns ⚠️ 1.14 0 0
query_posvel_fields_broadcast
100 1.87ns 1.90ns 1.01 0 0
1000 1.75ns 1.77ns 1.02 0 0
10000 1.82ns 1.82ns 1.00 0 0
100000 2.24ns 2.17ns 0.97 0 0
1000000 2.40ns 2.63ns 1.08 0 0
query_posvel_hot
100 0.55ns 0.55ns 0.99 0 0
1000 0.18ns 0.18ns 0.99 0 0
10000 0.30ns 0.30ns 1.00 0 0
100000 0.64ns 0.65ns 1.01 0 0
1000000 0.78ns 0.81ns 1.07 0 0
query_posvel_mean
100 0.74ns 0.73ns 0.98 0 0
100000 0.86ns 0.87ns 1.01 0 0
query_posvel_mean_readonly
100 0.74ns 0.73ns 0.99 0 0
100000 0.86ns 0.86ns 1.00 0 0
query_posvel_soa
100 1.17ns 1.16ns 1.00 0 0
1000 1.00ns 0.99ns 0.99 0 0
10000 1.02ns 1.02ns 1.00 0 0
100000 1.07ns 1.07ns 1.00 0 0
1000000 1.08ns 1.08ns 1.00 0 0
query_posvel_soa_unpack
100 0.42ns 0.42ns 0.99 0 0
1000 0.19ns 0.19ns 1.00 0 0
10000 0.30ns 0.30ns 1.00 0 0
100000 0.69ns 0.68ns 0.99 0 0
1000000 0.76ns 0.83ns ⚠️ 1.12 0 0
world_add_remove_1
100 48.64ns 48.93ns 1.01 0 0
10000 49.38ns 49.37ns 1.00 0 0
world_add_remove_1_batch
100 4.00ns 4.05ns 1.02 0 0
10000 2.39ns 2.41ns 1.01 0 0
world_add_remove_1_buffer
100 55.31ns 56.08ns 1.01 0 0
10000 56.14ns 56.49ns 1.01 0 0
world_add_remove_1_buffer_single
100 53.68ns 53.94ns 1.01 0 0
10000 54.67ns 54.76ns 1.00 0 0
world_add_remove_1_large
100 49.09ns 49.35ns 1.00 0 0
10000 49.36ns 49.74ns 1.01 0 0
world_add_remove_1_soa
100 54.33ns 54.44ns 1.00 0 0
10000 54.93ns 54.82ns 1.00 0 0
world_add_remove_8
100 75.67ns 75.93ns 1.00 0 0
10000 76.88ns 76.84ns 1.00 0 0
world_add_remove_8_large
100 77.18ns 76.46ns 0.99 0 0
10000 78.03ns 77.85ns 1.00 0 0
world_add_remove_8_soa
100 95.98ns 95.88ns 1.00 0 0
10000 98.64ns 98.37ns 1.00 0 0
world_copy_entity_5
100 66.89ns 67.21ns 1.01 0 0
10000 67.22ns 67.14ns 1.00 0 0
world_get_1
100 0.86ns 0.86ns 1.00 0 0
10000 0.85ns 0.85ns 1.00 0 0
world_get_1_soa
100 1.39ns 1.36ns 0.97 0 0
10000 1.33ns 1.35ns 1.01 0 0
world_get_5
100 4.05ns 3.94ns 0.97 0 0
10000 4.03ns 3.95ns 0.98 0 0
world_get_5_unchecked
100 2.66ns 2.66ns 1.00 0 0
10000 2.63ns 2.63ns 1.00 0 0
world_get_rel
100 0.81ns 0.80ns 1.00 0 0
10000 0.78ns 0.79ns 1.01 0 0
world_has_1
100 0.73ns 0.71ns 0.96 0 0
10000 0.70ns 0.69ns 0.99 0 0
world_has_5
100 0.80ns 0.85ns 1.07 0 0
10000 0.79ns 0.79ns 1.00 0 0
world_new_entities_1
100 3.21ns 3.28ns 1.02 0 0
10000 2.37ns 2.36ns 1.00 0 0
world_new_entities_1_def
100 3.10ns 3.28ns 1.06 0 0
10000 2.43ns 2.44ns 1.00 0 0
world_new_entities_5
100 5.71ns 5.57ns 0.98 0 0
10000 4.24ns 4.17ns 0.98 0 0
world_new_entities_5_def
100 5.26ns 5.14ns 0.98 0 0
10000 3.68ns 3.68ns 1.00 0 0
world_new_entity_1
100 10.55ns 10.82ns 1.03 0 0
10000 10.26ns 10.88ns 1.06 0 0
world_new_entity_1_rel
100 28.30ns 32.91ns ⚠️ 1.15 0 0
10000 28.06ns 32.70ns ⚠️ 1.14 0 0
world_new_entity_1_soa
100 11.89ns 11.92ns 1.00 0 0
10000 11.67ns 12.03ns 1.03 0 0
world_new_entity_5
100 19.20ns 21.90ns ⚠️ 1.13 0 0
10000 18.74ns 21.82ns ⚠️ 1.16 0 0
world_new_entity_5_rel
100 40.56ns 40.90ns 1.01 0 0
10000 39.66ns 40.53ns 1.02 0 0
world_new_entity_5_soa
100 26.43ns 30.36ns ⚠️ 1.14 0 0
10000 26.45ns 30.66ns ⚠️ 1.15 0 0
world_partition
100 27.74ns 28.01ns 1.01 0 0
10000 37.15ns 37.33ns 1.00 0 0
world_posvel
100 2.93ns 2.92ns 1.00 0 0
10000 3.08ns 3.07ns 1.00 0 0
world_remove_entities_5
100 2.47ns 2.49ns 1.01 0 0
10000 0.86ns 0.86ns 1.00 0 0
world_remove_entity_5
100 62.50ns 61.96ns 0.99 0 0
10000 63.11ns 62.21ns 0.98 0 0
world_resource
1 6.30ns 6.46ns 1.02 0 0
world_set_1
100 1.76ns 1.70ns 0.96 0 0
10000 1.85ns 1.95ns 1.06 0 0
world_set_1_soa
100 2.64ns 2.62ns 0.99 0 0
10000 2.74ns 2.73ns 1.00 0 0
world_set_5
100 6.07ns 5.94ns 0.98 0 0
10000 7.20ns 7.06ns 0.98 0 0
world_set_rel
100 39.89ns 41.66ns 1.04 0 0
10000 38.86ns 41.07ns 1.06 0 0
world_set_rel_batch
100 2.55ns 2.50ns 0.98 0 0
10000 0.80ns 0.79ns 1.00 0 0
world_shuffle
100 102.54ns 102.41ns 1.00 0 0
10000 115.39ns 114.16ns 0.99 0 0
world_sort
100 57.43ns 57.08ns 0.99 0 0
10000 202.06ns 202.20ns 1.00 0 0
world_update_1
100 2.28ns 2.27ns 1.00 0 0
10000 2.41ns 2.43ns 1.01 0 0
world_update_5
100 9.77ns 9.94ns 1.02 0 0
10000 10.75ns 10.92ns 1.02 0 0

- ArkOpenCLInterop: implement _gpuvector_pinned_device, _gpuvector_ordinal
  and _gpuvector_withdev for OpenCL. OpenCL.jl provides per-device task-local
  state via cl.device!, so device selection works like for the other back-ends.
- _new_gpuvector_storage now resolves the memory type inside the device switch,
  as OpenCL's unified-memory back-end is determined per device.
- Simplify _gpuvector_ordinal for CUDA to just CUDA.deviceid, matching the
  other back-ends (CuDevice is validated at construction).
- Update docs to list :OpenCL among the back-ends supporting device selection.
- Guard the GPU test helper structs with @isdefined, as the suite is included
  once per world mode (hard error on Julia 1.10/LTS otherwise).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant