I saw this link but I'm not asking for a performance degradation for code using "extern". I mean without "extern", is there "context switching" when using C library in C++? Are there any problems when using pure C (not class-wrapped) functions in C++ application?
我看到了这个链接,但是我并不是要求使用“extern”的代码性能降低。我是说,如果没有“extern”,在c++中使用C库时是否存在“上下文切换”?在c++应用程序中使用纯C(非类包装)函数时,有什么问题吗?
3 个解决方案
#1
36
Both C and C++ are programming language specifications (written in English, see e.g. n1570 for the specification of C11) and do not speak about performance (but about behavior of the program, i.e. about semantics).
C和c++都是编程语言规范(用英语编写,参见n1570了解C11的规范),并且不讨论性能(而是关于程序的行为,即语义)。
However, you are likely to use a compiler such as 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.
然而,你可能会使用一个编译器GCC或叮当声等,不要带任何性能损失,因为它的构建同样的中间内部表示(例如GIMPLE GCC,叮当声LLVM)对C和c++语言,因为C和c++代码使用兼容的abi和调用约定。
In practice extern "C"
don't change any calling convention but disables name mangling. However, its exact influence on the compiler is specific to that compiler. It might (or not) disable inlining (but consider -flto
for link-time-optimization in GCC).
在实践中,外部“C”不改变任何调用约定,而是禁用名称管理。但是,它对编译器的确切影响是特定于该编译器的。它可能(或不)禁用内联(但是考虑-flto在GCC中用于链接时间优化)。
Some C compilers (e.g. tinycc) produce code with poor performance. Even GCC or Clang, when used with -O0
or without explicitly enabling optimization (e.g. by passing -O1
or -O2
etc...) might produce slow code (and optimizations are by default disabled with it).
一些C编译器(例如tinycc)生成性能较差的代码。即使是GCC或Clang,当与-O0一起使用或没有显式地启用优化(例如通过传递-O1或-O2等)时,也可能产生缓慢的代码(默认情况下禁用优化)。
BTW, C++ was designed to be interoperable with C (and that strong constraint explains most of the deficiencies of C++).
顺便说一句,c++的设计目的是与C进行互操作(强大的约束解释了c++的大部分缺陷)。
In some cases, genuine C++ code might be slightly faster than corresponding genuine C code. For example, to sort an array of numbers, you'll use 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 and each compare goes through an indirect function call (because the compiler is not inlining qsort
, even if in theory it could...).
在某些情况下,真正的c++代码可能比相应的真正的C代码快一些。例如,要对数字数组进行排序,您将在真正的c++中使用std::array和std::sort中的比较操作可能会被内联。使用C代码,您只需使用qsort,每个比较都经过一个间接的函数调用(因为编译器并没有插入qsort,即使在理论上它可以…)。
In some other cases, genuine C++ code might be slightly slower; for example, several (but not all) implementations of ::operator new
are simply calling malloc
(then checking against failure) but are not inlined.
在其他一些情况下,真正的c++代码可能会稍微慢一些;例如,一些(但不是全部)的:操作符new只是调用malloc(然后检查失败),但是没有内联。
In practice, there is no penalty in calling C code from C++ code, or C++ code from C code, since the calling conventions are compatible.
在实践中,从c++代码调用C代码,或者从C代码调用c++代码,没有任何代价,因为调用约定是兼容的。
The C longjmp facility is probably faster than throwing C++ exceptions, but they don't have the same semantics (see stack unwinding) and longjmp
doesn't mix well accross C++ code.
C longjmp工具可能比抛出c++异常要快,但是它们没有相同的语义(参见堆栈展开),而且longjmp不能很好地混合使用c++代码。
If you care that much about performance, write (in genuine C and in genuine C++) twice your code and benchmark. You are likely to observe a small change (a few percents at most) between C and C++, so I would not bother at all (and your performance concerns are practically unjustified).
如果您非常关心性能,那么就编写(以正版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++编译器、Go编译器、SBCL编译器,或者作为其他语言的解释器,如Perl或字节码Python)完全无关紧要(因为上下文切换可以在任何机器指令、中断期间发生)。读一些像操作系统这样的书:三篇Eeasy文章。
#2
13
At a basic level, no, you won't see any type of "switching" performance penalty when calling a C library from C++ code. For example calling from C++ a C method defined in another translation unit should have approximately identical performance to calling the same method implemented in C++ (in the same C-like way) in another translation unit.
在基本的层次上,不,在从c++代码调用C库时,您不会看到任何类型的“切换”性能损失。例如,从c++调用另一个翻译单元中定义的C方法,其性能应该与在另一个翻译单元中调用在c++中实现的相同方法(以类似于C的方式)几乎相同。
This is because common implementations of C and C++ compilers ultimately compile the source down to native code, and calling an extern "C"
function is efficiently supported using the same type of call
that might occur for a C++ call. The calling conventions are usually based on the platform ABI and are similar in either case.
这是因为C和c++编译器的常见实现最终将源代码编译为本机代码,并且使用与c++调用相同的调用类型,可以有效地支持调用extern“C”函数。调用约定通常基于平台ABI,两者都类似。
That basic fact aside, there might still be some performance downsides when calling a C function as opposed to implementing the same function in C++:
撇开这个基本事实不谈,调用C函数与在c++中实现相同的函数相比,可能还有一些性能上的缺点:
- Functions implemented in C and declared
extern "C"
and called from C++ code usually won't be inlined (since by definition they aren't implemented in a header), which inhibits a whole host of possibly very powerful optimization0. - 在C中实现的函数和声明的extern“C”以及从c++代码中调用的函数通常不会被内联(因为从定义上看,它们不是在header中实现的),这将抑制可能非常强大的优化。
- Most data types used in C++ code1 can't be directly used by C code, so for example if you have a
std::string
in your C++ code, you'll need to pick a different type to pass it to C code -char *
is common but loses information about the explicit length, which may be slower than a C++ solution. Many types have no direct C equivalent, so you may be stuck with a costly conversion. - 大多数数据类型用于c++ code1不能直接使用的C代码,举个例子,如果你有一个std::string在c++代码中,您需要选择一个不同的类型将其传递给C代码,char *共同但失去了明确的长度的信息,这可能是慢于c++的解决方案。许多类型都没有直接的C,因此您可能会陷入代价高昂的转换。
- C code uses
malloc
andfree
for dynamic memory management, while C++ code generally usesnew
anddelete
(and usually prefers to hide those calls behind other classes as much as possible). If you need to allocate memory in one language that will be freed in other other, this may cause a mismatch where you need to call back into the "other" language to do the free, or may unnecessary copies, etc. - C代码使用malloc和free进行动态内存管理,而c++代码通常使用new和delete(并且通常更喜欢尽可能将这些调用隐藏在其他类后面)。如果您需要在一种语言中分配内存,而该语言将在另一种语言中释放内存,这可能会导致不匹配,您需要回调到“其他”语言来做免费的工作,或者可能产生不必要的副本,等等。
- C code often makes heavy use of the C standard library routines, while C++ code usually uses methods from the C++ standard library. Since there is a lot of functional overlap, it is possible that a mix of C and C++ has a larger code footprint than pure C++ code since more C library methods are used2.
- C代码经常大量使用C标准库例程,而c++代码通常使用c++标准库中的方法。由于有很多功能上的重叠,所以C和c++的混合比纯c++代码有更大的代码占用空间,因为更多的C库方法是used2。
The concerns above would apply only when contrasting a pure C++ implementation versus a C one, and doesn't really mean there is a performance degradation when calling C: it is really answering the question "Why could writing an application in a mix of C and C++ be slower than pure C++?". Furthermore, the above issues are mostly a concern for very short calls where the above overheads may be significant. If you are calling a lengthy function in C, it is less of a problem. The "data type mismatch" might still bite you, but this can be designed around on the C++ side.
上面的担忧将仅适用于当对比纯c++实现与C,和并不意味着有一个性能下降当调用C:这真的是回答这个问题“为什么可以编写一个应用程序的C和c++低于纯c++ ?”。此外,上述问题主要是对非常短的调用的关注,在这些调用中,上面的开销可能非常大。如果您正在用C调用一个冗长的函数,那么这不是什么问题。“数据类型不匹配”可能仍然会影响到您,但这可以在c++方面进行设计。
0 Interestingly, link-time optimization actually allows C methods to be inlined in C++ code, which is a little-mentioned benefit of LTO. Of course, this is generally dependent on building the C library yourself from source with the appropriate LTO options.
有趣的是,link-time优化实际上允许C方法内联在c++代码中,这是LTO的一个很少提到的好处。当然,这通常依赖于使用适当的LTO选项自己从源代码构建C库。
1 E.g., pretty much anything other than a standard layout type.
除了标准的布局类型之外,几乎什么都有。
2 This is at least partially mitigated by the fact that many C++ standard library calls ultimately delegate to C library routines for the "heavy" lifting, such as how std::copy
calls memcpy
or memset
when possible and how most new
implementations ultimately call malloc
.
许多c++标准库调用最终委托给C库例程来完成“繁重”的工作,例如std::copy如何在可能的情况下调用memcpy或memset,以及大多数新实现如何最终调用malloc,这至少部分减轻了这一点。
#3
4
C++ has grown and changed a lot since its inception, but by design it is backwards-compatible with C. C++ compilers are generally built from C compilers, but even more modernized with 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. I answered a question recently that involved passing a C++ class member function pointer, to a C-implemented library function. The poster said it worked for him. So it's possible C++ is more compatible with C than any programmers or users would think.
c++自创建以来已经发展了很多,但是通过设计它是向后兼容的,c++编译器一般都是由C编译器构建的,但是通过链接时间优化更现代化。我认为很多软件可以在用户空间和使用的库中可靠地混合C和c++代码。我最近回答了一个问题,这个问题涉及到向C实现的库函数传递一个c++类成员函数指针。海报上说这对他有用。因此,c++可能比任何程序员或用户认为的都更兼容C。
However, C++ works in many different paradigms that C does not, as it is object-oriented, and implements a whole spectrum of abstractions, new data types, and operators. Certain data types are easily translatable (char *
C string to a std::string
), while others are not. This section on GNU.org about C++ compiler options may be of some interest.
但是,c++在许多不同的范例中工作,而C不这么做,因为它是面向对象的,并且实现了一系列抽象、新数据类型和操作符。某些数据类型很容易翻译(char * C字符串到std::string),而其他数据类型则不容易翻译。关于c++编译器选项的GNU.org的这一节可能会有一些兴趣。
I would not be too worried or concerned about any decline in performance, when mixing the two languages. The end user, and even the programmer, would hardly notice any measurable changes in performance, unless they were dealing with big abstractions of data.
在混合这两种语言时,我不会太担心或担心性能下降。最终用户,甚至程序员,几乎不会注意到性能的任何可度量的变化,除非他们处理的是大量抽象的数据。
#1
36
Both C and C++ are programming language specifications (written in English, see e.g. n1570 for the specification of C11) and do not speak about performance (but about behavior of the program, i.e. about semantics).
C和c++都是编程语言规范(用英语编写,参见n1570了解C11的规范),并且不讨论性能(而是关于程序的行为,即语义)。
However, you are likely to use a compiler such as 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.
然而,你可能会使用一个编译器GCC或叮当声等,不要带任何性能损失,因为它的构建同样的中间内部表示(例如GIMPLE GCC,叮当声LLVM)对C和c++语言,因为C和c++代码使用兼容的abi和调用约定。
In practice extern "C"
don't change any calling convention but disables name mangling. However, its exact influence on the compiler is specific to that compiler. It might (or not) disable inlining (but consider -flto
for link-time-optimization in GCC).
在实践中,外部“C”不改变任何调用约定,而是禁用名称管理。但是,它对编译器的确切影响是特定于该编译器的。它可能(或不)禁用内联(但是考虑-flto在GCC中用于链接时间优化)。
Some C compilers (e.g. tinycc) produce code with poor performance. Even GCC or Clang, when used with -O0
or without explicitly enabling optimization (e.g. by passing -O1
or -O2
etc...) might produce slow code (and optimizations are by default disabled with it).
一些C编译器(例如tinycc)生成性能较差的代码。即使是GCC或Clang,当与-O0一起使用或没有显式地启用优化(例如通过传递-O1或-O2等)时,也可能产生缓慢的代码(默认情况下禁用优化)。
BTW, C++ was designed to be interoperable with C (and that strong constraint explains most of the deficiencies of C++).
顺便说一句,c++的设计目的是与C进行互操作(强大的约束解释了c++的大部分缺陷)。
In some cases, genuine C++ code might be slightly faster than corresponding genuine C code. For example, to sort an array of numbers, you'll use 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 and each compare goes through an indirect function call (because the compiler is not inlining qsort
, even if in theory it could...).
在某些情况下,真正的c++代码可能比相应的真正的C代码快一些。例如,要对数字数组进行排序,您将在真正的c++中使用std::array和std::sort中的比较操作可能会被内联。使用C代码,您只需使用qsort,每个比较都经过一个间接的函数调用(因为编译器并没有插入qsort,即使在理论上它可以…)。
In some other cases, genuine C++ code might be slightly slower; for example, several (but not all) implementations of ::operator new
are simply calling malloc
(then checking against failure) but are not inlined.
在其他一些情况下,真正的c++代码可能会稍微慢一些;例如,一些(但不是全部)的:操作符new只是调用malloc(然后检查失败),但是没有内联。
In practice, there is no penalty in calling C code from C++ code, or C++ code from C code, since the calling conventions are compatible.
在实践中,从c++代码调用C代码,或者从C代码调用c++代码,没有任何代价,因为调用约定是兼容的。
The C longjmp facility is probably faster than throwing C++ exceptions, but they don't have the same semantics (see stack unwinding) and longjmp
doesn't mix well accross C++ code.
C longjmp工具可能比抛出c++异常要快,但是它们没有相同的语义(参见堆栈展开),而且longjmp不能很好地混合使用c++代码。
If you care that much about performance, write (in genuine C and in genuine C++) twice your code and benchmark. You are likely to observe a small change (a few percents at most) between C and C++, so I would not bother at all (and your performance concerns are practically unjustified).
如果您非常关心性能,那么就编写(以正版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++编译器、Go编译器、SBCL编译器,或者作为其他语言的解释器,如Perl或字节码Python)完全无关紧要(因为上下文切换可以在任何机器指令、中断期间发生)。读一些像操作系统这样的书:三篇Eeasy文章。
#2
13
At a basic level, no, you won't see any type of "switching" performance penalty when calling a C library from C++ code. For example calling from C++ a C method defined in another translation unit should have approximately identical performance to calling the same method implemented in C++ (in the same C-like way) in another translation unit.
在基本的层次上,不,在从c++代码调用C库时,您不会看到任何类型的“切换”性能损失。例如,从c++调用另一个翻译单元中定义的C方法,其性能应该与在另一个翻译单元中调用在c++中实现的相同方法(以类似于C的方式)几乎相同。
This is because common implementations of C and C++ compilers ultimately compile the source down to native code, and calling an extern "C"
function is efficiently supported using the same type of call
that might occur for a C++ call. The calling conventions are usually based on the platform ABI and are similar in either case.
这是因为C和c++编译器的常见实现最终将源代码编译为本机代码,并且使用与c++调用相同的调用类型,可以有效地支持调用extern“C”函数。调用约定通常基于平台ABI,两者都类似。
That basic fact aside, there might still be some performance downsides when calling a C function as opposed to implementing the same function in C++:
撇开这个基本事实不谈,调用C函数与在c++中实现相同的函数相比,可能还有一些性能上的缺点:
- Functions implemented in C and declared
extern "C"
and called from C++ code usually won't be inlined (since by definition they aren't implemented in a header), which inhibits a whole host of possibly very powerful optimization0. - 在C中实现的函数和声明的extern“C”以及从c++代码中调用的函数通常不会被内联(因为从定义上看,它们不是在header中实现的),这将抑制可能非常强大的优化。
- Most data types used in C++ code1 can't be directly used by C code, so for example if you have a
std::string
in your C++ code, you'll need to pick a different type to pass it to C code -char *
is common but loses information about the explicit length, which may be slower than a C++ solution. Many types have no direct C equivalent, so you may be stuck with a costly conversion. - 大多数数据类型用于c++ code1不能直接使用的C代码,举个例子,如果你有一个std::string在c++代码中,您需要选择一个不同的类型将其传递给C代码,char *共同但失去了明确的长度的信息,这可能是慢于c++的解决方案。许多类型都没有直接的C,因此您可能会陷入代价高昂的转换。
- C code uses
malloc
andfree
for dynamic memory management, while C++ code generally usesnew
anddelete
(and usually prefers to hide those calls behind other classes as much as possible). If you need to allocate memory in one language that will be freed in other other, this may cause a mismatch where you need to call back into the "other" language to do the free, or may unnecessary copies, etc. - C代码使用malloc和free进行动态内存管理,而c++代码通常使用new和delete(并且通常更喜欢尽可能将这些调用隐藏在其他类后面)。如果您需要在一种语言中分配内存,而该语言将在另一种语言中释放内存,这可能会导致不匹配,您需要回调到“其他”语言来做免费的工作,或者可能产生不必要的副本,等等。
- C code often makes heavy use of the C standard library routines, while C++ code usually uses methods from the C++ standard library. Since there is a lot of functional overlap, it is possible that a mix of C and C++ has a larger code footprint than pure C++ code since more C library methods are used2.
- C代码经常大量使用C标准库例程,而c++代码通常使用c++标准库中的方法。由于有很多功能上的重叠,所以C和c++的混合比纯c++代码有更大的代码占用空间,因为更多的C库方法是used2。
The concerns above would apply only when contrasting a pure C++ implementation versus a C one, and doesn't really mean there is a performance degradation when calling C: it is really answering the question "Why could writing an application in a mix of C and C++ be slower than pure C++?". Furthermore, the above issues are mostly a concern for very short calls where the above overheads may be significant. If you are calling a lengthy function in C, it is less of a problem. The "data type mismatch" might still bite you, but this can be designed around on the C++ side.
上面的担忧将仅适用于当对比纯c++实现与C,和并不意味着有一个性能下降当调用C:这真的是回答这个问题“为什么可以编写一个应用程序的C和c++低于纯c++ ?”。此外,上述问题主要是对非常短的调用的关注,在这些调用中,上面的开销可能非常大。如果您正在用C调用一个冗长的函数,那么这不是什么问题。“数据类型不匹配”可能仍然会影响到您,但这可以在c++方面进行设计。
0 Interestingly, link-time optimization actually allows C methods to be inlined in C++ code, which is a little-mentioned benefit of LTO. Of course, this is generally dependent on building the C library yourself from source with the appropriate LTO options.
有趣的是,link-time优化实际上允许C方法内联在c++代码中,这是LTO的一个很少提到的好处。当然,这通常依赖于使用适当的LTO选项自己从源代码构建C库。
1 E.g., pretty much anything other than a standard layout type.
除了标准的布局类型之外,几乎什么都有。
2 This is at least partially mitigated by the fact that many C++ standard library calls ultimately delegate to C library routines for the "heavy" lifting, such as how std::copy
calls memcpy
or memset
when possible and how most new
implementations ultimately call malloc
.
许多c++标准库调用最终委托给C库例程来完成“繁重”的工作,例如std::copy如何在可能的情况下调用memcpy或memset,以及大多数新实现如何最终调用malloc,这至少部分减轻了这一点。
#3
4
C++ has grown and changed a lot since its inception, but by design it is backwards-compatible with C. C++ compilers are generally built from C compilers, but even more modernized with 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. I answered a question recently that involved passing a C++ class member function pointer, to a C-implemented library function. The poster said it worked for him. So it's possible C++ is more compatible with C than any programmers or users would think.
c++自创建以来已经发展了很多,但是通过设计它是向后兼容的,c++编译器一般都是由C编译器构建的,但是通过链接时间优化更现代化。我认为很多软件可以在用户空间和使用的库中可靠地混合C和c++代码。我最近回答了一个问题,这个问题涉及到向C实现的库函数传递一个c++类成员函数指针。海报上说这对他有用。因此,c++可能比任何程序员或用户认为的都更兼容C。
However, C++ works in many different paradigms that C does not, as it is object-oriented, and implements a whole spectrum of abstractions, new data types, and operators. Certain data types are easily translatable (char *
C string to a std::string
), while others are not. This section on GNU.org about C++ compiler options may be of some interest.
但是,c++在许多不同的范例中工作,而C不这么做,因为它是面向对象的,并且实现了一系列抽象、新数据类型和操作符。某些数据类型很容易翻译(char * C字符串到std::string),而其他数据类型则不容易翻译。关于c++编译器选项的GNU.org的这一节可能会有一些兴趣。
I would not be too worried or concerned about any decline in performance, when mixing the two languages. The end user, and even the programmer, would hardly notice any measurable changes in performance, unless they were dealing with big abstractions of data.
在混合这两种语言时,我不会太担心或担心性能下降。最终用户,甚至程序员,几乎不会注意到性能的任何可度量的变化,除非他们处理的是大量抽象的数据。