CUDA计算结果和CPU不一样

我仿照例子程序写了一个1024*1024两个矩阵相乘的程序,为了相互比对,我分别在CPU上计算一次,在GPU上计算一次,用GPU的CUBLAS库计算一次.为了避免不必要的麻烦,我都用的FLOAT. 这三者输出应该一样,或者相应不大.
我原来环境为 WINXP VS2010 CPU AMD X4 635 GPU GT240 …结果相差很小,都在小数点后面.
目前环境SLES X64 SERVER CPU I7 3770K GPU GTX660 ,结果相差较大,请问问题出在哪里?
代码如下:

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <sys/time.h>
#include <assert.h>
#include <pthread.h>
#include <cuda_runtime.h>
#include <cublas_v2.h>
#include <device_launch_parameters.h>



#define TILE_WIDTH 16
#define MAX_DIM 1024 

float MatrixA[MAX_DIM][MAX_DIM];
float MatrixB[MAX_DIM][MAX_DIM];
float MatrixC[MAX_DIM][MAX_DIM];


/* 设置矩阵内容 */
void FillMatrix()
{
register int i, j;
srand( ( unsigned int )time( NULL ) );

for ( i = 0; i < MAX_DIM; i ++ )
{
for ( j = 0; j < MAX_DIM; j ++ )
{
MatrixA[i][j] = ( float )rand() * ((float)rand() / 10000) / RAND_MAX;
MatrixB[i][j] = ( float )rand() * ((float)rand() / 10000) / RAND_MAX;
}
}
}


/********************************************************************/

/* 运行在CPU上,最笨的方法 */
void RunOnCPU()
{
float sum;
register int i, j, k;

for ( i = 0; i < MAX_DIM; ++ i )
{
for ( j = 0; j < MAX_DIM; ++ j )
{
sum = 0;
for ( k = 0; k < MAX_DIM; ++ k )
{
sum += MatrixA[i][k] * MatrixB[k][j];
}
MatrixC[i][j] = sum;
}
}
}



/********************************************************************/

/* 运行在GPU上 */
__global__ void Matrix_Mul1( float* c, const float* a, const float* b )
{
unsigned int i, j, bx, by, tx, ty;
float mulResult;
__shared__ float d_m[TILE_WIDTH][TILE_WIDTH];
__shared__ float d_n[TILE_WIDTH][TILE_WIDTH];

bx = blockIdx.x;
by = blockIdx.y;
tx = threadIdx.x;
ty = threadIdx.y;

mulResult = 0.0f;

for ( i = 0; i < gridDim.x; ++i )
{
d_m[ty][tx] = *( a + ( by * blockDim.y + ty ) * MAX_DIM + i * blockDim.x + tx );
d_n[ty][tx] = *( b + ( i * blockDim.y + ty ) * MAX_DIM + bx * blockDim.x + tx );
__syncthreads();

for ( j = 0; j < blockDim.x; ++ j )
{
mulResult += d_m[ty][j] * d_n[j][tx];
}
__syncthreads();
}
c[( by * blockDim.y + ty ) * MAX_DIM + bx * blockDim.x + tx] = mulResult;
}

void MatrixMul1( float* c, const float* a, const float* b )
{
int cnt;
float* dev_a;
float* dev_b;
float* dev_c;
cudaError_t cudaStatus;
// 64 * 64 ====> 16 * 16
dim3 grid( MAX_DIM / TILE_WIDTH, MAX_DIM / TILE_WIDTH );
dim3 blocks( TILE_WIDTH, TILE_WIDTH );

cnt = MAX_DIM * MAX_DIM;
dev_a = NULL;
dev_b = NULL;
dev_c = NULL;

/* 设置显卡,构建上下文 */
cudaStatus = cudaSetDevice( 0 );
assert( cudaStatus == cudaSuccess );

/* 分配显存 */
cudaStatus = cudaMalloc( ( void** )&dev_c, cnt * sizeof( float ) );
assert( cudaStatus == cudaSuccess );

cudaStatus = cudaMalloc( ( void** )&dev_a, cnt * sizeof( float ) );
assert( cudaStatus == cudaSuccess );

cudaStatus = cudaMalloc( ( void** )&dev_b, cnt * sizeof( float ) );
assert( cudaStatus == cudaSuccess );


/* 内存传送数据到显存 */
cudaStatus = cudaMemcpy( dev_a, a, cnt * sizeof( float ), cudaMemcpyHostToDevice );
assert( cudaStatus == cudaSuccess );

cudaStatus = cudaMemcpy( dev_b, b, cnt * sizeof( float ), cudaMemcpyHostToDevice );
assert( cudaStatus == cudaSuccess );

/* 调用显卡 */
Matrix_Mul1 <<< grid, blocks >>> ( dev_c, dev_a, dev_b );

/* 设备同步 */
cudaStatus = cudaDeviceSynchronize();
assert( cudaStatus == cudaSuccess );


/* 结果从显存传送到内存 */
cudaStatus = cudaMemcpy( c, dev_c, cnt * sizeof( float ), cudaMemcpyDeviceToHost );
assert( cudaStatus == cudaSuccess );

/* 释放显存 */
cudaFree( dev_c );
cudaFree( dev_a );
cudaFree( dev_b );

/* 重启显卡(上下文) */
cudaDeviceReset();
}


