Skip to main content

mldrift-add-new-kernel

Generates code for a new GPU Kernel in ml_drift using UCL syntax. Use when the user wants to add or implement a new operation/kernel in ml_drift (e.g., SquaredDifference, Maximum). Creates the .h, .cc files, generates test files (test.cc, test_util.h, test_util.cc), and updates BUILD rules. Don't use for generic C++ development outside of ml_drift.

来源信息

仓库
google-ai-edge/ml-drift
最近来源活动
2026年10月8日 23:59
检测到的 SKILL.md 语言
英语
星标
155
分支
14

安装方式

默认使用会先检查来源的 Prompt;你也可以切换为直接命令,或下载本地副本。

检查来源文件

决定是否安装前,请先阅读 SKILL.md,以及 SkillsMP 当前展示的配套文件。

正在显示 SKILL.md

SKILL.md
来源说明 · 只读预览
name
mldrift-add-new-kernel
description
Generates code for a new GPU Kernel in ml_drift using UCL syntax. Use when the user wants to add or implement a new operation/kernel in ml_drift (e.g., SquaredDifference, Maximum). Creates the .h, .cc files, generates test files (test.cc, test_util.h, test_util.cc), and updates BUILD rules. Don't use for generic C++ development outside of ml_drift.
license
Apache-2.0 (full text in the repository's top-level LICENSE file)
<!-- disableFinding(LINE_OVER_80) --> # ML Drift Add New Kernel This skill guides you to implement a new ML Drift GPU Kernel using ML Drift's internal layouts and the internal Unified Compute Language (UCL) so that the kernel can run across multiple backends natively. ## Instructions When asked to add a new GPU kernel (e.g., "squared_difference") to ml_drift, strictly follow these 5 steps in order: ### 1. Create the Header (`common/kernels/{op_name}.h`) Create the header file with standard include guards (`ML_DRIFT_COMMON_KERNELS_{OP_NAME}_H_`). * **Licensing**: If generating new files in a user's repository, do NOT hardcode ML Drift's Apache copyright headers. Instead, adopt the existing license header style of the user's surrounding files, or leave it blank. * **Namespace**: Always place the code under `namespace ml_drift { ... }`. * **Factory Function**: Declare a factory function that returns a `GPUOperation`. * Example: `GPUOperation CreateSquaredDifference(const OperationDef& definition, ...);` ### 2. Create the Implementation (`common/kernels/{op_name}.cc`) Create the implementation file. It must define the factory function and inject UCL shader code. * **Factory Setup**: * Instantiate `GPUOperation op;`. * Add input/output tensors using `op.AddSrcTensor()` and `op.AddDstTensor()`. * Register any custom parameters with primitive data types using `op.args_.AddFloat()`, `op.args_.AddInt()`, etc. * *Multiple Inputs Tip*: If the operation has two inputs, add them with distinct names: `op.AddSrcTensor("src_tensor_0", ...);` and `op.AddSrcTensor("src_tensor_1", ...);`. * **Grid Assignment**: * Explicitly set the `grid_size_` based on the tensor shape using `int3` and `DivideRoundUp`. * Example: `op.grid_size_ = int3(DivideRoundUp(shape.w, 8), DivideRoundUp(shape.h, 4), shape.c);` * **UCL Code Generation**: * The `op.code_` must be a string containing UCL code. * Wrap the actual shader inside `MAIN_FUNCTION($0) { ... }`. **CRITICAL: Tensor Layout and Storage Type Guide** When designing the shader logic and declaring tests, you MUST work with ML Drift's layout rules: * **Logical Layouts**: * Inputs, outputs, and intermediate tensors typically use `LINEAR`, `HWC`, or `BHWC`. Weights use `OHWI` or `IHWO`. * Use `Layout::HWC` (or `BHWC`) for variables read using `X, Y, S` coordinates in UCL. * `Layout::LINEAR` is only for 1D vectors and does not support three-parameter reads like `.Read(X, Y, S)`. * **Slices & Padding**: * Tensors are split into 4-channel slices stored sequentially in memory. * If the number of channels is not divisible by 4, it is zero-padded. * **Physical Coords Translation**: * The shader `Read`/`Write` calls (e.g., `args.src_tensor.Read(X, Y, S)`) use logical coordinates. * ML Drift automatically translates these to physical coordinates based on the underlying hardware storage type. * For `BHWC` Textures, the arguments translate to `(X * batch_size + B, Y * num_slices + S)`. * Therefore, you do NOT need to manually compute physical memory offsets in UCL; just pass logical coordinates `(B, X, Y, S)` for BHWC, `(X, Y, S)` for HWC, and `(index)` for LINEAR directly. **CRITICAL: UCL Syntax Guide** When generating the shader code string, you MUST use the following UCL semantics: * **Tensor/Arg Names**: The names accessed via `args.NAME` in the shader MUST exactly match the string names registered in the C++ file. * **Thread IDs**: Retrieve global coordinates using `ucl::GetGlobalId<0>()` (usually X), and local workgroup coordinates using `ucl::GetLocalId<0>()`. * **Local Memory & Synchronization**: Declare shared memory arrays with `__local` (e.g., `__local int hist[SIZE];`). Synchronize threads in a workgroup using `ucl::SyncThreads<WorkGroup, Local>();`. * **Macro Injection**: For constants (like array sizes or bucket counts), do NOT pass them as runtime arguments. Instead, inject them as macros into the shader string in C++ using `absl::StrReplaceAll({{"NUM_BUCKETS", std::to_string(num)}}, &code);`. * **Initialization**: Vectors MUST be initialized via `ucl::Init`. E.g., `int4 result = ucl::Init<int4>(0);`. * **Type Casting**: Use `ucl::Convert<TargetType>(value)`. * **Tensor Properties**: Read tensor dimensions via `args.dst_tensor.Width()`, `args.dst_tensor.Height()`, `args.dst_tensor.Slices()`. * **Tensor I/O**: * Read: `args.src_tensor.Read(X, Y, S)` * Write: `args.dst_tensor.Write(value, X, Y, S)` * Linear read/write: `args.src_tensor.ReadPerChannel(value, index)`, `args.dst_tensor.WriteLinear(value, DST_S)` * **Type deduction**: Use `args.src_tensor::type` and `args.src_tensor::zero_value` to create and initialize generic variables. ### 3. Create Tests (`common/kernels/tests/`) Do not bundle tests into a single file. You must create three separate files and implement actual test logic using `TestExecutionEnvironment` and `TensorDescriptor`. 1. `tests/{op_name}_test_util.h`: Declare `Run*Test(TestExecutionEnvironment& env, ...)` parameterized test helpers. 2. `tests/{op_name}_test_util.cc`: Implement setup, `UploadDataRaw`, `ExecuteGPUOperation`, and `DownloadData` for verification. 3. `tests/{op_name}_test.cc`: The actual googletest cases calling the util functions. **Implementation Guide for Tests:** * **Test Parameterization**: * If the kernel supports variations (e.g., multiple data types like `float32`/`float16`, or varying sizes), use GTest value parameterization (`TEST_P` and `INSTANTIATE_TEST_SUITE_P`) rather than `TYPED_TEST`. * For an example, refer to `tests/add_test.cc`. This allows you to cleanly execute all test cases across supported configurations. * **Required Test Cases** in `{op_name}_test.cc`: * `SingleElementTest`: A trivial `1x1x1x1` shape test. * *Note: For **ALL** subsequent tests, you MUST use multidimensional shapes, e.g., `BHWC(1, 2, 3, 4)` to ensure UCL coordinate traversal is fully exercised.* * `BasicTest`: Multidimensional shape with normal positive numbers. * `PaddingTest`: Crucial for ML Drift! Since the channel dimension is zero-padded to multiples of 4, test with `C` that is NOT a multiple of 4 (e.g., `C=3` or `C=5`) to ensure bounds are handled cleanly. * `NegativeValuesTest`: Multidimensional shape with negative numbers, to ensure sign preservation and logic hold up. * `ZerosTest`: Multidimensional shape with zeros, to catch div-by-zero or initialization errors. ### 4. Update `BUILD` Rules You must update both the kernel and test BUILD files: 1. **`common/kernels/BUILD`**: Append a new `cc_library` target for your operation. * The target name should match the op_name. * `srcs = ["{op_name}.cc"]` and `hdrs = ["{op_name}.h"]` 2. **`common/kernels/tests/BUILD`**: Register the new test target using `mld_kernel_test(name = "{op_name}_test")` and `cc_library(name = "{op_name}_test_util")`. ### 5. Test and Verify The `mld_kernel_test` macro generates test targets for multiple GPU backends (e.g., `_opencl`, `_opengl`, `_metal`). You MUST test your kernel on all available backends.
在 GitHub 查看