在 Fortran 中,如果不将子例程或函数明确声明为recursive
. 一位 Fortran 程序员告诉我,正因为如此,编译器可以将静态存储分配给所有局部变量,从而提高程序的速度。我对这种说法感到非常惊讶,因为当今大多数处理器都针对快速引用堆栈进行了优化。我认为,从静态地址加载的局部变量可能会导致很多缓存未命中,因为与堆栈相反,其他子例程不使用静态地址。
静态地址的局部变量真的有加速吗?还有哪些其他优化可能禁止递归子例程和函数?
在 Fortran 中,如果不将子例程或函数明确声明为recursive
. 一位 Fortran 程序员告诉我,正因为如此,编译器可以将静态存储分配给所有局部变量,从而提高程序的速度。我对这种说法感到非常惊讶,因为当今大多数处理器都针对快速引用堆栈进行了优化。我认为,从静态地址加载的局部变量可能会导致很多缓存未命中,因为与堆栈相反,其他子例程不使用静态地址。
静态地址的局部变量真的有加速吗?还有哪些其他优化可能禁止递归子例程和函数?
您咨询的 Fortran 程序员有一些倒退 - 出现了对递归的限制,因为以前(假设的)编译器只能为任何变量分配静态存储。性能主要是一个无关紧要的考虑因素——尽管我想如果你根本不能做某事,那么你不太可能很快做到。
早期的 Fortran(F77 和更早版本)旨在允许整个程序的内存需求在程序运行之前由 fortran 处理器静态确定。这适合那个时代的一些有限的机器架构。在“必须能够通过静态分析计算总内存”限制的一般情况下,很难让递归(内存需求可能因程序输入而异)之类的东西工作 - 因此语言不允许这样做。
具体处理器如何实际实现语言取决于他们——如果他们想使用基于堆栈的 stoge 来处理局部变量,那么他们可以。在标准边界之外编写的 Fortran 程序员在历史上可能已经习惯于期望您从使用静态存储中获得的行为(非保存的变量在调用之间记住它们的值等),但是对底层实现敏感的程序不是符合标准。
(具有此限制的架构已被 F90 淘汰。该标准引入了几种方法,程序内存需求可以根据程序输入动态变化 - 显而易见的一种是 allocate 语句,但现在也允许使用自动变量。)
对于小的未保存的局部变量(标量),相关存储在堆栈上的速度很可能(假设硬件合理)更快。
区分能够递归的过程和那些不能递归的过程在今天仍有一些实际价值——如果局部变量很大(数组或长字符),那么它可能根本不适合堆栈。为了避免这种情况——如果一个过程没有被标记为递归并且数组的大小是已知的,那么 fortran 处理器可以使用静态存储。这避免了与动态内存分配相关的任何开销,这可能相当昂贵。如果过程被标记为递归(并且局部变量未保存)或数组的大小在编译时未知,那么 fortran 处理器别无选择 - 它要么希望数据适合堆栈,要么使用动态内存分配。
这只是因为某些大型机 cpu 根本没有堆栈(例如 s/360)。编译器必须生成特殊代码来模拟它。因此,非递归函数确实包含更简单/更少的代码。我不知道这在今天是否仍然适用。
引用特定地址而不是堆栈上的位置并不意味着更多的缓存未命中。相反,静态地址简化了优化器的工作,因为它可以根据它们的使用情况将它们放在内存中。