7.6. Interpret CUDA spans
Build TorchLean with CUDA and run a device model:
LEAN_PROFILE=1 \ LEAN_PROFILE_OUT=build/mlp-cuda-trace.json \ LEAN_PROFILE_SUMMARY_OUT=build/mlp-cuda-summary.json \ lake -R -K cuda=true exe leanprofiler_torchlean \ quickstart_mlp --device cuda --arithmetic native --steps 3
The integration forwards cuda=true and the optional libtorch_home setting to TorchLean.
TorchLean builds and links the LibTorch adapter; the profiler needs no separate CUDA bridge.
Without option forwarding, Lake would leave the dependency on its unavailable-backend shim even
though the workspace root received -K cuda=true.
When the command line contains --device cuda or --device gpu, the runner uses Cuda.spanHooks.
The hook checks TorchLean's native runtime, samples its buffer counters, runs the model, calls
TorchLean's LibTorch.synchronize, and samples the counters again:
The duration is a synchronized host boundary. It includes queue delay, device work, and
synchronization overhead; it is not the sum of kernel execution times. In the trace, look for
timing = device-synchronized and check hook_error before using a span as a device measurement.
The alloc_live_bytes, alloc_peak_bytes, and alloc_delta_bytes fields count logical payload
bytes owned by TorchLean buffer handles, not LibTorch's allocated or reserved memory. Shared
storage can be counted more than once, and native temporaries need not have a TorchLean handle.
TorchLean's Buffer.memory exposes native allocator counters separately; these fields record
buffer ownership.
LeanProfiler does not read CUDA events, CUPTI activity, stream IDs,
memcopy events, or per-kernel allocations, so Perfetto still shows Lean host threads. Use a native
device profiler when the synchronized boundary points to CUDA and the next question is which kernel
or transfer is responsible.
Run TorchLean's CUDA kernel and allocator checks without the unrelated Python and dataset suites:
CUDA_VISIBLE_DEVICES=0 \ lake -R -K cuda=true exe leanprofiler_torchlean_cuda_tests