/********************************************************************/

/* 使用CUBLAS库 */
void MatrixMul2( float* c, const float* a, const float* b )
{
int cnt;
float* dev_a;
float* dev_b;
float* dev_c;
cublasHandle_t handle;
cublasStatus_t cuBlasStatus;
cudaError_t cudaStatus;
float alpha;
float beta;
// 64 * 64 ====> 16 * 16
dim3 grid( MAX_DIM / TILE_WIDTH, MAX_DIM / TILE_WIDTH );
dim3 blocks( TILE_WIDTH, TILE_WIDTH );


dev_a = NULL;
dev_b = NULL;
dev_c = NULL;

cnt = MAX_DIM * MAX_DIM;

alpha = 1.0f;
beta = 0.0f;


/* 设置显卡,构建上下文 */
cudaStatus = cudaSetDevice( 0 );
assert( cudaStatus == cudaSuccess );

/* 初始化BLAS库 */
cuBlasStatus = cublasCreate( &handle );
assert( cuBlasStatus == CUBLAS_STATUS_SUCCESS );

/* 分配显存 */
cudaStatus = cudaMalloc( ( void** )&dev_c, cnt * sizeof( float ) );
assert( cudaStatus == cudaSuccess );

cudaStatus = cudaMalloc( ( void** )&dev_a, cnt * sizeof( float ) );
assert( cudaStatus == cudaSuccess );

cudaStatus = cudaMalloc( ( void** )&dev_b, cnt * sizeof( float ) );
assert( cudaStatus == cudaSuccess );

/* 内存传送数据到显存 */
cudaStatus = cudaMemcpy( dev_a, a, cnt * sizeof( float ), cudaMemcpyHostToDevice );
assert( cudaStatus == cudaSuccess );

cudaStatus = cudaMemcpy( dev_b, b, cnt * sizeof( float ), cudaMemcpyHostToDevice );
assert( cudaStatus == cudaSuccess );


/* 处理 */
cuBlasStatus = cublasSgemm( handle, CUBLAS_OP_N, CUBLAS_OP_N, \
MAX_DIM, MAX_DIM, MAX_DIM, &alpha, \
dev_b, MAX_DIM, dev_a, MAX_DIM, &beta, dev_c, MAX_DIM );
assert( cuBlasStatus == CUBLAS_STATUS_SUCCESS );

/* 处理 */
cuBlasStatus = cublasSgemm( handle, CUBLAS_OP_N, CUBLAS_OP_N, \
MAX_DIM, MAX_DIM, MAX_DIM, &alpha, \
dev_b, MAX_DIM, dev_a, MAX_DIM, &beta, dev_c, MAX_DIM );
assert( cuBlasStatus == CUBLAS_STATUS_SUCCESS );

/* 结果从显存传送到内存 */
cudaStatus = cudaMemcpy( c, dev_c, cnt * sizeof( float ), cudaMemcpyDeviceToHost );
assert( cudaStatus == cudaSuccess );

/* 销毁BLAS */
cuBlasStatus = cublasDestroy( handle );
assert( cuBlasStatus == CUBLAS_STATUS_SUCCESS );

/* 重启显卡(上下文) */
cudaDeviceReset();
}


/********************************************************************/


int main()
{
struct timeval tm1, tm2;
printf("short:%d\tint:%d\tlong:%d\tfloat:%d\tdouble:%d\n", sizeof(short), sizeof(int), sizeof(long), sizeof(float), sizeof(double));

FillMatrix();

memset( MatrixC, 0, sizeof( MatrixC ) );
gettimeofday(&tm1, NULL);
RunOnCPU();
gettimeofday(&tm2, NULL);
printf( "%f, %f, %f, %f\nTIMES: %f\n\n", MatrixC[0][0], MatrixC[37][71], MatrixC[MAX_DIM - 1][MAX_DIM - 1], MatrixC[217][13],(tm2.tv_sec - tm1.tv_sec) * 1000 + (float)(tm2.tv_usec - tm1.tv_usec) / 1000 );


memset( MatrixC, 0, sizeof( MatrixC ) );
gettimeofday(&tm1, NULL);
MatrixMul1( ( float* )MatrixC, ( const float* )MatrixA, ( const float* )MatrixB );
gettimeofday(&tm2, NULL);
printf( "%f, %f, %f, %f\nTIMES: %f\n\n", MatrixC[0][0], MatrixC[37][71], MatrixC[MAX_DIM - 1][MAX_DIM - 1], MatrixC[217][13],(tm2.tv_sec - tm1.tv_sec) * 1000 + (float)(tm2.tv_usec - tm1.tv_usec) / 1000 );

memset( MatrixC, 0, sizeof( MatrixC ) );
gettimeofday(&tm1, NULL);
MatrixMul2( ( float* )MatrixC, ( const float* )MatrixA, ( const float* )MatrixB );
gettimeofday(&tm2, NULL);
printf( "%f, %f, %f, %f\nTIMES: %f\n\n", MatrixC[0][0], MatrixC[37][71], MatrixC[MAX_DIM - 1][MAX_DIM - 1], MatrixC[217][13],(tm2.tv_sec - tm1.tv_sec) * 1000 + (float)(tm2.tv_usec - tm1.tv_usec) / 1000 );


return 0;
}

结果如下

short:2 int:4 long:8 float:4 double:8
2921729884160.000000, 2847927435264.000000, 3035846410240.000000, 2896220389376.000000
TIMES: 2813.224121

2921730146304.000000, 2847927435264.000000, 3035846410240.000000, 2896220389376.000000
TIMES: 207.865005

2921730146304.000000, 2847927435264.000000, 3035846410240.000000, 2896220389376.000000
TIMES: 174.639008

请问是什么原因?
谢谢了.

新环境和老环境毕竟不同.特别是线程,计时…所以单贴WINXP下的结果:(效率,调优之类先不谈)
6687720.500000, 6869132.500000, 6410965.000000, 6952017.500000
TIMES: 47125
6687720.500000, 6869132.500000, 6410964.500000, 6952017.000000
TIMES: 328
6687720.500000, 6869132.500000, 6410964.500000, 6952017.000000
TIMES: 250

像上面的结果完全可以接受…但是,LINUX下硬件更强了…反而不准了…
请问下各位大侠…问题出在哪里?

我自己分析如下:

  1. 代码问题

无论在WINDOWS下还是LINXU下.GPU两个计算结果是一致的…至少证明,我GPU方面代码问题不大.因为一个是用的官方库.
CPU代码,就三个FOR循环.我不认为会写错…另外,所得结果中,一般来说,都有一样的…也证明了.CPU和GPU方面代码应该都没有问题.
2. 编译选项问题
这中间,涉及VS2010(VC10), g++4.3, CUDA5.0.35(两个环境版本号一样).
VS2010下大部分都是VC自己的选项…也即-Xcompiler 后面的…NVCC方面就一个-gencode=arch=compute_12,code=sm_12和一个-m32
G++下面更简单了…就一个-gencode=arch=compute_30,code=sm_30和-m64
但我个人印象中,无论VC还是G++都有一个控制FLOAT规格的东西…比如说VC我印象中是float=precise(记不准了.大概这样).
G++是什么我真忘了…
我觉得会和这个有关吗? 毕竟10亿次FLOAT乘 10亿次FLOAT加.数据不会小.请大侠赐教…
3. LINUX下驱动有问题
我在本论坛还是哪里呀…看到说LINUX下驱动改一下就好了…但是,我SAMPLE里面的程序都可顺利编译并运行…如果错,应该跑不起来吧…而且,以这个程序为例…所用时间,也在我料想之中.
4. GPU,CPU硬件不同.
这就没办法了.说明有一个硬件真的有问题…如果这样…我马上去送修了…呵呵

