WEB开发网
开发学院软件开发VC 优化增强您的Visual C++应用程序 阅读

优化增强您的Visual C++应用程序

 2010-07-01 20:43:10 来源:WEB开发网   
核心提示:全程序优化Visual C++ .NET 对非托管代码添加了 WPO,而在 Visual C++ 2005 中,优化增强您的Visual C++应用程序(2),这个功能扩展到了托管代码,它不是一次编译和优化一个源文件,有一个很好的小规则,那就是记住,而是一次跨所有源文件和头文件进行编译和优化,现在编译器可以跨多个源文件

全程序优化

Visual C++ .NET 对非托管代码添加了 WPO。而在 Visual C++ 2005 中,这个功能扩展到了托管代码。它不是一次编译和优化一个源文件,而是一次跨所有源文件和头文件进行编译和优化。

现在编译器可以跨多个源文件执行分析和优化。例如,如果没有 WPO,编译器只能在单个编译域中内联函数。有了 WPO,编译器就可以从程序中的所有源文件内联函数。

在以下的示例中,编译器可以做的事情包括跨编译器内联和常量传递,以及其他类型的过程间优化:

以下是引用片段:
   // Main.cpp
  ...
  MSDNClass ^MSDNObj = gcnew MSDNClass;
  int x = MSDNObj->Square(42);
  return x;
  ...
  // MSDNClass.cpp
  int MSDNClass::Square(int x)
  {
  return x*x;
  }

在这个示例中,Main.cpp 调用 Square 方法,而这个方法是另一个源文件中的 MSDNClass 的一部分。当编译时进行 /O2 优化,而不进行全程序优化时,Main.cpp 中产生的 MSIL 如下所示:

ldc.i4.s 42

call instance int32 MSDNClass::Square(int32)

您可以看到,它首先将值 42 加载到堆栈中,然后调用 Square 函数。作为对照,对于相同的程序,当编译时打开全程序优化时,则生成的 MSIL 如下所示:

ldc.i4 0x6e4

它没有加载 42,也没有调用 Square 函数。相反,在全程序优化下,编译器可以内联来自 MSDNClass.cpp 的函数并进行常量传递。最终的结果只是一条简单的指令 — 加载 42*42 的结果,十六进制表示为 0x6e4。

虽然 Visual C++ 编译器执行的一些分析和优化在理论上 JIT 编译器也可以执行,但对 JIT 编译器的时间限制使得这里提到的许多优化当前还无法实现。一般情况下,NGEN 会比 JIT 编译器更早实现这些类型的优化,因为 NGEN 没有 JIT 编译器必须面对的这类响应时间限制。

64 位 NGEN 优化

出于本文需要,我将 JIT 和 NGEN 统称为 JIT。对于 32 位版本的 CLR,JIT 编译器和 NGEN 执行的优化相同。但 64 位版本的却不是这样,在 64 位版本中,NGEN 比 JIT 所进行的优化明显更多。

64 位的 NGEN 利用了这样的事实:它可以比 JIT 花费更多的时间进行编译,因为 JIT 的吞吐量直接影响应用程序的响应时间。我在本文特别提到了 64 位的 NGEN,因为它针对 C++ 风格的代码进行相对地微调,它进行的一些优化(例如双 Thunk 消除优化)对 C++ 起到很大的帮助,这些优化是其他 JIT 和 NEGN 所不具备的。32 位 JIT 和 64 位 JIT 分别是 Microsoft 中两个不同团队使用两种不同的代码基实现的。32 位 JIT 是由 CLR 团队开发的,而 64 位 JIT 是由 Visual C++ 团队开发的,而且基于 Visual C++ 代码基。因为 64 位 JIT 是由 C++ 团队开发的,所以它更加注重与 C++ 相关的问题。

双 Thunk 消除

64 位 NGEN 执行的最重要的优化之一就是所谓的双 thunk 消除。这个优化在带 /clr 开关编译的 C++ 代码中通过函数指针或虚拟调用解决了一个转换,这个转换发生在通过托管代码调用托管入口点的时候。(在 /clr:pure 或 /clr:safe 编译代码中不会发生这种转换。)发生这个转换是因为在 callsite 上函数指针和虚拟调用都没有足够的信息可以确定它们调用的是托管入口点 (MEP) 还是非托管入口点 (UEP)。

为了向后兼容,始终选择 UEP。但如果托管 callsite 实际调用的是托管方法呢?在这种情况下,除了初始 thunk 从托管 callsite 进入 UEP 外,还会有一个 thunk 从 UEP 进入目标托管方法。这个托管-托管 thunk 过程通常称为双 thunk。

64 位 NGEN 实现了对“从非托管到托管”的调用(即为反过来对托管代码 thunk 的调用),从而实现了优化。可以进行一个检查来确定是否是这种情况;如果是,它就会跳过这两个 thunk,并直接跳到托管代码,如图 2 所示。这样可以节省许多指令,在实际的代码建模基准中,我发现有 5-10% 的提高(在人为测试中,可以看到超过 100% 的性能提高)。

双 Thunk 消除

图 2 双 Thunk 消除

不过有一点需要注意,那就是这个优化只有在位于默认应用程序域时才生效。有一个很好的小规则,那就是记住,默认 AppDomain 通常会获得更好的性能。

上一页  1 2 3 4 5  下一页

Tags:优化 Visual

编辑录入:爽爽 [复制链接] [打 印]
赞助商链接