FORTRAN比C快——对于在同一个处理器上运行的矩阵乘法程序,为什么?
我在xeon处理器系统上使用C和FORTRAN运行FORTRAN比C快——对于在同一个处理器上运行的矩阵乘法程序,为什么?,c,matrix,time,fortran,matrix-multiplication,C,Matrix,Time,Fortran,Matrix Multiplication,我在xeon处理器系统上使用C和FORTRAN运行n*n矩阵乘法代码。我惊讶地看到这两种方法之间的实时差异。为什么FORTRAN代码给了我更快的执行时间?我使用了dgemm(),并从我的C代码中调用了相同的函数。我尝试运行通用C代码,改变循环顺序,并尝试使用不同的标志来优化模拟过程。我无法获得使用dgemm()获得的相同响应 FORTRAN代码-dgemm(): #包括“stdio.h” #包括“time.h” #包括“sys/time.h” #包括“math.h” #包括“stdlib.h”
n*n
矩阵乘法代码。我惊讶地看到这两种方法之间的实时差异。为什么FORTRAN代码给了我更快的执行时间?我使用了dgemm()
,并从我的C代码中调用了相同的函数。我尝试运行通用C代码,改变循环顺序,并尝试使用不同的标志来优化模拟过程。我无法获得使用dgemm()
获得的相同响应
FORTRAN代码-dgemm():
#包括“stdio.h”
#包括“time.h”
#包括“sys/time.h”
#包括“math.h”
#包括“stdlib.h”
长读TSC(无效)
{
/*读取英特尔x86芯片上的时间戳计数器*/
并集{long-long-complete;无符号整数部分[2];}个记号;
__asm_uu2;(“rdtsc;mov%%eax,%0;mov%%edx,%1”
:“=mr”(第[0]部分),
“=mr”(第[1]部分)
:/*无输入*/
:“eax”、“edx”);
返回ticks.complete;
}
挥发性双gtod(无效)
{
静态结构时间值电视;
静态结构时区;
gettimeofday(&tv,&tz);
返回tv.tv_sec+1.e-6*tv.tv_usec;
}
无效dgemm(字符*transa,字符*transb,字符*x,字符*xa,字符*xb,双字符*alphaa,双字符*ma,字符*xc,双字符*mb,字符*xd,双字符*betaa,双字符*msum,双字符*xe);
int main(int argc,字符**argv)
{
int n=atoi(argv[1]);
朗朗tm;
//禁用转置,禁用C中的加法运算:=alpha*op(A)*op(B)+beta*C
char trans='N';
双α=1.0;
双β=0.0;
长整型p=2*n*n*n;
长双q;
双倍*a、*b、*和;
双t_real,t,flop_clk,flops;
int i,j,k;
//内存分配
a=(双*)malloc(n*n*sizeof(双));
b=(双*)malloc(n*n*sizeof(双));
总和=(双*)malloc(n*n*sizeof(双));
//矩阵初始化
对于(i=0;i只是一个猜测,因为OP没有显示任何代码。如果他正在调用(来自LAPACK BLAS),则可能是用Fortran编写的
C和Fortran中的规则是不同的
在C例程中声明形式时,可以(小心!)使用restrict
关键字。这应该会有所帮助
此外,C和Fortran中的算术也不同。在C的某些方言中(例如C89),每个浮点运算都是基于双精度数字计算的。IIRC在Fortran中的定义不同。并且在C89和C99之间发生了变化(可能在C11中也发生了变化)
如果您的两个代码是由最近的编译器编译的(即,对于c代码使用gcc-O2 foo.c
,对于Fortran90代码使用gfortran-O2 foo.f90
),则这两个编译器都会生成相似的内部表示(Gimple,您可以使用-fdump tree ssa
或许多其他生成数百个转储文件的-fdump
标志获得它),然后对其进行优化。因此,在这种情况下,编译器后端相同,中间端非常相似,但前端确实不同
您只需查看汇编代码(使用gcc-O2-fverbose asm
&gfortran-O2-fverbose asm
)即可发现差异
您可以使用其他选项,如-ffast math
(这使编译器能够针对标准进行优化)或-mtune=native
(这要求GCC编译器针对特定处理器进行优化)除了-O2
或-O3
优化标志…您仍然没有给出足够的明确答案。值得注意的是,在任何关于性能的问题中,当您说某个东西更快时,您应该显示您所做的实际测量以及用于编译可执行文件的命令
无论如何,可以得出一些结论
您似乎没有使用任何优化(-O
或-fast
标志)。因此,任何性能分析基本上都是毫无意义的
从您展示的源代码可以清楚地看出,您根本不比较同一件事,而是比较两种不同的算法。比较两种不同算法的速度绝对没有意义。gemm
不包含您在自己的代码中使用的简单循环,主要是为了优化ca,它要复杂得多che利用率
您在自己的C代码中使用了非常简单的方法来乘以矩阵现在比gemm
快实际上是非常令人担忧的。你确定你使用了足够大的矩阵吗?在矩阵10x10上调用gemm
是没有意义的,它们应该有一些相当大的大小。gemm
应该比足够大的矩阵的原始循环快得多。原始数字为4.2和22 GFLOPS sou如果您不为自己的函数使用任何编译器优化,那么nd是合理的
您声称您正在与Fortran进行比较。这不是真的。只有参考BLAS实现是用Fortran编写的,但它不用于实际需要快速BLAS的严重计算。您似乎正在使用的MKL不是用Fortran编写的,它是一个非常优化的汇编代码。BLAS av还有其他实现它们通常不是用Fortran编写的,而是用C或汇编语言编写的
可能有很多原因,但通常Fortran是为数学工作和Fortran编译器设计的(一开始很好)在语言存在的近60年中,它才变得更好。像这样的问题会有很多猜测,但除非你在问题中同时展示Fortran和C代码,否则没有人能给你一个准确的答案。解释你是如何测量基准的也是一个好主意。关于什么工具的详细信息s、 编译器和传递的编译器标志也很有用。请显示一些代码以便编辑您的问题。如果OP选择修改此问题,请
#include "stdio.h"
#include "time.h"
#include "sys/time.h"
#include "math.h"
#include "stdlib.h"
long long readTSC(void)
{
/* read the time stamp counter on Intel x86 chips */
union { long long complete; unsigned int part[2]; } ticks;
__asm__ ("rdtsc; mov %%eax,%0;mov %%edx,%1"
: "=mr" (ticks.part[0]),
"=mr" (ticks.part[1])
: /* no inputs */
: "eax", "edx");
return ticks.complete;
}
volatile double gtod(void)
{
static struct timeval tv;
static struct timezone tz;
gettimeofday(&tv,&tz);
return tv.tv_sec + 1.e-6*tv.tv_usec;
}
void dgemm (char *transa, char *transb, int *x, int *xa, int *xb, double *alphaa, double *ma, int *xc, double *mb, int *xd, double *betaa, double *msum, int *xe);
int main(int argc, char** argv)
{
int n = atoi(argv[1]);
long long tm;
//disabling transpose, disabling addition operation in C := alpha*op(A)*op(B) + beta*C
char trans='N';
double alpha=1.0;
double beta=0.0;
long long int p=2*n*n*n;
long double q;
double *a,*b,*sum;
double t_real,t,flop_clk,flops;
int i,j,k;
//memory allocation
a=(double*)malloc(n*n*sizeof(double));
b=(double*)malloc(n*n*sizeof(double));
sum=(double*)malloc(n*n*sizeof(double));
//Matrix Initialization
for (i=0;i<n;i++)
{
for (j=0;j<n;j++)
{
a[i+n*j]=(double)rand();
b[i+n*j]=(double)rand();
sum[i+n*j]=0.0;
}
}
//Clock cycles computation using timing2 function and t_real using timing1 function
t = gtod();
tm = readTSC();
//dgemm function call
dgemm(&trans, &trans, &n, &n, &n, &alpha, a, &n, b, &n, &beta, sum, &n);
tm = readTSC() - tm;
t_real = gtod() - t;
return 0;
}
for (i=0;i<n;i++)
{
for (k=0;k<n;k++)
{
for (j=0;j<n;j++)
{
sum [i+n*j] +=a[i+n*k]*b[k+n*j];
}
}
}