最近,我开始致力于优化C++代码,因此开始与编译器浏览器一起玩。由于我主要使用Visual Studio在Windows上进行开发,因此我使用了msvc编译器。

在某些时候,msvc失控了。经过一些摆弄之后,我可以将其缩小到iostream header ,这应该是I/O(SL.io.3)的首选。

#include <iostream>
int main() {
    std::cout << "Hello World!\n";
    return 0;
}

虽然gcc或clang的总输出(main +调用一些ios_base init函数的静态初始化器)总计约20行汇编(在Godbolt编译器浏览器过滤掉指令和注释之后)。
MSVC explodes it转换为4000。 MSVC的main本身的定义是7条指令,而gcc/clang是8条指令。 (使用GNU/Linux libstdc++的gcc/clang将额外的长度arg传递给cout运算符重载函数,而不仅仅是2个指针,如MSVC在使用其自己的C++库时那样。)

如果我改用puts之类的东西,则MSVC的总输出相当紧凑,可以与gcc/clang相媲美,例如here

有人可以向我解释一下这里发生了什么,我做错了什么,或者指出正确的方向吗?

为什么对于使用C++库的简单功能而言,MSVC asm列表如此so肿?

最佳答案

这可能不是一个完整的答案,但是我认为我可以解释很多差异。

许多标准库(例如iostreams)都是模板繁重的代码。我相信Microsoft编译器会生成更多模板实例,并依靠链接程序删除不必要的实例。我认为这是Windows链接器使用的策略与大多数Posix策略不同的结果,但这也可能是由于简单地使用了不同的标准库实现而导致的。

如果指定/MD,它告诉编译器您打算使用标准库的DLL版本,则生成的代码将从4000多行减少到少于500行。我不确切知道为什么会这样。也许MSVC知道DLL库具有所有必需的模板实例化,而静态库则依赖于编译器的模板实例化。

您可以通过仅处理C++异常(使用/EHs)来引起增量改进。默认情况下,编译器还将生成处理异步系统异常的代码。尽管您的hello-world示例未明确使用异常,但标准库的某些部分可能会使用异常。在这一点上,似乎有很多其他行正在设置堆栈展开表和调用析构函数。

MSVC版本中许多剩余的多余部分看起来像是存在的,它们在调用析构函数时会释放堆栈,因此异常处理模型可能会有所不同。

我以为Compiler Explorer过去有一个“clang-cl”选项,但现在看不到。一般来说,clang-cl是一个命令驱动程序,可解释cl.exe选项并调整默认选项,以使clang生成与Microsoft代码兼容的二进制ABI的代码。有趣的是,它是否会生成类似于常规clang的代码,或者最终是否会像MSVC那样发出代码。

07-24 09:45
查看更多