在 C++ 代码中使用纯 C 库是否会导致性能下降/损失?
Is there a performance degradation / penalty in using pure C library in C++ code?
我看到了这个 link,但我并不是要求使用 "extern" 的代码性能下降。我的意思是没有 "extern",在 C++ 中使用 C 库时是否有 "context switching"?
Are there any problems when using pure C (not class-wrapped) functions in C++ application?
C和C++都是编程语言规范(用英文写的,见例n1570 for the specification of C11) and do not speak about performance (but about behavior of the program, i.e. about semantics)
但是,您可能会使用 GCC or Clang which don't bring any performance penalty, because it builds the same kind of intermediate internal representation (e.g. GIMPLE for GCC, and LLVM for Clang) for both C and C++ languages, and because C and C++ code use compatible ABIs and calling conventions.
等编译器
实际上extern "C"
不改变任何调用约定但禁用name mangling. However, its exact influence on the compiler is specific to that compiler. It might (or not) disable inlining(但考虑-flto
用于GCC中的link-时间优化)。
一些 C 编译器(例如 tinycc) produce code with poor performance. Even GCC or Clang, when used with -O0
or without explicitly enabling optimization (e.g. by passing -O1
或 -O2
等...)可能会产生缓慢的代码(默认情况下禁用优化)。
顺便说一句,C++ 被设计为可与 C 互操作(这种强烈的约束解释了 C++ 的大部分缺陷)。
在某些情况下,正版 C++ 代码可能 比对应的正版 C 代码 稍快。例如,要对数字数组进行排序,您将使用 std::array and std::sort in genuine C++, and the compare operations in the sort are likely to get inlined. With C code, you'll just use qsort 并且每个比较都通过间接函数调用(因为编译器没有内联 qsort
,即使理论上它可以...... ).
在其他一些情况下,真正的 C++ 代码可能稍微慢;例如,::operator new
的几个(但不是全部)实现只是简单地调用 malloc
(然后检查失败)但没有内联。
实际上,从 C++ 代码调用 C 代码或从 C 代码调用 C++ 代码没有任何惩罚,因为调用约定是兼容的。
C longjmp facility is probably faster than throwing C++ exceptions, but they don't have the same semantics (see stack unwinding) 和 longjmp
在 C++ 代码中不能很好地混合。
如果您非常关心性能,请编写(用真正的 C 和真正的 C++)两倍的代码和基准测试。您可能会观察到 C 和 C++ 之间的微小变化(最多几个百分点),因此 我根本不会打扰(您的性能问题实际上是不合理的)。
Context switch is a concept related to operating system and multitasking and happens on processes running machine code executable during preemption. How that executable is obtained (from a C compiler, from a C++ compiler, from a Go compiler, from an SBCL compiler, or being an interpreter of some other language like Perl or bytecode Python) is totally irrelevant (since a context switch can happen at any machine instruction, during interrupts). Read some books like Operating Systems: Three Eeasy Pieces。
在基本层面上,没有,当从 C++ 代码调用 C 库时,您不会看到任何类型的 "switching" 性能损失。例如,从 C++ 调用另一个翻译单元中定义的 C 方法应该具有与在另一个翻译单元中调用 C++ 中实现的相同方法(以相同的 C-like 方式)大致相同的性能。
这是因为 C 和 C++ 编译器的常见实现最终将源代码编译为本机代码,并且使用可能发生的相同类型的 call
有效地支持调用 extern "C"
函数一个 C++ 调用。调用约定通常基于平台 ABI,并且在任何一种情况下都是相似的。
抛开这个基本事实,与在 C++ 中实现相同的函数相比,调用 C 函数时可能仍然存在一些性能缺陷:
- 在 C 中实现并声明
extern "C"
并从 C++ 代码调用的函数通常不会被内联(因为根据定义它们不会在 header 中实现),这会抑制整个主机可能非常强大的优化0.
- C++ 代码中使用的大多数数据类型1 不能直接被 C 代码使用,例如,如果您的 C++ 代码中有一个
std::string
,您需要选择一种不同的类型将其传递给 C 代码 - char *
很常见,但会丢失有关显式长度的信息,这可能比 C++ 解决方案慢。许多类型没有直接的 C 等价物,因此您可能需要进行代价高昂的转换。
- C 代码使用
malloc
和 free
进行动态内存管理,而 C++ 代码通常使用 new
和 delete
(并且通常更喜欢将这些调用隐藏在其他调用之后类 尽可能)。如果您需要以一种语言分配内存,而该内存将以另一种语言释放,这可能会导致不匹配,您需要回调 "other" 语言来进行释放,或者可能是不必要的复制等。
- C 代码经常大量使用 C 标准库例程,而 C++ 代码通常使用 C++ 标准库中的方法。由于有很多功能重叠,C 和 C++ 的混合可能比纯 C++ 代码具有更大的代码占用空间,因为使用了更多的 C 库方法2.
上述问题仅在将纯 C++ 实现与 C 实现进行对比时适用,并不真正意味着调用 C 时性能会下降:它实际上是在回答问题 "Why could writing an application in a mix of C and C++ be slower than pure C++?"。此外,上述问题主要是非常短的呼叫所关心的问题,其中上述开销可能很大。如果您在 C 中调用冗长的函数,则问题不大。 "data type mismatch" 可能仍然会咬你,但这可以在 C++ 端设计。
0 有趣的是,link-time 优化实际上允许 C 方法是 inlined in C++ code,这是 LTO 的 little-mentioned 好处。当然,这通常取决于使用适当的 LTO 选项自己从源代码构建 C 库。
1 例如,标准布局类型以外的几乎所有内容。
2 许多 C++ 标准库调用最终委托给 C 库例程以进行 "heavy" 提升这一事实至少部分缓解了这种情况,例如如何 std::copy
尽可能调用 memcpy
或 memset
以及大多数 new
实现最终如何调用 malloc
.
C++ 自诞生以来已经发展和改变了很多,但在设计上它与 C 向后兼容。C++ 编译器通常是从 C 编译器构建的,但更现代化的 link-time optimizations. I would imagine lots of software can reliably mix C and C++ code, both in the user spaces and in the libraries used. 涉及传递一个C++ class 成员函数指针,指向 C 实现的库函数。海报说这对他有用。所以 C++ 与 C 的兼容性可能比任何程序员或用户想象的都要好。
但是,C++ 在许多 C 所不具备的范例中工作,因为它是面向对象的,并且实现了整个范围的抽象、新数据类型和运算符。某些数据类型很容易翻译(char *
C 字符串到 std::string
),而其他数据类型则不然。 This section on GNU.org about C++ compiler options 可能会有一些兴趣。
混合使用两种语言时,我不会太担心或担心性能下降。最终用户,甚至程序员,几乎不会注意到任何可衡量的性能变化,除非他们处理的是大量抽象数据。
我看到了这个 link,但我并不是要求使用 "extern" 的代码性能下降。我的意思是没有 "extern",在 C++ 中使用 C 库时是否有 "context switching"? Are there any problems when using pure C (not class-wrapped) functions in C++ application?
C和C++都是编程语言规范(用英文写的,见例n1570 for the specification of C11) and do not speak about performance (but about behavior of the program, i.e. about semantics)
但是,您可能会使用 GCC or Clang which don't bring any performance penalty, because it builds the same kind of intermediate internal representation (e.g. GIMPLE for GCC, and LLVM for Clang) for both C and C++ languages, and because C and C++ code use compatible ABIs and calling conventions.
等编译器实际上extern "C"
不改变任何调用约定但禁用name mangling. However, its exact influence on the compiler is specific to that compiler. It might (or not) disable inlining(但考虑-flto
用于GCC中的link-时间优化)。
一些 C 编译器(例如 tinycc) produce code with poor performance. Even GCC or Clang, when used with -O0
or without explicitly enabling optimization (e.g. by passing -O1
或 -O2
等...)可能会产生缓慢的代码(默认情况下禁用优化)。
顺便说一句,C++ 被设计为可与 C 互操作(这种强烈的约束解释了 C++ 的大部分缺陷)。
在某些情况下,正版 C++ 代码可能 比对应的正版 C 代码 稍快。例如,要对数字数组进行排序,您将使用 std::array and std::sort in genuine C++, and the compare operations in the sort are likely to get inlined. With C code, you'll just use qsort 并且每个比较都通过间接函数调用(因为编译器没有内联 qsort
,即使理论上它可以...... ).
在其他一些情况下,真正的 C++ 代码可能稍微慢;例如,::operator new
的几个(但不是全部)实现只是简单地调用 malloc
(然后检查失败)但没有内联。
实际上,从 C++ 代码调用 C 代码或从 C 代码调用 C++ 代码没有任何惩罚,因为调用约定是兼容的。
C longjmp facility is probably faster than throwing C++ exceptions, but they don't have the same semantics (see stack unwinding) 和 longjmp
在 C++ 代码中不能很好地混合。
如果您非常关心性能,请编写(用真正的 C 和真正的 C++)两倍的代码和基准测试。您可能会观察到 C 和 C++ 之间的微小变化(最多几个百分点),因此 我根本不会打扰(您的性能问题实际上是不合理的)。
Context switch is a concept related to operating system and multitasking and happens on processes running machine code executable during preemption. How that executable is obtained (from a C compiler, from a C++ compiler, from a Go compiler, from an SBCL compiler, or being an interpreter of some other language like Perl or bytecode Python) is totally irrelevant (since a context switch can happen at any machine instruction, during interrupts). Read some books like Operating Systems: Three Eeasy Pieces。
在基本层面上,没有,当从 C++ 代码调用 C 库时,您不会看到任何类型的 "switching" 性能损失。例如,从 C++ 调用另一个翻译单元中定义的 C 方法应该具有与在另一个翻译单元中调用 C++ 中实现的相同方法(以相同的 C-like 方式)大致相同的性能。
这是因为 C 和 C++ 编译器的常见实现最终将源代码编译为本机代码,并且使用可能发生的相同类型的 call
有效地支持调用 extern "C"
函数一个 C++ 调用。调用约定通常基于平台 ABI,并且在任何一种情况下都是相似的。
抛开这个基本事实,与在 C++ 中实现相同的函数相比,调用 C 函数时可能仍然存在一些性能缺陷:
- 在 C 中实现并声明
extern "C"
并从 C++ 代码调用的函数通常不会被内联(因为根据定义它们不会在 header 中实现),这会抑制整个主机可能非常强大的优化0. - C++ 代码中使用的大多数数据类型1 不能直接被 C 代码使用,例如,如果您的 C++ 代码中有一个
std::string
,您需要选择一种不同的类型将其传递给 C 代码 -char *
很常见,但会丢失有关显式长度的信息,这可能比 C++ 解决方案慢。许多类型没有直接的 C 等价物,因此您可能需要进行代价高昂的转换。 - C 代码使用
malloc
和free
进行动态内存管理,而 C++ 代码通常使用new
和delete
(并且通常更喜欢将这些调用隐藏在其他调用之后类 尽可能)。如果您需要以一种语言分配内存,而该内存将以另一种语言释放,这可能会导致不匹配,您需要回调 "other" 语言来进行释放,或者可能是不必要的复制等。 - C 代码经常大量使用 C 标准库例程,而 C++ 代码通常使用 C++ 标准库中的方法。由于有很多功能重叠,C 和 C++ 的混合可能比纯 C++ 代码具有更大的代码占用空间,因为使用了更多的 C 库方法2.
上述问题仅在将纯 C++ 实现与 C 实现进行对比时适用,并不真正意味着调用 C 时性能会下降:它实际上是在回答问题 "Why could writing an application in a mix of C and C++ be slower than pure C++?"。此外,上述问题主要是非常短的呼叫所关心的问题,其中上述开销可能很大。如果您在 C 中调用冗长的函数,则问题不大。 "data type mismatch" 可能仍然会咬你,但这可以在 C++ 端设计。
0 有趣的是,link-time 优化实际上允许 C 方法是 inlined in C++ code,这是 LTO 的 little-mentioned 好处。当然,这通常取决于使用适当的 LTO 选项自己从源代码构建 C 库。
1 例如,标准布局类型以外的几乎所有内容。
2 许多 C++ 标准库调用最终委托给 C 库例程以进行 "heavy" 提升这一事实至少部分缓解了这种情况,例如如何 std::copy
尽可能调用 memcpy
或 memset
以及大多数 new
实现最终如何调用 malloc
.
C++ 自诞生以来已经发展和改变了很多,但在设计上它与 C 向后兼容。C++ 编译器通常是从 C 编译器构建的,但更现代化的 link-time optimizations. I would imagine lots of software can reliably mix C and C++ code, both in the user spaces and in the libraries used.
但是,C++ 在许多 C 所不具备的范例中工作,因为它是面向对象的,并且实现了整个范围的抽象、新数据类型和运算符。某些数据类型很容易翻译(char *
C 字符串到 std::string
),而其他数据类型则不然。 This section on GNU.org about C++ compiler options 可能会有一些兴趣。
混合使用两种语言时,我不会太担心或担心性能下降。最终用户,甚至程序员,几乎不会注意到任何可衡量的性能变化,除非他们处理的是大量抽象数据。