Skip to content

Commit ee51dec

Browse files
[lang] Allow non portable dynamic shared memory size
Signed-off-by: Asher Mancinelli <amancinelli@nvidia.com>
1 parent 44996f8 commit ee51dec

3 files changed

Lines changed: 118 additions & 8 deletions

File tree

cext/cuda_loader.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
X(cuCtxGetDevice, 2000) \
2727
X(cuCtxGetId, 12000) \
2828
X(cuDeviceGet, 2000) \
29+
X(cuDeviceGetCount, 2000) \
2930
X(cuDeviceGetAttribute, 2000) \
3031
X(cuDevicePrimaryCtxRetain, 7000) \
3132
X(cuDriverGetVersion, 2020) \
@@ -34,6 +35,8 @@
3435
X(cuEventQuery, 2000) \
3536
X(cuEventRecord, 2000) \
3637
X(cuKernelGetFunction, 12000) \
38+
X(cuKernelGetAttribute, 12000) \
39+
X(cuKernelSetAttribute, 12000) \
3740
X(cuMemAlloc, 3020) \
3841
X(cuMemAllocHost, 3020) \
3942
X(cuMemFree, 3020) \

cext/tile_kernel.cpp

Lines changed: 71 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -280,6 +280,66 @@ struct CudaKernel {
280280
CUkernel kernel;
281281
};
282282

