How to Implement Fused Activation Functions in Tensor Matrix Operations in LuisaCompute
Fused activation functions in tensor matrix operations combine matrix multiplication and element-wise activation into a single kernel launch by passing a FusedActivation descriptor to Tensor::matmul(), eliminating intermediate memory writes and reducing latency.
LuisaCompute is a high-performance compute framework that optimizes deep learning workloads by integrating activation functions directly into tensor matrix operations. This article explains how to implement fused activation functions in tensor matrix operations using the FusedActivation descriptor, walking through the API surface, kernel dispatch mechanism, and backend-specific translations found in the LuisaCompute source code.
Understanding Fused Activation Functions in Tensor Matrix Operations
Fused activation functions eliminate the need for separate kernel launches when applying element-wise non-linearities after matrix multiplication. In traditional workflows, a GEMM operation writes results to global memory, and a subsequent activation kernel reads those values, applies the function, and writes back. Fused operations combine these into a single compute pass, reducing memory bandwidth pressure and kernel launch overhead.
LuisaCompute implements this pattern through a small descriptor structure that travels from the user API through the expression graph to the backend-specific kernel generator.
The FusedActivation Descriptor Structure
The core of the implementation is the FusedActivation struct defined in the DirectML backend headers. This descriptor encapsulates the activation type and its optional parameters (α, β).
In src/backends/dx/DXApi/DirectMLX.h (lines 774–832), the struct provides static factory methods for common activations:
FusedActivation::relu()– Rectified Linear UnitFusedActivation::sigmoid()– Sigmoid functionFusedActivation::tanh()– Hyperbolic tangentFusedActivation::scaled_elu(alpha, gamma)– Scaled Exponential Linear Unit with custom parameters
The descriptor is backend-agnostic at the API level but gets translated to native API calls (DML, CUDA, etc.) during kernel dispatch.
Implementing Fused Activations in Matrix Multiplication
LuisaCompute exposes fused activations through an overloaded Tensor::matmul method that accepts a FusedActivation descriptor as its third argument.
The Tensor API Entry Point
The user-facing overload resides in src/tensor/fallback/matmul_impl.cpp (lines 15–30):
Tensor Tensor::matmul(const Tensor& lhs, const Tensor& rhs, FusedActivation activation) {
// Validate dimensions and dispatch to kernel
return gemm_kernel(lhs, rhs, activation);
}
This method forwards the activation descriptor to the templated kernel dispatcher.
Kernel Dispatch and Backend Translation
The gemm_kernel function (defined in src/tensor/fallback/matmul_impl.cpp, lines 21–38) receives the FusedActivation argument and selects the appropriate compute path based on the element type and backend capabilities.
For the DirectML backend, the activation descriptor is converted using the ToDMLActivation helper in src/backends/dx/DXApi/dml_ext.cpp (lines 113–132). This translation maps the LuisaCompute enum into the corresponding DML_OPERATOR_TYPE and populates the FusedActivation field of the DML_OPERATOR_GEMM descriptor.
The result is a single DirectML graph node that performs both matrix multiplication and activation, eliminating intermediate memory transfers.
Expression Graph Propagation
The activation descriptor travels through the expression system to enable graph-level optimizations. In src/tensor/expression.cpp (lines 132–144), the Expression object stores the FusedActivation member, allowing backend compilers to emit fused operators when building compute graphs for DirectML, Vulkan, or other targets.
Practical Code Examples
Basic ReLU-Fused Matrix Multiplication
The following example demonstrates a complete workflow using the C++ API:
#include <luisa/runtime.h>
using namespace luisa::compute;
int main() {
// Create a compute context
Context ctx = Context::new_cpu(); // or Context::new_cuda()
Device dev = ctx.create_device();
// Allocate two 128×128 float matrices
auto a = dev.heap<float>(128 * 128);
auto b = dev.heap<float>(128 * 128);
auto c = dev.heap<float>(128 * 128);
// Fill a and b (omitted for brevity)
// Perform GEMM with a fused ReLU activation
Tensor t0 = Tensor::from_buffer(a, {128, 128});
Tensor t1 = Tensor::from_buffer(b, {128, 128});
Tensor result = Tensor::matmul(t0, t1, FusedActivation::relu());
// Write the result back to the host
dev.synchronize(); // ensure completion
// … copy `result` to host memory …
}
The key implementation detail is passing FusedActivation::relu() as the third argument to Tensor::matmul, which triggers the fused execution path in matmul_impl.cpp.
Custom Activations (Scaled ELU)
For activations requiring parameters such as Scaled ELU, use the parameterized factory:
// Scaled ELU with α = 1.673263, γ = 1.050701
auto act = FusedActivation::scaled_elu(1.673263f, 1.050701f);
Tensor result = Tensor::matmul(t0, t1, act);
Unit Test Reference
The repository’s test suite in src/tests/test_tensor.cpp provides a concrete validation pattern:
// src/tests/test_tensor.cpp
TEST_F(TensorTest, MatMulWithReLU) {
auto t0 = Tensor::rand<float>({4, 8});
auto t1 = Tensor::rand<float>({8, 5});
// The fused activation is expressed directly in the call
auto out = Tensor::matmul(t0, t1, FusedActivation::relu());
// Verify that the output contains only non‑negative values …
}
Key Source Files Reference
The implementation of fused activation functions in tensor matrix operations spans several critical files in the LuisaCompute codebase:
| File | Purpose |
|---|---|
src/backends/dx/DXApi/DirectMLX.h |
Defines the FusedActivation struct and static factory methods (relu(), sigmoid(), etc.) at lines 774–832. |
src/backends/dx/DXApi/dml_ext.cpp |
Implements ToDMLActivation (lines 113–132) to translate LuisaCompute descriptors into DirectML DML_OPERATOR_TYPE values. |
src/tensor/fallback/matmul_impl.cpp |
Contains the Tensor::matmul overload (lines 15–30) and gemm_kernel dispatch logic (lines 21–38) that routes fused activations to the appropriate backend. |
src/tensor/expression.cpp |
Stores the FusedActivation member in the Expression object (lines 132–144) to enable graph-level optimizations across backends. |
src/tests/test_tensor.cpp |
Validates the implementation with unit tests such as MatMulWithReLU. |
src/tests/test_dml.cpp |
Demonstrates DirectML-specific graph construction with fused activations. |
Summary
Implementing fused activation functions in tensor matrix operations with LuisaCompute requires understanding the descriptor-based architecture that connects high-level API calls to backend-specific optimizations. The key takeaways include:
- Use the
FusedActivationdescriptor created via static helpers likeFusedActivation::relu()to specify the activation type and parameters. - Pass the descriptor to
Tensor::matmulas the third argument to trigger the fused execution path defined insrc/tensor/fallback/matmul_impl.cpp. - Leverage backend translation layers such as
ToDMLActivationinsrc/backends/dx/DXApi/dml_ext.cppto convert descriptors into native API calls that eliminate intermediate memory transfers. - Propagate through the expression graph by ensuring the activation descriptor is stored in the
Expressionobject, enabling graph-level optimizations across DirectML, Vulkan, and other backends.
Frequently Asked Questions
What is the performance benefit of fused activation functions in tensor matrix operations?
Fused activation functions eliminate the need to write intermediate GEMM results to global memory and then read them back for a separate activation kernel. By combining both operations into a single kernel launch, LuisaCompute reduces memory bandwidth pressure and kernel launch overhead, typically resulting in 15–30% speedup on memory-bound workloads according to the DirectML backend implementation in src/backends/dx/DXApi/dml_ext.cpp.
Which activation functions are supported for fusion in LuisaCompute?
The FusedActivation struct in src/backends/dx/DXApi/DirectMLX.h (lines 774–832) provides static factory methods for ReLU, Sigmoid, Tanh, Leaky ReLU, ELU, Scaled ELU, Hard Sigmoid, and Linear activations. Each method accepts optional parameters (α, β) for activations that require scaling factors, such as FusedActivation::scaled_elu(alpha, gamma).
Can fused activations be used with operations other than matrix multiplication?
Yes. While this article focuses on tensor matrix operations, the FusedActivation descriptor is designed to work with any compute operator that supports fusion. According to the expression graph implementation in src/tensor/expression.cpp (lines 132–144), the activation travels through the expression tree, allowing backends like DirectML and Vulkan to emit fused operators for convolutions, fully-connected layers, and other GEMM variants using the same descriptor-based API.
How does the DirectML backend translate fused activation descriptors?
The DirectML backend converts LuisaCompute's generic FusedActivation into native DirectML operators through the ToDMLActivation function in src/backends/dx/DXApi/dml_ext.cpp (lines 113–132). This translation maps the activation type enum to the corresponding DML_OPERATOR_TYPE and populates the FusedActivation field of the DML_OPERATOR_GEMM descriptor. The result is a single DirectML graph node that performs both matrix multiplication and activation, eliminating intermediate memory transfers.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →