GitHub - svg-project/flash-kmeans: Fast and memory-efficient exact kmeans
Key Points
- 1Flash-KMeans is an IO-aware, batched K-Means clustering implementation leveraging Triton GPU kernels to achieve fast and memory-efficient performance.
- 2It features specialized Triton kernels for different data dimensions (Small-D and Split-D paths) with automatic dispatch and architecture-specific tuning for NVIDIA GPUs.
- 3The library supports automatic multi-GPU scaling for large datasets via data partitioning and an efficient gather-reduce-broadcast mechanism, significantly outperforming baselines in benchmarks.
The flash-kmeans project presents an IO-aware, batched K-Means clustering algorithm implemented with highly optimized Triton GPU kernels. This library is designed to offer significant performance improvements for large-scale clustering tasks, particularly those involving high-dimensional or massive datasets, and serves as the official K-Means implementation for Sparse VideoGen2.
The core methodology revolves around leveraging NVIDIA Triton, a programming language for writing highly efficient GPU kernels, to accelerate the computationally intensive parts of K-Means clustering, primarily the assignment step. The implementation is "IO-aware," meaning it efficiently manages data transfer between CPU and GPU memory and within GPU memory hierarchies to minimize bottlenecks, often employing double-buffered streaming.
Key technical aspects and optimizations include:
- Batched Processing: The algorithm processes data in batches, allowing for efficient utilization of GPU resources and reducing memory overhead compared to non-batched approaches. The primary function
batch_kmeans_Euclidtakes input dataxof shape , where is batch size, is the number of data points per batch, and is the dimensionality.
- Triton Kernel Design for Assignment (E-step): The assignment kernel, responsible for finding the closest centroid for each data point, is meticulously optimized and dispatches between two main flavors based on input shape and data type:
- Small-D Path (): For relatively lower-dimensional data, this kernel loads a tile of data points of shape once. It then streams over the centroids, processing them in chunks of size
BLOCK_K. Distances are accumulated without materializing the full distance matrix, preserving a "K-streaming" property that minimizes memory footprint. Performance is tuned using per-architecture heuristics (e.g., for H200, H100, A100, GB10 GPUs) to select optimal tile sizes (BLOCK_N,BLOCK_K),num_warps, andnum_stages. - Split-D Path ( or Shared Memory Constraints): For higher-dimensional data or when the small-D kernel exceeds shared memory limits, this path employs an outer loop over centroids (K) and an inner loop over dimensions (D), tiling the D-loop by
BLOCK_D. Crucially, the cross-accumulator (which holds partial sums for distance calculations for a tile of data points against a tile of centroids) is maintained in fast GPU registers across the D-loop. This design also preserves the K-streaming property, avoiding the full distance matrix. For unknown GPU architectures, a conservative fallback configuration () is used, dispatching to this path to guarantee shared memory fit, though performance may be suboptimal without specific tuning.
- Small-D Path (): For relatively lower-dimensional data, this kernel loads a tile of data points of shape once. It then streams over the centroids, processing them in chunks of size
- Large Tensor Handling: For datasets that exceed GPU memory capacity,
flash-kmeanssupports large by transferring data from CPU pinned memory to the GPU in chunks. Bothflash-kmeansand compared baselines (e.g.,fastkmeans) adopt this strategy, ensuring scalability for massive datasets.
- Multi-GPU Support (
kmeans_largeN): The library offers automatic multi-GPU scaling for large workloads.- Data Partitioning: The total data points are partitioned into contiguous blocks, with one partition assigned to each available GPU.
- Independent Pipelines: Each GPU independently executes its own double-buffered Host-to-Device (H2D) data transfer and computation pipeline over its assigned data partition. This linear scaling of PCIe bandwidth with GPU count is achieved because data transfers occur in parallel.
- Lightweight AllReduce: After each GPU completes its local accumulation of partial centroid sums (approximately 4 MB) and counts (approximately 32 KB), these partial results are gathered to GPU 0 via Device-to-Device (D2D) copies over NVLink. GPU 0 then performs the reduction, and the updated centroids are broadcast back to all participating GPUs. This manual gather-reduce-broadcast mechanism is utilized instead of NCCL, as the small data size makes manual copies faster and avoids external library dependencies, keeping the process contained within a single application.
- H2D / D2D Overlap: To hide communication latency, the H2D transfers (which use PCIe) and the AllReduce operation (which uses NVLink) are overlapped. The first H2D block for the *next* iteration is prefetched concurrently with the AllReduce operation of the *current* iteration, effectively masking the latency of centroid synchronization.
This comprehensive approach allows flash-kmeans to achieve significant performance improvements over existing K-Means implementations, particularly on NVIDIA GPUs and for large-scale, high-dimensional data.