You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

ILGPU分块矩阵乘法Kernel输出错误,请求排查原因

分块矩阵乘法Kernel结果错误排查

我从ILGPU示例中获取了**分块形式(tiled form)**的矩阵乘法Kernel代码,用来计算以下矩阵的乘积:

a = |1  2  3  4|
    |5  6  7  8|

b = | 9  10|
    |11  12|
    |13  14|
    |15  16|

根据计算,正确结果应为:

130   140
   322   348

但我的代码输出结果为:

Result of matrix multiplication:
  31   34
 111  122

我使用了与示例完全相同的Kernel和tile-size,请问错误出在哪里?

源代码

using ILGPU;
using ILGPU.Runtime;
using System;

public static class Program
{
    // Define tile size for matrix multiplication (2x2 tiles for simplicity)
    // This determines how many elements will be loaded into shared memory at a time.
    const int TILE_SIZE = 2;

    // Kernel function to perform tiled matrix multiplication on the GPU
    static void MatrixMultiplyTiledKernel(
            ArrayView2D<int, Stride2D.DenseX> aView, // Input matrix A in GPU memory
            ArrayView2D<int, Stride2D.DenseX> bView, // Input matrix B in GPU memory
            ArrayView2D<int, Stride2D.DenseX> cView) // Output matrix C in GPU memory
    {
        // Get the global thread index in the 2D grid
        Index2D global = Grid.GlobalIndex.XY;

        // Get the thread indices within the thread group (local indices)
        int x = Group.IdxX; // Local row index within the tile
        int y = Group.IdxY; // Local column index within the tile

        // Allocate shared memory for storing tiles of matrix A and matrix B
        var aTile = SharedMemory.Allocate2D<int, Stride2D.DenseX>(
            new Index2D(TILE_SIZE, TILE_SIZE),
            new Stride2D.DenseX(TILE_SIZE)
        );
        var bTile = SharedMemory.Allocate2D<int, Stride2D.DenseX>(
            new Index2D(TILE_SIZE, TILE_SIZE),
            new Stride2D.DenseX(TILE_SIZE)
        );

        // Initialize the accumulation variable for the result of C[global.X, global.Y]
        var sum = 0;

        // Loop over the tiles of A and B matrices
        // The loop increments by TILE_SIZE to process one tile at a time
        for (var i = 0; i < aView.IntExtent.X; i += TILE_SIZE)
        {
            // Load the corresponding tile of A into shared memory
            if (global.X < aView.IntExtent.X && y + i < aView.IntExtent.Y)
                aTile[x, y] = aView[global.X, y + i];
            else
                aTile[x, y] = 0; // Pad with zeros if out of bounds

            // Load the corresponding tile of B into shared memory
            if (x + i < bView.IntExtent.X && global.Y < bView.IntExtent.Y)
                bTile[x, y] = bView[x + i, global.Y];
            else
                bTile[x, y] = 0; // Pad with zeros if out of bounds

            // Ensure all threads in the thread group have finished loading tiles
            Group.Barrier();

            // Perform computation on the tiles
            for (var k = 0; k < TILE_SIZE; k++)
            {
                // Multiply elements of A and B tiles and accumulate the result
                sum += aTile[new Index2D(x, k)] * bTile[new Index2D(k, y)];
            }

            // Synchronize threads before loading the next tile
            Group.Barrier();
        }

        // Write the computed result to the output matrix C
        if (global.X < cView.IntExtent.X && global.Y < cView.IntExtent.Y)
        {
            cView[global] = sum; // Store the result in the appropriate position
        }
    }

