是否有关于使用 C++ 编译器进行 C 编译的结论性研究/实验?

Posted

技术标签:

【中文标题】是否有关于使用 C++ 编译器进行 C 编译的结论性研究/实验?【英文标题】:Are there conclusive studies/experiments on C compilation using a C++ compiler? 【发布时间】:2009-08-14 06:05:06 【问题描述】:

我看到很多关于使用 C++ 编译器编译的 C 代码的一般性能的争论 -- 我很好奇是否有任何可靠的实验研究隐藏在所有轶事之下您在网络搜索中发现的火焰战争。我对 GCC 套件特别感兴趣,但任何数据点都会很有趣。 (比较“Hello,World!”的程序集并没有我想要的那么健壮。:-)

我通常假设您使用“嵌入式样式”标志——没有例外或 RTTI。我也不介意知道是否有关于编译时间本身的研究。蒂亚!

【问题讨论】:

比较不同的编译器?比较同一编译器的 C 编译与 C++ 编译? (会有区别吗?)什么是“嵌入式”标志? 由相同的编译器套件(即 gcc 与 g++)。由于规范差异,有用的优化可能或多或少适用于一种语言的中介表示而不是另一种(或者某些优化可能对一种语言启用,而对另一种语言没有启用)。我提到了两个嵌入式样式标志——那些禁用异常支持的和那些禁用 RTTI 的。 如果您编译类似 C 的代码,我看不出这些标志中的任何一个是如何适用的 - 如果您只有 POD,则任何地方都没有析构函数,因此不需要异常处理代码.由于没有虚函数,因此不需要 RTTI,除了编译时解析的 typeid 调用 - 如果没有这些,则不需要。 @Pavel:也许,我只是认为删除语言运行时的组件只有在它们未被使用时才能提高性能。 我越想这个问题,我就越觉得这是一个有趣的问题。并不是说我有答案(甚至不知道有人会如何有效地测试它)。 C++ 的理念是不用为不用的东西买单——我对这个问题的解释是,编译器实际上在多大程度上达到了这个理想?我猜他们做得很好,但这与实际测量不一样。 【参考方案1】:

添加一个数据点(或至少一个轶事):

我们最近正在为一个类似嵌入式的小型目标编写一个数学库,并开始用 C 语言编写它。大约在项目进行到一半时,我们将一些文件切换到 C++,主要是为了使用模板来实现一些否则我们将编写许多几乎相同的代码片段的函数(或者在预处理器宏中嵌入 40 行函数)。

在我们开始切换时,我们非常仔细地查看了许多函数上生成的汇编代码(使用 GCC),并确认文件是否编译为 C 实际上基本相同或 C++——“本质上相同”是指不同之处在于符号名称以及汇编文件开头和结尾的内容;函数中间的实际指令完全相同。

抱歉,我没有更可靠的答案。

编辑添加,2013-03-24: 最近我看到一篇文章,其中 Rusty Russell 比较了使用 C 编译器编译和使用 C++ 编译器编译的 GCC 的性能,以回应最近的切换到将 GCC 编译为 C++:http://rusty.ozlabs.org/?p=330。结论很有趣:用 C++ 编译器编译的版本要慢得多;差异约为 0.3%。但是,这完全可以由较大的调试信息引起的加载时间差异来解释;当他剥离二进制文件并删除调试信息时,差异小于 0.1% - 即,与测量噪声基本无法区分。

【讨论】:

不用抱歉,这似乎是迄今为止最有根据的答案。【参考方案2】:

我不知道有任何临时研究,但考虑到 C++ 哲学,即您不必为不使用的功能付出代价,我怀疑用C 编译器和 C++ 编译器。

【讨论】:

对,火焰战争中大部分人都是这么说的;但是,在 lkml 上也有轶事证据表明,Linux 内核在一段时间前曾尝试使用 g++ 进行编译,但最终速度显着下降。 编译Linux内核是个特例;它们具有不适用于普通应用程序的堆栈大小和堆分配等限制。 公平地说,Linux Kernel Mailing List FAQ 似乎表明使用 g++ 编译 Linux 内核的实验是在 1992 年进行的。我认为可以安全地假设这些结果没有告诉你什么考虑到 GCC 的 17 年发展,现在可以期待了。 上次我读到 Linus 关于 C++ 的咆哮,它提到了 90 年代的一次尝试,以及它是多么糟糕,以及 C++ 设计如何一文不值。我的印象是,他对 C++ 的问题更多的是摆脱了 C++ 的“C 子集”,而不是其他任何事情,他认为他对类、析构函数、对象设计等没有兴趣。但同样,这是一个咆哮,有点像 FQA 网站,没有真正的语言知识,也没有与编译器性能相关的实际性能问题的细节。 例如这个链接:thread.gmane.org/gmane.comp.version-control.git/57643/…。您将看不到基准测试,甚至看不到有关生成的机器代码的讨论。仅关于哲学或设计。这个:linuxgazette.net/issue32/rubini.html 似乎更周到......再次,没有原始性能问题,只有语言难度和编译时间【参考方案3】:

我不知道有任何研究,我怀疑有人会花时间去做。基本上,使用 C++ 编译器进行编译时,代码的语义与使用 C 编译器编译时的语义相同,因此取决于优化和代码生成。但是 IMO 这些太多了编译器特定的,以允许任何关于 C 与 C++ 的一般性陈述。