283+
static Status enable_maximum_dynamic_shared_memory(const DriverApi *driver,
284+
const CUkernel kernel,
285+
const char *func_name) {
286+
int device_count;
287+
CUresult res = driver->cuDeviceGetCount(&device_count);
288+
if (res != CUDA_SUCCESS) {
289+
return raise(PyExc_RuntimeError, "Failed to get device count: %s",
290+
get_cuda_error(driver, res));
291+
}
292+
293+
for (int device_ordinal = 0; device_ordinal < device_count;
294+
device_ordinal++) {
295+
CUdevice device;
296+
res = driver->cuDeviceGet(&device, device_ordinal);
297+
if (res != CUDA_SUCCESS) {
298+
return raise(PyExc_RuntimeError, "Failed to get device %d: %s",
299+
device_ordinal, get_cuda_error(driver, res));
300+
}
301+
302+
int max_smem;
303+
res = driver->cuDeviceGetAttribute(
304+
&max_smem, CU_DEVICE_ATTRIBUTE_MAX_SHARED_MEMORY_PER_BLOCK_OPTIN,
305+
device);
306+
if (res != CUDA_SUCCESS) {
307+
return raise(PyExc_RuntimeError,
308+
"Failed to get maximum shared memory for device %d: %s",
309+
device_ordinal, get_cuda_error(driver, res));
310+
}
311+
312+
int static_smem;
313+
res = driver->cuKernelGetAttribute(
314+
&static_smem, CU_FUNC_ATTRIBUTE_SHARED_SIZE_BYTES, kernel, device);
315+
if (res != CUDA_SUCCESS) {
316+
return raise(PyExc_RuntimeError,
317+
"Failed to get static shared memory for kernel %s: %s",
318+
func_name, get_cuda_error(driver, res));
319+
}
320+
321+
if (max_smem < static_smem) {
322+
// If the user's program uses more static shared memory than the
323+
// current device has available, then we cannot request enough
324+
// shared memory. If the user has another device capable of running
325+
// their program, they must run on that device and errors will be
326+
// reported at launch time.
327+
continue;
328+
}
329+
int largest_possible_dynamic_smem = max_smem - static_smem;
330+
res = driver->cuKernelSetAttribute(
331+
CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
332+
largest_possible_dynamic_smem, kernel, device);
333+
if (res != CUDA_SUCCESS) {
334+
return raise(PyExc_RuntimeError,
335+
"Failed to set dynamic shared memory for kernel %s: %s",
336+
func_name, get_cuda_error(driver, res));
337+
}
338+
}
339+
340+
return OK;
341+
}
342+
283343
static Result<CudaKernel> load_cuda_kernel(const DriverApi* driver,
284344
const char* cubin_data,
285345
size_t cubin_size,
@@ -291,11 +351,12 @@ static Result<CudaKernel> load_cuda_kernel(const DriverApi* driver,
291351

292352
CUkernel kernel;
293353
CUresult res = driver->cuLibraryGetKernel(&kernel, lib->get(), func_name);
294-
if (res == CUDA_SUCCESS)
295-
return CudaKernel{std::move(*lib), kernel};
354+
if (res != CUDA_SUCCESS) {
355+
return raise(PyExc_RuntimeError, "Failed to get kernel %s from library: %s",
356+
func_name, get_cuda_error(driver, res));
357+
}
296358

297-
return raise(PyExc_RuntimeError, "Failed to get kernel %s from library: %s",
298-
func_name, get_cuda_error(driver, res));
359+
return CudaKernel{std::move(*lib), kernel};
299360
}
300361

301362

@@ -2156,6 +2217,12 @@ static Result<TileKernel> compile(const DriverApi* driver,
21562217
Result<CudaKernel> cukernel = load_cuda_kernel(driver, cubin_data, cubin_size, cufunc_name);
21572218
if (!cukernel.is_ok()) return ErrorRaised;
21582219

2220+
if (py_dyn_smem_size_prog != Py_None) {
2221+
Status status = enable_maximum_dynamic_shared_memory(
2222+
driver, cukernel->kernel, cufunc_name);
2223+
if (!status) return ErrorRaised;
2224+
}
2225+
21592226
Result<HostProgram> dyn_smem_size_prog = host_program_parse(py_dyn_smem_size_prog, 1);
21602227
if (!dyn_smem_size_prog.is_ok()) return ErrorRaised;
21612228

experimental/cuda-lang/test/test_dynamic_shared_mem.py

Lines changed: 44 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -172,10 +172,8 @@ def kern(n):
172172
def test_dynamic_shared_alignment_runtime_round_up_launch():
173173
@cl.kernel
174174
def kern(x, n):
175-
smem = cl.shared_array(
176-
shape=(n,), dtype=cl.uint8, dynamic=True, alignment=128
177-
)
178-
values = cl.shared_array(shape=(1,), dtype=cl.int32, dynamic=True)
175+
smem = cl.shared_array(n, cl.uint8, dynamic=True, alignment=128)
176+
values = cl.shared_array(1, cl.int32, dynamic=True)
179177
smem[0] = cl.uint8(0)
180178
values[0] = 42
181179
x[0] = values[0] + cl.int32(smem[0])
@@ -188,6 +186,48 @@ def kern(x, n):
188186
assert spy.get_dynamic_smem_size() == 128 + 4
189187

190188

189+
def test_dynamic_shared_memory_over_device_limit():
190+
properties = torch.cuda.get_device_properties(torch.cuda.current_device())
191+
max_smem_bytes = properties.shared_memory_per_block_optin
192+
193+
@cl.kernel
194+
def kern():
195+
smem = cl.shared_array(max_smem_bytes + 1, cl.int8, dynamic=True)
196+
if cl.thread_index(0) == 0:
197+
smem[max_smem_bytes] = cl.int8(0)
198+
199+
with pytest.raises(RuntimeError, match="Failed to launch"):
200+
cl.launch(torch.cuda.current_stream(), (1,), (32,), kern, ())
201+
202+
203+
def test_max_shared_memory_with_static_allocation():
204+
properties = torch.cuda.get_device_properties(torch.cuda.current_device())
205+
max_smem_bytes = properties.shared_memory_per_block_optin
206+
static_smem_bytes = 1024
207+
dynamic_smem_bytes = max_smem_bytes - static_smem_bytes
208+
209+
@cl.kernel
210+
def kern(output):
211+
static_smem = cl.shared_array(
212+
static_smem_bytes, cl.int8, alignment=1024
213+
)
214+
dynamic_smem = cl.shared_array(
215+
dynamic_smem_bytes, cl.int8, dynamic=True
216+
)
217+
if cl.thread_index(0) == 0:
218+
static_smem[static_smem_bytes - 1] = cl.int8(3)
219+
dynamic_smem[dynamic_smem_bytes - 1] = cl.int8(4)
220+
output[0] = (
221+
static_smem[static_smem_bytes - 1]
222+
+ dynamic_smem[dynamic_smem_bytes - 1]
223+
)
224+
225+
output = torch.zeros(1, dtype=torch.int8, device="cuda")
226+
cl.launch(torch.cuda.current_stream(), (1,), (32,), kern, (output,))
227+
228+
assert output.item() == 7
229+
230+
191231
def test_dynamic_1d_array_and_static_1d_array():
192232
@cl.kernel
193233
def kern(x, n):

0 commit comments

Comments
 (0)