【问题标题】:Execution speed of references vs pointers引用与指针的执行速度
【发布时间】:2015-06-14 16:26:50
【问题描述】:

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

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

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

【问题讨论】:

  • 好吧,如果你没有正确管理内存,那肯定会影响执行速度。

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


【解决方案1】:

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

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

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

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

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

但是,这些优势都没有直接体现在托管语言上,所以我看不出这与您的讨论主题的相关性。

【讨论】:

    【解决方案2】:

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

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

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

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

    【讨论】:

    • 一个具体的大型项目证明是使用 IronPython 与 CPython 进行的。 IronPython 的最新版本在每个基准测试中都以或多或少的优势击败了 CPython,同时保持 100% 的兼容性。这是对 CPython 和一个年轻的 IronPython 项目进行了几年的优化。
    • 您还没有回答这个问题...“我想知道的是,在引用而不是指针上可以进行哪些优化?”...
    • 我认为问题的重点可能是基于关于在实际实践中如何使用引用和指针的假设(您是对特定对象的引用吗?您的指针仅仅是无效的吗?)相反他们工作方式固有的任何东西。
    【解决方案3】:

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

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

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

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

    【讨论】:

      【解决方案4】:

      其他答案都是对的。

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

      【讨论】:

        【解决方案5】:

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

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

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

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2011-04-28
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-10-18
          • 2018-02-04
          • 2014-10-28
          相关资源
          最近更新 更多