在C ++代码中使用纯C库是否会导致性能下降/损失?

我看到了这个链接,但我不是要求使用“extern”的代码性能下降。 我的意思是没有“extern”,在C ++中使用C库时是否存在“上下文切换”? 在C ++应用程序中使用纯C(非类包装)函数时是否有任何问题?

C和C ++都是编程语言规范 (用英语编写,参见例如n1570,用于C11的规范)并且不谈性能(但是关于程序的行为,即关于语义 )。

但是,您可能会使用诸如GCC或Clang之类的编译器,这些编译器不会带来任何性能损失,因为它为C和C ++语言构建了相同类型的中间内部表示(例如GCC的GIMPLE和Clang的LLVM)。 ,因为C和C ++代码使用兼容的ABI和调用约定 。

在实践中, extern "C"不会更改任何调用约定但禁用名称修改 。 但是,它对编译器的确切影响是特定于该编译器的。 它可能(或不)禁用内联 (但考虑-flto用于GCC中的链接时优化)。

某些C编译器(例如tinycc )会生成性能较差的代码。 甚至GCC或Clang ,当与-O0或没有明确地启用优化时 (例如,通过传递 -O1-O2等…)可能会产生慢速代码(默认情况下,优化禁用)。

BTW,C ++旨在与C互操作(强大的约束解释了C ++的大多数缺陷)。

在某些情况下,真正的C ++代码可能比相应的真正C代码快。 例如,要对数字数组进行排序,您将在正版C ++中使用std :: array和std :: sort ,并且排序中的比较操作可能会被内联。 使用C代码,您只需使用qsort ,每次比较都会通过间接函数调用(因为编译器不会内联qsort ,即使理论上它可以……)。

在其他一些情况下,真正的C ++代码可能会稍微慢一点; 例如, ::operator new几个(但不是全部)实现只是调用malloc (然后检查失败)但是没有内联。

实际上,从C ++代码调用C代码或从C代码调用C ++代码没有任何代价,因为调用约定是兼容的。

C longjmp工具可能比抛出C ++exception更快,但它们没有相同的语义(请参阅堆栈展开 ),而longjmp并不能很好地融合C ++代码。

如果你非常关心性能,那么在你的代码和基准测试中写两次(在真正的C和真正的C ++中)。 您可能会观察到C和C ++之间的微小变化(最多几个百分点),所以我根本不会打扰 (而且您的性能问题实际上是不合理的)。


上下文切换是与操作系统和多任务相关的概念,发生在运行抢占期间可执行的机器代码的 进程上。 如何获取该可执行文件 (来自C编译器,来自C ++编译器,来自Go编译器,来自SBCL编译器,或者是其他语言(如Perl或字节码Python)的解释器)完全无关紧要(因为上下文切换可能发生)在任何机器指令,在中断期间)。 阅读一些书籍,如操作系统:三个Eeasy Pieces

在基本级别, ,从C ++代码调用C库时,您不会看到任何类型的“切换”性能损失。 例如,从C ++调用另一个转换单元中定义的C方法应该具有与在另一个转换单元中调用C ++(以相同的C类方式)实现的相同方法大致相同的性能。

这是因为C和C ++编译器的常见实现最终将源代码编译为本机代码,并且使用与C ++调用可能发生的相同类型的call有效地支持调用extern "C"函数。 调用约定通常基于平台ABI,并且在任何一种情况下都是类似的。

除了基本事实之外,在调用C函数时可能仍然存在一些性能缺点,而不是在C ++中实现相同的函数:

  • 在C中实现的函数和声明的extern "C"以及从C ++代码调用的函数通常不会被内联(因为根据定义它们没有在头文件中实现),这会抑制整个主机可能非常强大的优化0
  • C代码中使用的大多数数据类型都不能被C代码直接使用,例如,如果你的C ++代码中有一个std::string ,你需要选择一个不同的类型将它传递给C代码 – char *很常见,但丢失了有关显式长度的信息,这可能比C ++解决方案慢。 许多类型没有直接的C等价物,因此您可能会遇到代价高昂的转换。
  • C代码使用mallocfree进行动态内存管理,而C ++代码通常使用newdelete (并且通常更喜欢尽可能隐藏其他类后面的调用)。 如果您需要以一种语言分配内存以便在其他语言中释放,这可能会导致不匹配,您需要回拨“其他”语言来执行免费,或者可能不必要的副本等。
  • C代码经常大量使用C标准库例程,而C ++代码通常使用C ++标准库中的方法。 由于存在大量function重叠,因此使用更多C库方法2 ,C和C ++的混合可能比纯C ++代码具有更大的代码占用量。

上述问题仅适用于纯C ++实现与C语言对比,并不意味着在调用C时性能会下降:它实际上是在回答“为什么可以在C和C的混合中编写应用程序”的问题。 C ++比纯C ++慢?“ 此外,上述问题主要是对上述开销可能很大的非常短的呼叫的关注。 如果你在C中调用一个冗长的函数,那就不是问题了。 “数据类型不匹配”可能仍会让你感到困惑,但这可以在C ++方面进行设计。


0有趣的是,链接时优化实际上允许在C ++代码中内联 C方法,这是LTO的一个鲜明的好处。 当然,这通常取决于使用适当的LTO选项从源代码构建C库。

1例如,除标准布局类型之外的其他任何东西。

2许多C ++标准库调用最终委托给C库例程进行“重”提升,例如std::copy如何在可能的情况下调用memcpymemset以及大多数new实现最终如何调用malloc这至少部分地减轻了这一点。 。

C ++从一开始就发展和变化很多,但是通过设计,它与C语言向后兼容.C ++编译器通常是用C编译器构建的,但是通过链接时优化更加现代化。 我认为很多软件可以在用户空间和使用的库中可靠地混合使用C和C ++代码。 我最近回答了一个问题 ,涉及将C ++类成员函数指针传递给C实现的库函数。 海报说这对他有用。 因此,C ++与C语言的兼容性可能超过任何程序员或用户的想法。

但是,C ++在许多不同的范例中工作,因为它是面向对象的,并且实现了整个范围的抽象,新数据类型和运算符。 某些数据类型很容易翻译( char * C string到std::string ),而其他数据类型则不然。 有关C ++编译器选项的GNU.org上的这一部分可能会引起一些兴趣。

混合这两种语言时,我不会太担心或担心性能下降。 最终用户,甚至程序员,几乎都不会注意到性能的任何可测量的变化,除非他们处理大量的数据抽象。