10

我最近阅读了关于托管语言是否比本地语言(特别是 C# 与 C++)慢(或快)的讨论。一位参与讨论的人说,托管语言的 JIT 编译器将能够对引用进行优化,这在使用指针的语言中是不可能的。

我想知道的是在引用而不是指针上可以进行什么样的优化?

请注意,讨论是关于执行速度,而不是内存使用。

4

5 回答 5

10

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

  1. 引用是常量(在其整个生命周期内引用同一个变量)

    正因为如此,编译器更容易推断出哪些名称引用了相同的底层变量——从而创造了优化机会。不能保证编译器在引用方面会做得更好,但它可能......

  2. 假定引用指向某物(没有空引用)

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

但是,这些优势都没有直接延续到托管语言,所以我看不出在您的讨论主题的上下文中的相关性。

于 2008-12-18T11:52:41.120 回答
3

Wikipedia 中提到了JIT 编译的一些好处:

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

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

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

于 2008-12-18T11:20:28.487 回答
3

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

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

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

真正的区别在于使用 JIT 或经典的“预先”编译器:JIT 可以将数据考虑在内,这些数据不适用于预先编译器。它与“参考”概念的实现无关。

于 2008-12-18T12:39:18.797 回答
1

其他答案是对的。

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

于 2008-12-18T12:48:15.813 回答
0

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

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

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

于 2013-07-15T22:21:13.217 回答