这个很正常。

简单的说:CPU和GPU的运算顺序,中间结果的精度,算法,都会影响结果的。

请不要较真取得和CPU上完全一样的结果。

以及,结果不同不代表CPU比GPU精确,也不代表GPU比CPU精确的(这个真心不一定)。

以及,从你这个问题看,应该是后来的GTX660的结果更精确了。

请不要惊慌。也不要以GT240/CPU的结果为准。

(你的GT240和CPU(AMD K12)都不支持FMA的,在矩阵乘法这种大量的乘加计算上,GTX660的FMA带来了极大的精度优势的)

(以及,如果需要接近老卡/AMD K12的结果,您可以人为的增大误差:
将您所有的a * b + c都改成__fadd*()和__fmul*()的分步。这样即可轻松增大误差,满足您的需求)

首先谢谢楼上的回答,
我个人是觉得这个结果比较诡…
经常是4个数里面,三个完全一样…有一个不同,而且相差不小…
如果说,有差异, 应该几个数都不一样.
如果说,没有差异吧,程序应该没问题,为什么结果总有一个或两个不一样.

你修改了2#了。

我将重新做出回答。

CPU在32-bit和64-bit下的默认ABI的计算浮点数方式不同,
在32-bit下,windows和linux平台均默认使用x87。
在64-bit下,它们均默认使用SSE.

前者具有80-bit的精简精度,弥补了没有FMA带来的缺陷。

所以你这个和平台无关,你这个实际上只是32-bit下默认的计算单元的问题,楼主可以使用64-bit的windows, 看看CPU可怜的精度。

回答完毕。

谢谢版主的回复…真心感谢…
但是,还是那个问题…
只在一个平台上…为什么我显示的四个数里,经常是两到三个完全一样…而一个不一样,并且差的还不少?
如果都不一样…这很容易理解…如果都一样…那就更好…关键是都是64位…平台.系统环境什么也一样…
为什么会出现,部分不同…?

以及,更精确的说一下,

您在XP下默认编译,运行,
CPU将使用x87, 中间结果使用80-bit。
GT240将使用fmad, 中间结果只能计算到double。
GTX650将使用FMA, 中间结果将计算到无限精度。(这个也是NV长期宣传的特色服务,无限精度,仅此一家)

而如果使用了X64便宜,
CPU将使用SSE,中间结果只有23-bit!

那么:
如果您用的是32-bit平台编译,
CPU将有80-bit, GT240将有53-bit, GTX660将有无限位的精度。
此时您将感到差别不是很大(请注意此时依然GTX660最精确)

而如果您用64-bit平台编译,
CPU将有23-bit, GT240仍然有53-bit, GTX660将有无限精度。
此时结果将相差较大。特别是GTX660对比CPU此时。

关于您的为何对部分数据偏差较大,部分数据偏移较小,无法直接给予回答,

不过,您可以手工用笔和纸分别模拟23-bit, 53-bit, 80-bit, 无限中间精度的多个结果,
看看它们对与特定的一些参与计算的值,在哪里会导致偏差,在哪里有不会。

这样您就可以知道对于一些数据在哪步产生的差了。

感谢版主…很少有这么热心的…多谢了…
您说的,我大概明白了,也就是说64位下…因为各种原因,导致CPU,GPU运算中间精度误差更大了…
这个我能理解…多谢了…原来真不知道…

但是,同在64位下.以我一楼数据为例…为什么后三个数字完全一样…而第一个不一样…而且.这种情况经常发生…每次都是一到两个不一样…另两个完全一样.这是为什么?

我和您说过,这玩意可能结果对比有误差,

但如果您要问如何能产生精确的某种误差模型,这个真心不知道,也许您需要使用9#建议。

谢谢版主了…我自己发现个问题…同样的程序…两个结果大小相差太远.中间可能有其它问题…我把问题排查完…再来请教…不好意思了呀…LINUX.

谢谢了…

LZ您好,我再补充一点:

您的计算使用的是浮点数,所谓浮点数,顾名思义,小数点的位置是浮动的。他们可以通过浮动小数点表示很大范围内的数,但是有效数字的位数是固定的。

