Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Convert PyTorch AT_DISPATCH macros to AT_DISPATCH_V2 format in ATen C++ code. Use when porting AT_DISPATCH_ALL_TYPES_AND*, AT_DISPATCH_FLOATING_TYPES*, or other dispatch macros to the new v2 API. For ATen kernel files, CUDA kernels, and native operator implementations.
.claude/skills/microck-at-dispatch-v2/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-01 | ✗→✓ | ▲ Improved | -41% | 0% |
| case-03 | ✗→✓ | ▲ Improved | 118% | 0% |
| case-04 | ✗→✓ | ▲ Improved | 26% | 0% |
| case-05 | ✗→✓ | ▲ Improved | 45% | 0% |
| case-06 | ✗→✓ | ▲ Improved | 22% | 0% |
This skill helps convert PyTorch's legacy AT_DISPATCH macros to the new AT_DISPATCH_V2 format, as defined in aten/src/ATen/Dispatch_v2.h.
Use this skill when:
aten/src/ATen/native/ that use dispatch macrosOld format:
cppAT_DISPATCH_ALL_TYPES_AND3(kBFloat16, kHalf, kBool, dtype, "kernel_name", [&]() { // lambda body });
New format:
cppAT_DISPATCH_V2(dtype, "kernel_name", AT_WRAP([&]() { // lambda body }), AT_EXPAND(AT_ALL_TYPES), kBFloat16, kHalf, kBool);
scalar_type and name come first, then lambda, then typesAT_WRAP(lambda) to handle internal commasAT_EXPAND(AT_ALL_TYPES) instead of implicit expansion#include <ATen/Dispatch_v2.h> near other Dispatch includesAdd the v2 header near the existing #include <ATen/Dispatch.h>:
cpp#include <ATen/Dispatch.h> #include <ATen/Dispatch_v2.h>
Keep the old Dispatch.h include for now (other code may still need it).
Common patterns to convert:
AT_DISPATCH_ALL_TYPES_AND{2,3,4}(type1, type2, ..., scalar_type, name, lambda)AT_DISPATCH_FLOATING_TYPES_AND{2,3}(type1, type2, ..., scalar_type, name, lambda)AT_DISPATCH_ALL_TYPES_AND_COMPLEX_AND{2,3}(type1, ..., scalar_type, name, lambda)AT_DISPATCH_FLOATING_AND_COMPLEX_TYPES_AND{2,3}(type1, ..., scalar_type, name, lambda)Identify which type group macro corresponds to the base types:
| Old macro base | AT_DISPATCH_V2 type group | |----------------|---------------------------| | ALL_TYPES | AT_EXPAND(AT_ALL_TYPES) | | FLOATING_TYPES | AT_EXPAND(AT_FLOATING_TYPES) | | INTEGRAL_TYPES | AT_EXPAND(AT_INTEGRAL_TYPES) | | COMPLEX_TYPES | AT_EXPAND(AT_COMPLEX_TYPES) | | ALL_TYPES_AND_COMPLEX | AT_EXPAND(AT_ALL_TYPES_AND_COMPLEX) |
For combined patterns, use multiple AT_EXPAND() entries:
cpp// Old: AT_DISPATCH_ALL_TYPES_AND_COMPLEX_AND2(...) // New: AT_EXPAND(AT_ALL_TYPES), AT_EXPAND(AT_COMPLEX_TYPES), type1, type2
From AT_DISPATCH_*_AND2(type1, type2, ...) or AT_DISPATCH_*_AND3(type1, type2, type3, ...), extract the individual types (type1, type2, etc.).
These become the trailing arguments after the type group:
cppAT_DISPATCH_V2(..., AT_EXPAND(AT_ALL_TYPES), kBFloat16, kHalf, kBool) ^^^^^^^^^^^^^^^^^^^^^^^^ Individual types from AND3
Apply the transformation:
Pattern:
cppAT_DISPATCH_V2( scalar_type, // 1st: The dtype expression "name", // 2nd: The debug string AT_WRAP(lambda), // 3rd: The lambda wrapped in AT_WRAP type_groups, // 4th+: Type groups with AT_EXPAND() individual_types // Last: Individual types )
Example transformation:
cpp// BEFORE AT_DISPATCH_ALL_TYPES_AND3( kBFloat16, kHalf, kBool, iter.dtype(), "min_values_cuda", [&]() { min_values_kernel_cuda_impl<scalar_t>(iter); } ); // AFTER AT_DISPATCH_V2( iter.dtype(), "min_values_cuda", AT_WRAP([&]() { min_values_kernel_cuda_impl<scalar_t>(iter); }), AT_EXPAND(AT_ALL_TYPES), kBFloat16, kHalf, kBool );
For lambdas with internal commas or complex expressions, AT_WRAP is essential:
cppAT_DISPATCH_V2( dtype, "complex_kernel", AT_WRAP([&]() { gpu_reduce_kernel<scalar_t, scalar_t>( iter, MinOps<scalar_t>{}, thrust::pair<scalar_t, int64_t>(upper_bound(), 0) // Commas inside! ); }), AT_EXPAND(AT_ALL_TYPES) );
Check that:
AT_WRAP() wraps the entire lambdaAT_EXPAND()AT_EXPAND() (just kBFloat16, not AT_EXPAND(kBFloat16))#include <ATen/Dispatch_v2.h>Available type group macros (use with AT_EXPAND()):
cppAT_INTEGRAL_TYPES // kByte, kChar, kInt, kLong, kShort AT_FLOATING_TYPES // kDouble, kFloat AT_COMPLEX_TYPES // kComplexDouble, kComplexFloat AT_QINT_TYPES // kQInt8, kQUInt8, kQInt32 AT_ALL_TYPES // INTEGRAL_TYPES + FLOATING_TYPES AT_ALL_TYPES_AND_COMPLEX // ALL_TYPES + COMPLEX_TYPES AT_INTEGRAL_TYPES_V2 // INTEGRAL_TYPES + unsigned types AT_BAREBONES_UNSIGNED_TYPES // kUInt16, kUInt32, kUInt64 AT_FLOAT8_TYPES // Float8 variants
cpp// Before AT_DISPATCH_ALL_TYPES_AND2(kHalf, kBFloat16, dtype, "op", [&]() { kernel<scalar_t>(data); }); // After AT_DISPATCH_V2(dtype, "op", AT_WRAP([&]() { kernel<scalar_t>(data); }), AT_EXPAND(AT_ALL_TYPES), kHalf, kBFloat16);
cpp// Before AT_DISPATCH_FLOATING_TYPES_AND3(kHalf, kBFloat16, kFloat8_e4m3fn, tensor.scalar_type(), "float_op", [&] { process<scalar_t>(tensor); }); // After AT_DISPATCH_V2(tensor.scalar_type(), "float_op", AT_WRAP([&] { process<scalar_t>(tensor); }), AT_EXPAND(AT_FLOATING_TYPES), kHalf, kBFloat16, kFloat8_e4m3fn);
cpp// Before AT_DISPATCH_ALL_TYPES_AND_COMPLEX_AND2( kComplexHalf, kHalf, self.scalar_type(), "complex_op", [&] { result = compute<scalar_t>(self); } ); // After AT_DISPATCH_V2( self.scalar_type(), "complex_op", AT_WRAP([&] { result = compute<scalar_t>(self); }), AT_EXPAND(AT_ALL_TYPES), AT_EXPAND(AT_COMPLEX_TYPES), kComplexHalf, kHalf );
cpp// Before AT_DISPATCH_ALL_TYPES(dtype, "op", [&]() { kernel<scalar_t>(); }); // After AT_DISPATCH_V2(dtype, "op", AT_WRAP([&]() { kernel<scalar_t>(); }), AT_EXPAND(AT_ALL_TYPES));
cpp// Before AT_DISPATCH_FLOATING_TYPES_AND4(kHalf, kBFloat16, kFloat8_e4m3fn, kFloat8_e5m2, dtype, "float8_op", [&]() { kernel<scalar_t>(); }); // After AT_DISPATCH_V2(dtype, "float8_op", AT_WRAP([&]() { kernel<scalar_t>(); }), AT_EXPAND(AT_FLOATING_TYPES), kHalf, kBFloat16, kFloat8_e4m3fn, kFloat8_e5m2);
cpp// Before AT_DISPATCH_ALL_TYPES_AND2(kHalf, kBool, dtype, "op", []() { static_kernel<scalar_t>(); }); // After AT_DISPATCH_V2(dtype, "op", AT_WRAP([]() { static_kernel<scalar_t>(); }), AT_EXPAND(AT_ALL_TYPES), kHalf, kBool);
AT_EXPAND()#include <ATen/Dispatch.h> - other code may need itAT_WRAP() is mandatory - prevents comma parsing issues in the lambdaAT_EXPAND(), individual types don'taten/src/ATen/Dispatch_v2.h - refer to it for full docsWhen asked to convert AT_DISPATCH macros:
#include <ATen/Dispatch_v2.h> if not presentDo NOT compile or test the code - focus on accurate conversion only.
| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-01 | fail→pass | 36,244 | 13,061 | -64% | 1 | 1 | 0% | 7,683 | 4,520 | -41% | 0 | 0 | — |
case-02 | fail→fail | 39,094 | 22,970 | -41% | 1 | 1 | 0% | 8,272 | 7,680 | -7% | 0 | 0 | — |
case-03 | fail→pass | 13,126 | 12,681 | -3% | 1 | 1 | 0% | 2,612 | 5,691 | +118% | 0 | 0 | — |
case-04 | fail→pass | 15,357 | 4,156 | -73% | 1 | 1 | 0% | 2,947 | 3,724 | +26% | 0 | 0 | — |
case-05 | fail→pass | 12,664 | 3,914 | -69% | 1 | 1 | 0% | 2,573 | 3,733 | +45% | 0 | 0 | — |
case-06 | fail→pass | 17,455 | 4,566 | -74% | 1 | 1 | 0% | 3,169 | 3,863 | +22% | 0 | 0 | — |
case-07 | fail→pass | 10,616 | 4,292 | -60% | 1 | 1 | 0% | 2,045 | 3,823 | +87% | 0 | 0 | — |
case-08 | fail→pass | 11,751 | 4,003 | -66% | 1 | 1 | 0% | 2,301 | 3,710 | +61% | 0 | 0 | — |
case-09 | fail→pass | 15,832 | 5,032 | -68% | 1 | 1 | 0% | 3,088 | 4,026 | +30% | 0 | 0 | — |
case-10 | pass→pass | 15,905 | 5,172 | -67% | 1 | 1 | 0% | 3,089 | 3,947 | +28% | 0 | 0 | — |
case-11 | fail→pass | 17,086 | 3,174 | -81% | 1 | 1 | 0% | 3,161 | 3,510 | +11% | 0 | 0 | — |
case-12 | pass→pass | 16,715 | 4,424 | -74% | 1 | 1 | 0% | 3,374 | 3,816 | +13% | 0 | 0 | — |
case-13 | fail→pass | 24,196 | 4,135 | -83% | 1 | 1 | 0% | 4,703 | 3,772 | -20% | 0 | 0 | — |
case-14 | fail→pass | 16,949 | 4,340 | -74% | 1 | 1 | 0% | 3,080 | 3,745 | +22% | 0 | 0 | — |
case-15 | pass→pass | 9,433 | 5,953 | -37% | 1 | 1 | 0% | 2,106 | 4,192 | +99% | 0 | 0 | — |
case-16 | pass→pass | 16,833 | 16,470 | -2% | 1 | 1 | 0% | 3,222 | 6,097 | +89% | 0 | 0 | — |
case-17 | pass→pass | 5,830 | 4,704 | -19% | 1 | 1 | 0% | 1,087 | 3,785 | +248% | 0 | 0 | — |
case-18 | fail→pass | 34,326 | 4,962 | -86% | 1 | 1 | 0% | 7,228 | 3,979 | -45% | 0 | 0 | — |
case-19 | fail→pass | 20,874 | 4,424 | -79% | 1 | 1 | 0% | 4,013 | 3,794 | -5% | 0 | 0 | — |
case-20 | fail→pass | 11,255 | 4,626 | -59% | 1 | 1 | 0% | 1,964 | 3,817 | +94% | 0 | 0 | — |
case-21 | fail→pass | 15,907 | 4,098 | -74% | 1 | 1 | 0% | 2,975 | 3,691 | +24% | 0 | 0 | — |
case-22 | fail→pass | 13,746 | 4,548 | -67% | 1 | 1 | 0% | 2,606 | 3,773 | +45% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 22 cases were attempted. The headline lift of +73 percentage points is the difference between those two pass rates over the 22 comparable cases.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.