当我在大学学习时,我经常听到这样的想法:对于同等程序,Fortran编译器生成的代码比C编译器更快。
关键原因是这样的:Fortran编译器每行代码平均发出1,1个处理器指令,而C编译器每行代码平均发出1,6处理器指令 -我不记得确切的数字了,但是想法是C编译器发出明显更多的机器代码,因此产生较慢的程序。
这样的比较有效吗?我们可以说Fortran编译器比C编译器生成更快的程序,反之亦然,为什么存在这种差异?
当我在大学学习时,我经常听到这样的想法:对于同等程序,Fortran编译器生成的代码比C编译器更快。
关键原因是这样的:Fortran编译器每行代码平均发出1,1个处理器指令,而C编译器每行代码平均发出1,6处理器指令 -我不记得确切的数字了,但是想法是C编译器发出明显更多的机器代码,因此产生较慢的程序。
这样的比较有效吗?我们可以说Fortran编译器比C编译器生成更快的程序,反之亦然,为什么存在这种差异?
Answers:
IIRC之所以说Fortran更快的主要原因之一是缺少指针别名,因此它们可以使用C编译器不能使用的优化:
在FORTRAN中,函数参数可能不会互为别名,并且编译器会假定它们没有别名。这可以实现出色的优化,这也是FORTRAN以快速语言而闻名的主要原因之一。(请注意,在FORTRAN函数中仍可能发生别名。例如,如果A是一个数组,而i和j是碰巧具有相同值的索引,则A [i]和A [j]是两个不同的名称幸运的是,由于基本数组必须具有相同的名称,因此可以进行索引分析以确定A [i]和A [j]不能别名的情况。
但我在这里与其他人相同:比较为一行代码生成的汇编程序指令的平均数目是完全无稽之谈。例如,现代x86内核如果不访问相同的寄存器,则可以并行执行两条指令。因此,从理论上讲,只需对指令重新排序,就可以使相同指令集的性能提高100%。好的编译器通常还会生成更多的汇编指令以获取更快的代码(请考虑展开循环,内联)。汇编指令的总数很少说明一段代码的性能。
restrict
关键字允许函数的作者指定指针没有别名。这是否足以解决差异,还是还有更多呢?
Dan是正确的,较长的程序并不意味着较慢的程序。这很大程度上取决于他们在做什么。
我不是Fortran的专家,我知道一点。比较它们,我认为写得很好的C在性能上比Fortran更好,并且具有更复杂的数据结构和功能。如果我在这里错了,请有人(请)纠正我,但我确实认为Fortran的水平要比C低。
乍一看,我想你是在问编译器是否更快。实际上,我确实认为,Fortran在使用相似数量的代码时通常会编译得更快,但是生成的程序及其运行方式将是另一回事。解析起来更简单。
该声明可能在C处于起步阶段的过去(大约70年代后期)是正确的,并且Fortran得到了所有主要制造商的支持并进行了高度优化。早期的Fortrans基于IBM体系结构,因此诸如算术之类的简单东西肯定会是每个汇编指令一个语句。对于像Data General和Prime这样的较旧机器来说,这是正确的,它们具有3种跳跃方式。这不适用于没有三向跳转的现代指令集。
代码行不等于代码声明。早期版本的Fortran仅允许每行一个语句。更高版本的Fortran可以每行使用多个语句。C每行可以有多个语句。在像英特尔的IVF(以前称为CVF,MS Powerstation)和英特尔的C之类的速度更快的生产编译器上,两者之间确实没有区别。这些编译器经过高度优化。
旧式FORTRAN要求程序员想要将数组的一部分提供给函数,以便将引用传递给整个数组,以及一个或多个整数值,这些整数值指定开始下标和结束下标或项数。C可以简化此过程,只需将指针传递到与元素数量一起感兴趣部分。直接来说,这会使事情变得更快(通过两件事而不是三件事)。但是,通过限制编译器可以执行的优化类型,它可能间接地减慢了运行速度。
考虑以下功能:
void diff(float dest[], float src1[], float src2[], int n)
{
for (int i=0; i<n; i++)
dest[i] = src1[i] - src2[i];
}
如果编译器知道每个指针都将标识数组的开始,则它可以生成将并行或以任何顺序作用于数组元素的代码,因为对于任何x!= y,对dest [x ]不会影响src1 [y]或src2 [y]。例如,在某些系统上,编译器可能会受益于生成等效于以下代码的代码:
void dif(float dest[], float src1[], float src2[], int n)
{
int i=0;
float t1a,t1b,t2a,t2b,tsa,tsb;
if (n > 2)
{
n-=4;
t1a = src1[n+3]; t1b = src2[n+3]; t1b=src2[n+2]; t2b = src2[n+2];
do
{
tsa = t1a-t2a;
t1a = src1[n+1]; t2a = src2[n+1];
tsb = t2b-t2b;
dest[n+3] = tsa;
t1b = src1[n]; t2b = src2[n];
n-=2;
dest[n+4] = tsb;
} while(n >= 0);
... add some extra code to handle cleanup
}
else
... add some extra code to handle small values of n
}
请注意,每个加载或计算值的操作在它与使用该值的下一个操作之间至少还有一个操作。当满足这样的条件时,某些处理器可能会使不同操作的处理重叠,从而提高性能。但是请注意,由于C编译器无法知道不会将代码传递给指向公共数组的部分重叠区域的指针,因此C编译器无法进行上述转换。但是,给定等效代码的FORTRAN编译器可以并且确实进行了这种转换。
尽管C程序员可以尝试通过显式编写出展开循环并使相邻遍历的操作重叠的代码来获得可比的性能,但是如果这样的代码使用了太多的自动变量,则编译器不得不“溢出”它们,从而很容易降低性能。记忆。FORTRAN编译器的优化器可能比程序员更了解在给定情况下哪种形式的交织会产生最佳性能,并且通常最好由此类编译器来决定。尽管C99尝试通过添加restrict
限定符来改善C的情况,但只有在与和dest[]
分开的数组或程序员添加单独的循环版本来处理所有的情况下,才能在此处使用C99src1[]
src2[]
dest
是不相交的src1
和src2
,其中src1[]
并且dest
相等src2
且不相交,其中src2[]
和dest[]
相等src1
且不相交,并且所有三个数组均相等。相比之下,FORTRAN可以使用相同的源代码和相同的机器代码轻松处理所有四种情况。