您使用了单精度的浮点数,这种浮点数的有效数字换算成十进制也就7位左右。(注意并不是小数点后面7位,而是一共只有7位左右)
您可以回顾一下您1#和2#给出的数据,出现差别至少都是在第6位有效数字的时候,这已经属于正常现象了,毕竟不同的计算精度,截断精度,计算顺序等都会有所影响。

您在1#和2#给出的数据,本身大小相差巨大,您不可能要求相差如此之大的两组数据,都在小数点后面同样的位数处才有误差,而只能按照有效位数去要求(也就是你不能要求一个很大的数的误差和一个小得多的数的误差同样细微)。否则就不符合浮点数的储存原理了。

所以,请以科学合理的方法评估和认识您的计算结果。

祝您好运~

以及,LZ您还可以这样评估一下:

不看差别的绝对大小,而是去看相对误差。

前一组中有:0.5 / 6687720 =7.476389561763949e-8的相对误差;
后一组中有:262144 / 2921729884160=8.972218870101561e-8的相对误差。

容易看出,相对误差水平是相当的,这个和前面叙述的浮点数的存储特性是一致的。

而您的数据大小相差了几十万倍的大小,以及可能在计算中,计算规模也大了很多,有更多积累误差的机会,这个相对误差水平是完全合理的。

大致如此,供您参考,祝您编码顺利~

首先谢谢两位版主.
结合两位版主的想法,我自己又试了下,很可能是中间运算误差的原因.这个相对误差也并不大.

至于上面我两个环境下,数据差距很大的原因已经找到了…
64位系统,RAND_MAX是32位的…而32位系统RAND_MAX只有16位…

先贴下两个结果
WINDOWS XP

10614726.000000, 5090015.000000, 110866.632813, 9604837.000000, 9567676.000000
TIMES: 21969

10614726.000000, 5090015.000000, 110866.632813, 9604837.000000, 9567676.000000
TIMES: 7375

10614726.000000, 5090014.500000, 110866.625000, 9604837.000000, 9567676.000000
TIMES: 313

10614726.000000, 5090014.500000, 110866.625000, 9604837.000000, 9567676.000000
TIMES: 250

LINUX 64

10144230.000000, 5652628.500000, 103878.734375, 10019275.000000, 9244681.000000
TIMES: 2832.854980

10144230.000000, 5652628.500000, 103878.734375, 10019275.000000, 9244681.000000
TIMES: 788.281982

10144230.000000, 5652629.000000, 103878.742188, 10019275.000000, 9244681.000000
TIMES: 195.311005

10144230.000000, 5652629.000000, 103878.742188, 10019275.000000, 9244681.000000
TIMES: 169.654007

中间有事离开了一下,现在才回…实在抱歉…
谢谢两位版主了.

对了,第二个结果是CPU上多线程跑的成绩. WINDOWS下开了4线程. LINUX下开了8线程…都是考虑硬件因素的.

贴下代码,希望对大家能有点用

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <sys/time.h>
#include <assert.h>
#include <pthread.h>
#include <cuda_runtime.h>
#include <cublas_v2.h>
#include <device_launch_parameters.h>


#define TILE_WIDTH   16
#define MAX_DIM      1024


float MatrixA[MAX_DIM][MAX_DIM];
float MatrixB[MAX_DIM][MAX_DIM];
float MatrixC[MAX_DIM][MAX_DIM];


/* 设置矩阵内容 */
void FillMatrix()
{
	register int i, j;
	srand( ( unsigned int )time( NULL ) );

	for ( i = 0; i < MAX_DIM; i ++ )
	{
		for ( j = 0; j < MAX_DIM; j ++ )
		{
			MatrixA[i][j] = ( float )( rand( ) % 0x7fff ) * ( ( float )rand() / RAND_MAX );
			if ( i > j )
			{
				MatrixA[i][j] /= 100.123f;
			}
			MatrixB[i][j] = 1.23456f;
		}
	}
}


/********************************************************************/

/* 运行在CPU上,最笨的方法 */
void RunOnCPU()
{
	register float sum;
	register int i, j, k;

	for ( i = 0; i < MAX_DIM; ++ i )
	{
		for ( j = 0; j < MAX_DIM; ++ j )
		{
			sum = 0;
			for ( k = 0; k < MAX_DIM; ++ k )
			{
				sum += MatrixA[i][k] * MatrixB[k][j];
			}
			MatrixC[i][j] = sum;
		}
	}
}



/********************************************************************/

pthread_rwlock_t g_rwlock = PTHREAD_RWLOCK_INITIALIZER;

