引用与指针的执行速度

时间:2008-12-18 11:05:35

标签: pointers compiler-construction reference jit vm-implementation

我最近阅读了有关托管语言是否比本机语言(特别是C#vs C ++)更慢(或更快)的讨论。一位为讨论做出贡献的人说,托管语言的JIT编译器能够对使用指针的语言中无法实现的引用进行优化。

我想知道的是,在引用而不是指针上可以进行哪种优化?

请注意,讨论的内容是执行速度,而不是内存使用情况。

5 个答案:

答案 0 :(得分:10)

在C ++中,与优化方面相关的引用有两个优点:

  1. 引用是常量(指整个生命周期内的相同变量)

    因此,编译器更容易推断哪些名称引用相同的底层变量 - 从而创建优化机会。无法保证编译器在引用方面做得更好,但它可能......

  2. 假设引用引用某些内容(没有空引用)

    可以创建“不引用任何内容”(相当于NULL指针)的引用,但这并不像创建NULL指针那么容易。因此,可以省略对NULL的引用的检查。

  3. 但是,这些优势都不会直接传递给托管语言,因此我不会在讨论主题的上下文中看到它的相关性。

答案 1 :(得分:3)

维基百科中提到的JIT compilation有一些好处:

  

JIT代码通常提供比口译员更好的性能。此外,它可以在某些或许多情况下提供比静态编译更好的性能,因为许多优化仅在运行时可行:

     
      
  1. 可以针对目标CPU和运行应用程序的操作系统模型优化编译。例如,JIT可以在检测到CPU支持时选择SSE2 CPU指令。使用静态编译器时,必须编写两个版本的代码,可能使用内联汇编。
  2.   
  3. 系统能够收集有关程序在其所处环境中实际运行的统计信息,并且可以重新排列和重新编译以获得最佳性能。但是,一些静态编译器也可以将配置文件信息作为输入。
  4.   
  5. 系统可以进行全局代码优化(例如内联库函数),而不会失去动态链接的优点,也不会产生静态编译器和链接器固有的开销。具体来说,在进行全局内联替换时,静态编译器必须插入运行时检查,并确保如果对象的实际类重写内联方法,则会发生虚拟调用。
  6.   
  7. 尽管使用静态编译的垃圾收集语言可以实现这一点,但字节码系统可以更轻松地重新安排内存以提高缓存利用率。
  8.   

我想不出与使用引用而不是指针直接相关的东西。

答案 2 :(得分:3)

一般来说,引用可以从不同的地方引用同一个对象。

'Pointer'是实现引用的机制的名称。 C ++,Pascal,C ...有指针,C ++提供了另一种称为“引用”的机制(略带其他用例),但基本上这些都是一般引用概念的实现。

因此,根据定义,引用没有理由比指针更快/更慢。

真正的区别在于使用JIT或经典的“前端”编译器:JIT可以考虑前端编译器无法使用的数据。它与“参考”概念的实现无关。

答案 3 :(得分:1)

其他答案是正确的。

我只想补充说,任何优化都不会产生差异,除非它在程序计数器实际花费很多时间的代码中,例如在不包含函数调用的紧密循环中(例如比较字符串)。

答案 4 :(得分:0)

托管框架中的对象引用与C ++中传递的引用非常不同。为了理解它们的特殊之处,想象一下如何在机器级别处理以下场景,而不使用垃圾收集对象引用:方法“Foo”返回一个字符串,该字符串存储在各种集合中并传递给不同的代码片段。一旦什么都不需要字符串,应该可以回收用于存储它的所有内存,但是不清楚哪一段代码将是最后一个使用该字符串的代码。

在非GC系统中,每个集合都需要拥有自己的字符串副本,或者需要保存包含指向共享对象的指针的东西,该共享对象保存字符串中的字符。在后一种情况下,共享对象需要以某种方式知道何时消除它的最后一个指针。有多种方法可以处理,但所有这些方法的基本共同点是,当复制或销毁指向它们的指针时,需要通知共享对象。这种通知需要工作。

相比之下,在GC系统中,程序使用元数据进行修饰,以说明在任何给定时间将使用哪些寄存器或堆栈帧的一部分来保存有根对象引用。当垃圾收集周期发生时,垃圾收集器必须解析这些数据,识别并保存所有活动对象,并核对其他所有内容。然而,在所有其他时间,处理器可以以其喜欢的任何模式或顺序复制,替换,混洗或销毁引用,而不必通知任何涉及的对象。请注意,在多处理器系统中使用指针使用通知时,如果不同的线程可能复制或销毁对同一对象的引用,则需要使用同步代码来使必要的通知成为线程安全的。相比之下,在GC系统中,每个处理器可以随时更改参考变量,而无需将其操作与任何其他处理器同步。