当您使用 C++ 编译器编译 C 代码时,您主要获得的是更严格的检查(函数声明等)。 IMO 这将使使用 C++ 编译器编译 C 代码非常有吸引力。但请注意,如果您有一个从未通过 C++ 编译器运行的大型 C 代码库,那么您可能会面临一场非常陡峭的上坡战斗,直到代码编译得足够干净以能够看到任何有意义的警告。

【讨论】:

为什么使用 C++ 编译器会导致更严格的检查?或者你是那些似乎从未听说过警告标志的人之一(至少-pedantic -Wall -Wextra 用于 gcc;更完整的列表,请参见此处:***.com/questions/432835/…)? @Christoph:C++ 作为一种语言更加严格。例如,它不允许从 'void*' 到任何其他指针类型的隐式转换——您必须显式转换。我指的是这样的事情。 C++ 可能是一种更安全的语言,但您想使用 C++ 编译器来编译 C 代码!除了void * 隐式转换的(非)问题之外,与 C++ 兼容的 C 与 C99 相比并没有太大的优势——实际上,这将是一种回归,因为 C++ 不支持诸如复合文字或指定初始化程序之类的东西;我能想到的 C++ 编译器可以解决的唯一真正问题是通过(库)函数的返回值静默删除 const,但是你必须使用 stdlib 的重载 C++ 版本 - 这将导致在无法再用 C 编译器编译的代码中 如果我们说 C 可以被 C++ 编译器编译,我们说的是 C89。 C99 不是这个选项,所以我什至没有考虑它。解决了这个问题,我们回到诊断。这是一个问题,你在哪个阵营。在 C++ 阵营中,void* 通常不被接受,因为从void* 进行强制转换本质上是不安全的——因此编译器要求你明确地这样做。如果你在 C 阵营——很好。然后就不要使用 C++ 编译器。如果您喜欢 C++,但受困于 C 代码库,您可能希望使用 C++ 编译器对其进行编译。 @sbi:“更严格的检查”声明与这个问题无关,主要是诱饵。我是在专门询问性能和证据。【参考方案4】:

GCC 项目目前正处于从 C 到 C++ 的过渡阶段——也就是说,GCC 将来可能会用 C++ 实现,它目前是用 C 编写的。GCC 的下一个版本将用 C 的子集编写也是有效的 C++。

Some performance tests 在 GCC 的代码库上针对 g++gcc 执行。他们比较了“bootstrap”的时间,也就是先用sysmem编译器编译gcc,然后再用生成的编译器编译,然后反复检查,结果是一样的。

总结:使用 g++ 慢了 20%。编译器版本略有不同,但认为这不会造成 20% 的差异。

请注意,这衡量了不同的程序,gcc 与 g++,尽管它们大多使用相同的代码,但具有不同的前端。

【讨论】:

【参考方案5】:

从性能的角度来看,我没有尝试过,但我认为使用 C++ 编译器编译 C 应用程序是一个好主意,因为它会阻止你做“顽皮”的事情,例如使用未声明的函数。

但是,输出不会相同 - 至少,您会得到不同的符号,这将使其(大部分)无法与来自 C 编译器的代码链接。

所以我认为您真正的意思是“从性能的角度来看,编写非常类似于 C 的 C++ 代码并使用 C++ 编译器编译它可以吗”?

您还必须不使用一些 C99 的东西,例如 C++ 不支持的一些 C99 东西,因为它有自己的东西。

【讨论】:

我的意思是我有一个现有的 C 代码主体,我愿意对(比如从 malloc 中转换值、摆脱 stdbool 等)进行小的语法更正,以便获得它使用 C++ 编译器进行编译,我对性能增量感兴趣。 我不得不说,“试试看吧”。我不认为二进制文件在运行时会运行得更慢,它可能会更大一些并且启动时间更长。 对,所以我的问题是想知道人们是否“尝试并看到”了很多次(希望在各种应用程序上),记录结果并发布它们。 ;-) cdleary - 如果您决定“试试看”,如果您在博客上发布结果摘要,我会很感兴趣。【参考方案6】:

如果代码不是为设计而设计的,请不要这样做。如果解释为 C 或 C++,相同的有效语言结构可能会导致不同的行为。您可能会引入非常难以理解的错误。问题较少,但仍然是可维护性的噩梦;某些 C 构造(尤其是来自 C99 的)在 C++ 中无效。

【讨论】:

【参考方案7】:

过去我做过一些事情,比如查看 C++ 的二进制文件的大小,这并不意味着它们只是简单地链接到一堆未使用的库中。最简单的可能是使用 gcc -S myprog.cpp vs gcc -S myprog.c 并比较汇编器输出。

【讨论】:

生成的二进制文件的大小对其性能影响很小(如果有的话)。它的 asm 表示也没有。 asm 清楚地显示了非臃肿代码中的臃肿代码。如果 asm 显示它们相似,那么您必须执行它们才能找出答案。 当然,如果需要性能,您无论如何都不会使用 gcc @dwelch:你有证据支持这个说法吗?

以上是关于是否有关于使用 C++ 编译器进行 C 编译的结论性研究/实验?的主要内容,如果未能解决你的问题,请参考以下文章

使用makefile动态编译c++代码

C++ -malign-double 编译器标志

是否有任何理由使用 C 而不是 C++ 进行嵌入式开发? [关闭]

我是否必须在开发期间每次编译C ++代码?

如何 AOT(提前)编译 C++ 程序

关于CUDA C 项目中“ error C2059: 语法错误:“<” ”问题的解决方法