/* 子线程ROUTINE */
void* ThreadRoutine( void* arg )
{
	register int i, j, k;
	long dy, dy1;
	float mulResult;

	i = ( ( int )MAX_DIM >> 3 );
	dy = i * ( ( long  )arg );
	dy1 = dy + i;

	pthread_rwlock_rdlock( &g_rwlock );

	for ( i = dy; i < dy1; i ++ )
	{
		for ( j = 0; j < MAX_DIM; j ++ )
		{
			mulResult = 0;
			for ( k = 0; k < MAX_DIM; k ++ )
			{
				mulResult += MatrixA[i][k] * MatrixB[k][j];
			}

			MatrixC[i][j] = mulResult;
		}
	}

	pthread_rwlock_unlock( &g_rwlock );
	return NULL;
}


/* 运行在CPU上, CPU==>i7 3770K  8个线程 */
void RunOnCPUMulThr()
{
	int ret;
	register int i, j;
	pthread_t pids[8];
	struct timeval tv;
	struct timespec abstime;


	pthread_rwlock_wrlock( &g_rwlock );

	for ( i = 0; i < 8; i ++ )
	{
		ret = pthread_create( &pids[i], NULL, ThreadRoutine, ( void* )i );
		if ( ret != 0 )
		{
			fprintf( stderr, "pthread_create: %s\n", strerror( ret ) );

			for ( j = 0; j < i; j ++ )
			{
				pthread_cancel( pids[j] );
			}
			pthread_rwlock_unlock( &g_rwlock );
			return ;
		}
	}

	for ( i = 0; i < 8; i ++ )
	{
		pthread_detach( pids[i] );
	}

	pthread_rwlock_unlock( &g_rwlock );

	usleep( 5000 );

	do
	{
		gettimeofday( &tv, NULL );
		abstime.tv_sec = tv.tv_sec;
		abstime.tv_nsec = ( tv.tv_usec + 5000 ) * 1000;
	}
	while ( pthread_rwlock_timedwrlock( &g_rwlock, &abstime ) != 0 );

	pthread_rwlock_unlock( &g_rwlock );

}

/********************************************************************/

/* 运行在GPU上 */
__global__ void Matrix_Mul1( float* c, const float* a, const float* b )
{
	unsigned int i, j, bx, by, tx, ty;
	float mulResult;
	__shared__ float d_m[TILE_WIDTH][TILE_WIDTH];
	__shared__ float d_n[TILE_WIDTH][TILE_WIDTH];

	bx = blockIdx.x;
	by = blockIdx.y;
	tx = threadIdx.x;
	ty = threadIdx.y;

	mulResult = 0.0;

	for ( i = 0; i < gridDim.x; ++i )
	{
		d_m[ty][tx] = *( a + ( by * blockDim.y + ty ) * MAX_DIM + i * blockDim.x + tx );
		d_n[ty][tx] = *( b + ( i * blockDim.y + ty ) * MAX_DIM + bx * blockDim.x + tx );
		__syncthreads();

		for ( j = 0; j < blockDim.x; ++ j )
		{
			mulResult += d_m[ty][j] * d_n[j][tx];
		}
		__syncthreads();
	}
	c[( by * blockDim.y + ty ) * MAX_DIM + bx * blockDim.x + tx] = mulResult;
}