    static void Main()
    {
        // Create an ILGPU context (manages devices and resources)
        using (var context = Context.CreateDefault())
        {
            // Select the preferred accelerator (GPU or CPU)
            using (var accelerator = context.GetPreferredDevice(preferCPU: true).CreateAccelerator(context))
            {
                try
                {
                    // Initialize sample input matrices (4x4)
                    int[,] a = {
                        { 1, 2, 3, 4},
                        { 5, 6, 7, 8}
                    };
                    int[,] b = {
                        { 9, 10},
                        { 11, 12},
                        { 13, 14},
                        { 15, 16}
                    };

                    int m = a.GetLength(0);
                    int ka = a.GetLength(1);
                    int kb = b.GetLength(0);
                    int n = b.GetLength(1);

                    int[,] hostResult = new int[m, n];

                    // Define the group size (number of threads per tile) and number of groups
                    Index2D groupSize = new Index2D(TILE_SIZE, TILE_SIZE); // Threads per group (block)
                    Index2D numGroups = new Index2D((m + TILE_SIZE - 1) / TILE_SIZE, (n + TILE_SIZE - 1) / TILE_SIZE); // Total number of thread groups

                    // Allocate device memory for input matrices A, B, and output matrix C
                    MemoryBuffer2D<int, Stride2D.DenseX> aBuffer = accelerator.Allocate2DDenseX<int>(new Index2D(m, ka));
                    MemoryBuffer2D<int, Stride2D.DenseX> bBuffer = accelerator.Allocate2DDenseX<int>(new Index2D(kb, n));
                    MemoryBuffer2D<int, Stride2D.DenseX> cBuffer = accelerator.Allocate2DDenseX<int>(new Index2D(m, n));

                    try
                    {
                        // Copy input matrices from host (CPU) to device (GPU) memory
                        aBuffer.CopyFromCPU(a);
                        bBuffer.CopyFromCPU(b);

                        // Load and precompile the kernel function
                        var loadedKernel = accelerator.LoadStreamKernel<
                            ArrayView2D<int, Stride2D.DenseX>,
                            ArrayView2D<int, Stride2D.DenseX>,
                            ArrayView2D<int, Stride2D.DenseX>>(MatrixMultiplyTiledKernel);

                        // Launch the kernel function on the GPU
                        // Specify the number of thread groups and threads per group
                        loadedKernel((numGroups, groupSize), aBuffer, bBuffer, cBuffer);

                        // Wait for the GPU to complete execution
                        accelerator.Synchronize();

                        // Retrieve the result matrix from GPU memory back to host memory
                        cBuffer.CopyToCPU(hostResult);

                        // Print the result matrix
                        Console.WriteLine("Result of matrix multiplication:");
                        for (int i = 0; i < m; i++)
                        {
                            for (int j = 0; j < n; j++)
                            {
                                Console.Write($"{hostResult[i, j],4} ");
                            }
                            Console.WriteLine();
                        }
                    }
                    finally
                    {
                        // Dispose of GPU resources to free up memory
                        aBuffer.Dispose();
                        bBuffer.Dispose();
                        cBuffer.Dispose();
                    }
                }
                finally
                {
                    // Dispose of the accelerator and context
                    accelerator.Dispose();
                }
            }
        }

        // Wait for user input before closing the console
        Console.ReadLine();
    }
}

错误分析与修正

核心错误:分块循环的终止条件错误

当前循环条件为 for (var i = 0; i < aView.IntExtent.X; i += TILE_SIZE),其中aView.IntExtent.X是矩阵A的行数(2),但矩阵乘法需要遍历的是A的列数/ B的行数(4)——这是矩阵乘法中累加维度的长度。

错误导致循环仅执行1次(仅处理前2列的元素),只累加了一半的乘积项,最终结果为正确值的前半部分。

修正方法

将循环终止条件改为遍历A的列数,即把i < aView.IntExtent.X替换为i < aView.IntExtent.Y:

// 修正:循环终止条件改为遍历A的列数(即B的行数)
for (var i = 0; i < aView.IntExtent.Y; i += TILE_SIZE)

修正后的完整Kernel代码

static void MatrixMultiplyTiledKernel(
        ArrayView2D<int, Stride2D.DenseX> aView, // Input matrix A in GPU memory
        ArrayView2D<int, Stride2D.DenseX> bView, // Input matrix B in GPU memory
        ArrayView2D<int, Stride2D.DenseX> cView) // Output matrix C in GPU memory
{
    // Get the global thread index in the 2D grid
    Index2D global = Grid.GlobalIndex.XY;

    // Get the thread indices within the thread group (local indices)
    int x = Group.IdxX; // Local row index within the tile
    int y = Group.IdxY; // Local column index within the tile

    // Allocate shared memory for storing tiles of matrix A and matrix B
    var aTile = SharedMemory.Allocate2D<int, Stride2D.DenseX>(
        new Index2D(TILE_SIZE, TILE_SIZE),
        new Stride2D.DenseX(TILE_SIZE)
    );
    var bTile = SharedMemory.Allocate2D<int, Stride2D.DenseX>(
        new Index2D(TILE_SIZE, TILE_SIZE),
        new Stride2D.DenseX(TILE_SIZE)
    );

    // Initialize the accumulation variable for the result of C[global.X, global.Y]
    var sum = 0;

    // 修正:循环终止条件改为遍历A的列数(即B的行数)
    for (var i = 0; i < aView.IntExtent.Y; i += TILE_SIZE)
    {
        // Load the corresponding tile of A into shared memory
        if (global.X < aView.IntExtent.X && y + i < aView.IntExtent.Y)
            aTile[x, y] = aView[global.X, y + i];
        else
            aTile[x, y] = 0; // Pad with zeros if out of bounds

        // Load the corresponding tile of B into shared memory
        if (x + i < bView.IntExtent.X && global.Y < bView.IntExtent.Y)
            bTile[x, y] = bView[x + i, global.Y];
        else
            bTile[x, y] = 0; // Pad with zeros if out of bounds

        // Ensure all threads in the thread group have finished loading tiles
        Group.Barrier();

        // Perform computation on the tiles
        for (var k = 0; k < TILE_SIZE; k++)
        {
            // Multiply elements of A and B tiles and accumulate the result
            sum += aTile[x, k] * bTile[k, y];
        }

        // Synchronize threads before loading the next tile
        Group.Barrier();
    }

    // Write the computed result to the output matrix C
    if (global.X < cView.IntExtent.X && global.Y < cView.IntExtent.Y)
    {
        cView[global] = sum; // Store the result in the appropriate position
    }
}

修正后,循环会执行2次(i=0和i=2),完整累加所有4个维度的乘积项,输出结果将与预期一致。

内容的提问来源于stack exchange,提问作者user366312

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.14 17:04:54