首页 > 解决方案 > 为什么 C# 和 CUDA C++ 的矩阵逆计算中的中间值略有不同?

问题描述

我写了两个程序来使用高斯消元法计算矩阵的逆,第一个程序是在 C# 中,第二个是在 CUDA C++ 中。这两个程序遵循完全相同的程序并给出相同的最终结果。但是,当我检查中间步骤中的值时,我发现值略有不同,相对误差小于 1e-5。

这是两个程序的每个代码的一部分。

C#

int i, j, i1, n, y, z;
double[,] M = new double[n, n];
double[,] inv = new double[n, n];
for (i = 0; i < n; i++)
    inv[i, i] = 1;
for (i = 0; i < n; i++)
{
    for (j = i + 1; j < n; j++)
        M[i, j] /= M[i, i];
    for (j = 0; j < n; j++)
        inv[i, j] /= M[i, i];
    if (i != n - 1)
    {
        for (i1 = i + 1; i1 < n; i1++)
            if (Math.Abs(M[i1, i]) >= 1e-9)
            {
                for (j = i + 1; j < n; j++)
                    M[i1, j] -= M[i1, i] * M[i, j];
                for (j = 0; j < n; j++)
                    inv[i1, j] -= M[i1, i] * inv[i, j];
            }
        f = new StreamWriter("CPU.csv");
        for (y = 0; y < n; y++)
        {
            for (z = 0; z < n; z++)
                f.Write(M[y, z].ToString() + ",");
            for (z = 0; z < n; z++)
                f.Write(ans[y, z].ToString() + ",");
            f.WriteLine();
        }
        f.Close();
    }
}
for (i = n - 1; i > 0; i--)
{
    for (i1 = 0; i1 < i; i1++)
        if (Math.Abs(M[i1, i]) >= 1e-9)
            for (j = 0; j < n; j++)
                inv[i1, j] -= M[i1, i] * inv[i, j];
}

CUDA C++

int i, j;
double v;
double* d_A, * d_B, * d_v, * Z;
size = n * n * sizeof(double);
cudaMalloc(&d_A, size);
cudaMemcpy(d_A, A, size, cudaMemcpyHostToDevice);
cudaMalloc(&d_B, size);
cudaMalloc(&d_v, sizeof(double));
Z = new double[n * n];
Unity <<<1, n>>> (d_B, n);
cudaDeviceSynchronize();
for (i = 0; i < n; i++)
{
    GetVal <<<1, 1>>> (d_A, i * (n + 1), d_v);
    cudaMemcpy(&v, d_v, sizeof(double), cudaMemcpyDeviceToHost);
    if (i != n - 1)
        DivideRow <<<1, n - i - 1>>> (d_A, i * (n + 1) + 1, n - i - 1, v);
    DivideRow <<<1, n>>> (d_B, i * n, n, v);
    cudaDeviceSynchronize();
    cudaMemcpy(Z, d_A, size, cudaMemcpyDeviceToHost);
    cudaMemcpy(B, d_B, size, cudaMemcpyDeviceToHost);
    if (i != n - 1)
    {
        dim3 GridA(1, 1);
        dim3 BlockA(n - i - 1, n - i - 1);
        dim3 GridB(1, 1);
        dim3 BlockB(n - i - 1, n);
        ModifyRow <<<GridA, BlockA>>> (d_A, i, i, i + 1, n - i - 1, n - i - 1);
        ModifyRow <<<GridB, BlockB>>> (d_A, n, i, i, d_B, i + 1, 0, n - i - 1, n);
        cudaDeviceSynchronize();
        cudaMemcpy(Z, d_A, size, cudaMemcpyDeviceToHost);
        cudaMemcpy(B, d_B, size, cudaMemcpyDeviceToHost);
        myfile.open("GPU.csv");
        for (x = 0; x < n; x++)
        {
            for (y = 0; y < n; y++)
                myfile << Z[x * n + y] << ",";
            for (y = 0; y < n; y++)
                myfile << B[x * n + y] << ",";
            myfile << "\n";
        }
        myfile.close();
    }
}
cudaFree(d_v);
for (i = n - 1; i > 0; i--)
{
    dim3 GridB(1, 1);
    dim3 BlockB(i, n);
    ModifyRow <<<GridB, BlockB>>> (d_A, n, i, i, d_B, 0, 0, i, n);
    cudaDeviceSynchronize();
    cudaMemcpy(Z, d_A, size, cudaMemcpyDeviceToHost);
    cudaMemcpy(B, d_B, size, cudaMemcpyDeviceToHost);
}
cudaMemcpy(B, d_B, size, cudaMemcpyDeviceToHost);
cudaFree(d_A);
cudaFree(d_B);

我比较了CPU.csvGPU.csv文件中的值,发现了这些差异。

这可能是什么原因?CUDA C++ 中的计算精度是否低于 C#?

标签: c#c++cudaprecisionmatrix-inverse

解决方案


来自NVIDIA 文档(大约下降了 2/3):

[舍入] 的结果是,不能期望不同的数学库为给定的输入计算完全相同的结果。这也适用于 GPU 编程。为 GPU 编译的函数将使用 NVIDIA CUDA 数学库实现,而为 CPU 编译的函数将使用主机编译器数学库实现(例如 Linux 上的 glibc)。因为这些实现是独立的,并且都不能保证正确四舍五入,所以结果通常会略有不同。

告诉你所有你需要知道的,真的。


推荐阅读