void MatrixMul1( float* c, const float* a, const float* b )
{
	int cnt;
	float* dev_a;
	float* dev_b;
	float* dev_c;
	cudaError_t cudaStatus;
	// 64 * 64 ====> 16 * 16
	dim3 grid( MAX_DIM / TILE_WIDTH, MAX_DIM / TILE_WIDTH );
	dim3 blocks( TILE_WIDTH, TILE_WIDTH );

	cnt = MAX_DIM * MAX_DIM;
	dev_a = NULL;
	dev_b = NULL;
	dev_c = NULL;

	/* 设置显卡,构建上下文 */
	cudaStatus = cudaSetDevice( 0 );
	assert( cudaStatus == cudaSuccess );

	/* 分配显存 */
	cudaStatus = cudaMalloc( ( void** )&dev_c, cnt * sizeof( float ) );
	assert( cudaStatus == cudaSuccess );

	cudaStatus = cudaMalloc( ( void** )&dev_a, cnt * sizeof( float ) );
	assert( cudaStatus == cudaSuccess );

	cudaStatus = cudaMalloc( ( void** )&dev_b, cnt * sizeof( float ) );
	assert( cudaStatus == cudaSuccess );


	/* 内存传送数据到显存 */
	cudaStatus = cudaMemcpy( dev_a, a, cnt * sizeof( float ), cudaMemcpyHostToDevice );
	assert( cudaStatus == cudaSuccess );

	cudaStatus = cudaMemcpy( dev_b, b, cnt * sizeof( float ), cudaMemcpyHostToDevice );
	assert( cudaStatus == cudaSuccess );

	/* 调用显卡 */
	Matrix_Mul1 <<< grid, blocks >>> ( dev_c, dev_a, dev_b );

	/* 设备同步 */
	cudaStatus = cudaDeviceSynchronize();
	assert( cudaStatus == cudaSuccess );


	/* 结果从显存传送到内存 */
	cudaStatus = cudaMemcpy( c, dev_c, cnt * sizeof( float ), cudaMemcpyDeviceToHost );
	assert( cudaStatus == cudaSuccess );

	/* 释放显存 */
	cudaFree( dev_c );
	cudaFree( dev_a );
	cudaFree( dev_b );

	/* 重启显卡(上下文) */
	cudaDeviceReset();
}


/********************************************************************/

/* 使用CUBLAS库 */
void MatrixMul2( float* c, const float* a, const float* b )
{
	int cnt;
	float* dev_a;
	float* dev_b;
	float* dev_c;
	cublasHandle_t handle;
	cublasStatus_t cuBlasStatus;
	cudaError_t cudaStatus;
	float alpha;
	float beta;
	// 64 * 64 ====> 16 * 16
	dim3 grid( MAX_DIM / TILE_WIDTH, MAX_DIM / TILE_WIDTH );
	dim3 blocks( TILE_WIDTH, TILE_WIDTH );


	dev_a = NULL;
	dev_b = NULL;
	dev_c = NULL;

	cnt = MAX_DIM * MAX_DIM;

	alpha = 1.0f;
	beta  = 0.0f;


	/* 设置显卡,构建上下文 */
	cudaStatus = cudaSetDevice( 0 );
	assert( cudaStatus == cudaSuccess );

	/* 初始化BLAS库 */
	cuBlasStatus = cublasCreate( &handle );
	assert( cuBlasStatus == CUBLAS_STATUS_SUCCESS );

	/* 分配显存 */
	cudaStatus = cudaMalloc( ( void** )&dev_c, cnt * sizeof( float ) );
	assert( cudaStatus == cudaSuccess );

	cudaStatus = cudaMalloc( ( void** )&dev_a, cnt * sizeof( float ) );
	assert( cudaStatus == cudaSuccess );

	cudaStatus = cudaMalloc( ( void** )&dev_b, cnt * sizeof( float ) );
	assert( cudaStatus == cudaSuccess );

	/* 内存传送数据到显存 */
	cudaStatus = cudaMemcpy( dev_a, a, cnt * sizeof( float ), cudaMemcpyHostToDevice );
	assert( cudaStatus == cudaSuccess );

	cudaStatus = cudaMemcpy( dev_b, b, cnt * sizeof( float ), cudaMemcpyHostToDevice );
	assert( cudaStatus == cudaSuccess );


	/* 处理 */
	cuBlasStatus = cublasSgemm( handle, CUBLAS_OP_N, CUBLAS_OP_N, \
	                            MAX_DIM, MAX_DIM, MAX_DIM, &alpha, \
	                            dev_b, MAX_DIM, dev_a, MAX_DIM, &beta, dev_c, MAX_DIM );
	assert( cuBlasStatus == CUBLAS_STATUS_SUCCESS );

	/* 处理 */
	cuBlasStatus = cublasSgemm( handle, CUBLAS_OP_N, CUBLAS_OP_N, \
	                            MAX_DIM, MAX_DIM, MAX_DIM, &alpha, \
	                            dev_b, MAX_DIM, dev_a, MAX_DIM, &beta, dev_c, MAX_DIM );
	assert( cuBlasStatus == CUBLAS_STATUS_SUCCESS );

	/* 结果从显存传送到内存 */
	cudaStatus = cudaMemcpy( c, dev_c, cnt * sizeof( float ), cudaMemcpyDeviceToHost );
	assert( cudaStatus == cudaSuccess );

	/* 销毁BLAS */
	cuBlasStatus = cublasDestroy( handle );
	assert( cuBlasStatus == CUBLAS_STATUS_SUCCESS );

	/* 重启显卡(上下文) */
	cudaDeviceReset();
}


