为什么C#和CUDA C ++之间的矩阵求逆计算中的中间值有些不同?

问题描述

我编写了两个程序来使用Gaussian elimination计算矩阵的逆,第一个程序是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,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,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,");
            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];
}

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,i * n,v);
    cudaDeviceSynchronize();
    cudamemcpy(Z,d_A,cudamemcpyDevicetoHost);
    cudamemcpy(B,d_B,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 + 1,n - i - 1);
        ModifyRow <<<GridB,BlockB>>> (d_A,n);
        cudaDeviceSynchronize();
        cudamemcpy(Z,cudamemcpyDevicetoHost);
        cudamemcpy(B,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,n);
    cudaDeviceSynchronize();
    cudamemcpy(Z,cudamemcpyDevicetoHost);
}
cudamemcpy(B,cudamemcpyDevicetoHost);
cudaFree(d_A);
cudaFree(d_B);

我比较了 cpu.csv GPU.csv 文件中的值,并发现了这些差异。

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

解决方法

NVIDIA documentation(大约2/3)开始:

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

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