我最近看到了一篇关于托管语言与本地语言(特别是C#与C++)哪个更快的讨论。其中一个参与讨论的人说,托管语言的JIT编译器能够对引用进行优化,这在使用指针的语言中是不可能实现的。
我想知道的是,有哪些引用上的优化是指针不能实现的?
请注意,讨论的重点是执行速度,而不是内存使用。
我想知道的是,有哪些引用上的优化是指针不能实现的?
请注意,讨论的重点是执行速度,而不是内存使用。
在C++中,与优化相关的引用具有两个优点:
引用是常量(在其整个生命周期内都指向同一变量)
由于这一点,编译器更容易推断哪些名称引用相同的底层变量,从而创造优化机会。不能保证编译器使用引用能够做得更好,但它可能会......
引用被认为是指向某个东西的(不存在空引用)
可以创建“不指向任何内容”的引用(相当于NULL指针),但这不像创建NULL指针那样容易。由于这一点,可以省略对引用是否为空的检查。
然而,这些优点都没有直接转移到托管语言中,因此我不认为这在你的讨论主题上有什么相关性。
JIT代码通常比解释器具有更好的性能。此外,它在某些或许大多数情况下可以比静态编译提供更好的性能,因为许多优化只有在运行时才可行:
- 编译器可以根据应用程序运行的操作系统模型和目标CPU进行优化。例如,当JIT检测到CPU支持SSE2指令时,它可以选择使用这些指令。而静态编译器则必须编写两个版本的代码,可能需要使用内联汇编。
- 系统能够收集关于程序在其所处环境中实际运行方式的统计信息,并且可以重新排列和重新编译以获得最佳性能。但是,一些静态编译器也可以将配置文件信息作为输入。
- 系统可以进行全局代码优化(例如库函数的内联),而不会失去动态链接的优势并避免静态编译器和链接器固有的开销。具体来说,在进行全局内联替换时,静态编译器必须插入运行时检查,并确保如果对象的实际类覆盖了内联方法,则会发生虚拟调用。
- 尽管在静态编译垃圾回收语言时也可能发生,但字节码系统可以更轻松地重新排列内存以获得更好的缓存利用率。
我想不到与使用引用而不是指针直接相关的内容。
其他答案都是正确的。
我只想补充一点,除非优化的代码在程序计数器实际花费大量时间的地方,否则任何优化都不会有什么区别,比如在不包含函数调用的紧密循环中(例如比较字符串)。
在托管框架中,对象引用与C++中的传递引用非常不同。要了解它们的特殊之处,想象一下以下情景在机器级别上是如何处理的,没有垃圾回收对象引用:方法"Foo"返回一个字符串,并将其存储到各种集合中并传递给不同的代码片段。一旦没有任何东西再需要这个字符串,就应该能够回收用于存储它的所有内存,但是不清楚哪个代码片段会成为最后一个使用该字符串的片段。
在非垃圾回收系统中,每个集合都需要拥有字符串的自己副本,或者需要持有一个包含指向共享对象的指针的东西,该共享对象保存字符串中的字符。在后一种情况下,共享对象需要以某种方式知道何时消除对它的最后一个指针。处理这种情况的方法有多种,但是所有方法的一个关键共同点是共享对象需要在复制或销毁指向它们的指针时得到通知。这样的通知需要进行工作。
在GC系统中,程序会被装饰上元数据,以表明哪些寄存器或堆栈帧的部分将在任何给定时间用于保存根对象引用。当进行垃圾回收周期时,垃圾收集器将需要解析这些数据,识别和保留所有活动对象,并清除其他所有对象。然而,在其他所有时间,处理器可以按任何模式或顺序复制、替换、洗牌或销毁引用,而无需通知任何涉及的对象。请注意,在多处理器系统中使用指针使用通知时,如果不同的线程可能会复制或销毁对同一对象的引用,则需要同步代码来使必要的通知线程安全。相比之下,在GC系统中,每个处理器都可以随时更改引用变量,而无需将其操作与任何其他处理器同步。