/********************************************************************/


int main()
{
	struct timeval tm1, tm2;

	FillMatrix();

	memset( MatrixC, 0, sizeof( MatrixC ) );
	gettimeofday( &tm1, NULL );
	RunOnCPU();
	gettimeofday( &tm2, NULL );
	printf( "%f, %f, %f, %f, %f\nTIMES: %f\n\n",  MatrixC[0][0], MatrixC[MAX_DIM / 2][MAX_DIM / 2], \
	        MatrixC[MAX_DIM - 1][MAX_DIM - 1], MatrixC[37][71], MatrixC[83][53], \
	        ( tm2.tv_sec - tm1.tv_sec ) * 1000 + ( float )( tm2.tv_usec - tm1.tv_usec ) / 1000 );

	memset( MatrixC, 0, sizeof( MatrixC ) );
	gettimeofday( &tm1, NULL );
	RunOnCPUMulThr();
	gettimeofday( &tm2, NULL );
	printf( "%f, %f, %f, %f, %f\nTIMES: %f\n\n",  MatrixC[0][0], MatrixC[MAX_DIM / 2][MAX_DIM / 2], \
	        MatrixC[MAX_DIM - 1][MAX_DIM - 1], MatrixC[37][71], MatrixC[83][53], \
	        ( tm2.tv_sec - tm1.tv_sec ) * 1000 + ( float )( tm2.tv_usec - tm1.tv_usec ) / 1000 );

	memset( MatrixC, 0, sizeof( MatrixC ) );
	gettimeofday( &tm1, NULL );
	MatrixMul1( ( float* )MatrixC, ( const float* )MatrixA, ( const float* )MatrixB );
	gettimeofday( &tm2, NULL );
	printf( "%f, %f, %f, %f, %f\nTIMES: %f\n\n",  MatrixC[0][0], MatrixC[MAX_DIM / 2][MAX_DIM / 2], \
	        MatrixC[MAX_DIM - 1][MAX_DIM - 1], MatrixC[37][71], MatrixC[83][53], \
	        ( tm2.tv_sec - tm1.tv_sec ) * 1000 + ( float )( tm2.tv_usec - tm1.tv_usec ) / 1000 );

	memset( MatrixC, 0, sizeof( MatrixC ) );
	gettimeofday( &tm1, NULL );
	MatrixMul2( ( float* )MatrixC, ( const float* )MatrixA, ( const float* )MatrixB );
	gettimeofday( &tm2, NULL );
	printf( "%f, %f, %f, %f, %f\nTIMES: %f\n\n",  MatrixC[0][0], MatrixC[MAX_DIM / 2][MAX_DIM / 2], \
	        MatrixC[MAX_DIM - 1][MAX_DIM - 1], MatrixC[37][71], MatrixC[83][53], \
	        ( tm2.tv_sec - tm1.tv_sec ) * 1000 + ( float )( tm2.tv_usec - tm1.tv_usec ) / 1000 );

	return 0;
}


感谢楼主发表这个心得。

对于我们和楼主都获得了充分的教训:
(1)对于楼主,要仔细观察数据范围,对比偏移是否“真的很大”。
(2)对于我们,要自己观察楼主的数据,独立判断,不能原文说大就大,说小就小。

:slight_smile:
欢迎楼主再次莅临。

楼主那么在意精度的话,自己实现高精度计算去,float、double全换成整数计算,保证结果能完全吻合上,而且想要精确到小数点后几百位也不会太困难

cuda编译器默认代码编译选项是:–arch compute_10 –code sm_10,这个可能是为了兼容老显卡吧,但是这样计算精度就跟CPU有差异了。
把编译选项中的10换成你显卡实际的计算能力就可以,双精度浮点计算结果跟CPU也不会有差异。

您好:

1:这个是为了在老卡上可以跑,或者说是为了兼容。

2:但这并非是GPU结果和CPU结果有差异的唯一原因。

3:以及换成更高版本的计算能力编译,在更高版本的GPU上跑,也并不保证和CPU结果完全一致。

具体原因本帖和之前的讨论帖已经叙述多次,不再赘述。

祝您好运~