Skip to content

Memoize the driver extension list and reduce launch-path overhead - #605

Open
michel2323 wants to merge 5 commits into
mainfrom
cache-launch-configuration
Open

Memoize the driver extension list and reduce launch-path overhead#605
michel2323 wants to merge 5 commits into
mainfrom
cache-launch-configuration

Conversation

@michel2323

@michel2323 michel2323 commented Aug 5, 2026

Copy link
Copy Markdown
Member

The KernelAbstractions backend reaches properties(::ZeKernel) through launch_configuration on every dispatch of a kernel with a dynamic workgroupsize, and extension_properties rebuilt the driver's extension list on each of those calls (the source carried a TODO: memoize there). On a launch-bound workload that query dominates the per-launch host cost.

Four independent changes:

  • Memoize the driver extension list. A driver's extension list is fixed for its lifetime and Level Zero has no driver-destroy entry point, so the answer is cached per driver (keyed by handle, emptied in __init__ so nothing is inherited from a precompiled image).
  • Fill arrays with a kernel instead of the memory-fill command. The Base.fill! specialization required a USM host allocation, a residency call, a free and a full queue synchronize per call; GPUArrays' generic kernel fill does none of this.
  • Account for spill memory when picking a group size. maxGroupSize ignores spillMemSize, so a heavily spilling kernel was launched at group sizes with very large per-group scratch demands; cap the group size to a fixed scratch budget.
  • Hold the kernel-property extension structs across the query. The pNext chain stores raw interior pointers the GC cannot see; GC.@preserve them like the neighbouring constructors do. Defensive, not a fix for an observed failure.

(This PR originally cached launch_configuration per kernel; memoizing extension_properties supersedes that — it removes the same cost and benefits every caller of properties.)

@github-actions

github-actions Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Your PR no longer requires formatting changes. Thank you for your contribution!

@michel2323
michel2323 force-pushed the cache-launch-configuration branch from 7c4dcc1 to 5f74421 Compare August 7, 2026 13:38
@codecov

codecov Bot commented Aug 7, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 80.22%. Comparing base (1de937a) to head (0690b28).

Additional details and impacted files
@@            Coverage Diff             @@
##             main     #605      +/-   ##
==========================================
+ Coverage   78.64%   80.22%   +1.58%     
==========================================
  Files          50       50              
  Lines        3488     3490       +2     
==========================================
+ Hits         2743     2800      +57     
+ Misses        745      690      -55     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

`properties(::ZeKernel)` consults `extension_properties` to decide whether to
link the max-group-size extension struct, and the KernelAbstractions backend
reaches it through `launch_configuration` on every dispatch of a kernel with a
dynamic workgroupsize. Rebuilding the answer each time takes two driver
round-trips plus a `String` allocation and a `Dict` insertion per extension, so
the query dominates the host cost of launch-bound workloads.

A driver's extension list is fixed for its lifetime, and Level Zero has no
driver-destroy entry point, so the result can be cached per driver. `ZeDriver`
compares and hashes by handle, keying the cache by value, and the cache is
emptied in `__init__` so no entry can be inherited from a precompiled image.
The `Base.fill!` specialization drove Level Zero's memory-fill command, which
requires the fill pattern to live in USM memory: a host allocation, a residency
call and a free around every call, plus a full queue synchronize to keep the
pattern alive until the asynchronous fill has read it. The repeated host
allocations also pushed `zeMemAllocHost` into `retry_reclaim`'s garbage
collections.

GPUArrays' generic definition lowers to a single fill kernel and does none of
this, so drop the specialization and let it apply.
`launch_configuration` returned the kernel's `maxGroupSize`, which Level Zero
reports without regard to spill. The driver allocates
`spillMemSize * group_size` bytes of scratch per work-group, so a heavily
spilling kernel is reported as launchable at group sizes with a very large
scratch demand.

Cap the group size so a spilling kernel's per-group scratch stays within a
fixed budget. Level Zero exposes no scratch-space query, so the budget is a
conservative constant; CUDA.jl gets the equivalent from an occupancy API that
accounts for register pressure. The inner division is clamped so a kernel
spilling more than the whole budget still gets a group size of one.
`link_extensions` chains the extension descriptors together with raw interior
pointers stored into each other's `pNext` fields, which the GC cannot see:
`zeKernelGetProperties` receives only `props_ref` and reaches the other structs
through the chain, so nothing in the call itself keeps them rooted.

Preserve all three refs across the query, as `device_alloc`, `ZeModule` and
`ZeKernel` already do for the buffers their descriptors point at. Defensive;
not a fix for an observed failure.
@michel2323 michel2323 changed the title Cache launch_configuration per kernel Memoize the driver extension list and reduce launch-path overhead Aug 7, 2026
@michel2323
michel2323 force-pushed the cache-launch-configuration branch 2 times, most recently from 25f9079 to 0690b28 Compare August 7, 2026 15:40
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