优雅的接口设计无需为性能妥协——C++ Copy Elision
许多程序员,尤其是很多稍有一些经验的C++程序员,会陷入一种为了性能而牺牲接口设计可维护性的误区,最终往往在性能上提升很少,甚至没有提高,程序可维护性也大大降低,这是我们都不希望看到的结果。
下面是一个典型的例子,当然进行了一定的抽象和简化。
void getA(A& x) { // do some initialization to "x" } void f() { A a; getA(a); // use "a" }
我问,为什么不设计成“A getA()”,让对象a在函数getA中进行构造呢?这样接口就更加容易阅读,一般并不鼓励将参数作为输出参数来使用,这样会降低程序的可读性。
他给出的答案是,这样可以避免对象的拷贝构造,能够提升性能。因为如果在getA中构造了A的对象,那么在返回时要调用一次拷贝构造函数将这个对象拷贝构造到调用处,然后再析构getA中临时产生的对象。
事实果真如此吗?让我们阅读一下C++标准的相关章节。
When certain criteria are met, an implementation is allowed to omit the copy/move construction of a class object, even if the constructor selected for the copy/move operation and/or the destructor for the object have side effects. In such cases, the implementation treats the source and target of the omitted copy/move operation as simply two different ways of referring to the same object, and the destruction of that object occurs at the later of the times when the two objects would have been destroyed without the optimization. This elision of copy/move operations, called copy elision, is permitted in the following circumstances (which may be combined to eliminate multiple copies):
- in a return statement in a function with a class return type, when the expression is the name of a non-volatile automatic object (other than a function or catch-clause parameter) with the same cv-unqualified type as the function return type, the copy/move operation can be omitted by constructing the automatic object directly into the function’s return value
让我们根据上面条款来看看,下面的代码会怎么处理。
A getA() { A x; // logically construct "x" here, but actually not // do some initialization to "x" return x; // logically copy-construct "x" to "a" here, but actually not // logically destruct "x", but actually not } void f() { A a = getA(); // actually construct "x" directly here // use "a" }
根据标准说明,函数getA的返回值是类型A,并且与返回类型一样是non-const、non-volatile对象,也是automatic对象(逻辑上被分配到栈上),从对象x到调用处(f函数中的变量a)的拷贝构造可以被省略,对象x将会被直接构造在f函数的变量a处(getA中的x和f中的a将被视为同一个对象)。即使拷贝构造函数中有任何副作用,这个调用也仍然可以被省略,并且这种行为被视为正常的语义,而非优化行为。
在gcc编译器中,即使指定优化级别为0,在上面的例子中也不会有两个A对象被构造;而Visual Studio指定了一定优化级别以后,多余的对象构造也会被省略。
因此,第一种写法能够提升性能的说法并不成立,而且接口设计相当丑陋难读。相反,有时第一种写法甚至会损害性能。
请看下面的代码。
A::A(int x, int y, int z) : x(x), y(y), z(z) {} A getA() { // get height, width, length from somewhere else A x(height, width, length); return x; } void f() { A a = getA(); // use "a" }
这段代码说A有一种接受三个参数的构造函数,而这三个参数在getA中可以通过某些逻辑得到,那么可以直接通过带参数的构造函数对A进行初始化,这个带参数的构造函数直接用高效的冒号语法进行初始化。反观第一种方案,如果获取三个参数的逻辑对于函数f是不可见的,那么我们就必须要首先默认构造A对象,然后再进行赋值操作对A对象进行初始化,非但性能低下,且代码丑陋难以维护。
在C++11中引入了右值引用的概念,即使有编译器决定将x构造在getA内部,再用它来构造对象a,这种情形也适用move constructor,而几乎没有可观的性能损耗。当然,在这种情况下,即使定义了move constructor,gcc也不会调用它。
所以,我们不应该想当然地对代码进行优化,尤其是在要牺牲代码可读性的情况下更加应该慎重:
- 首先应该考察,是否确实存在性能低下(在这个例子中没有)
- 如果性能低下,则需考察这个性能问题是否是系统性能的瓶颈
- 如果是,是否能够借助编译器优化提升性能,或者局部性地改变代码使得编译器更容易优化,或许会影响一定的代码的可读性,但对于接口的可读性没有影响
- 如果仍然无法改善,才有可能考虑牺牲接口的可读性
Aug 30, 2021 12:42:00 AM
You possess lifted an essential offspring..Blesss for using..I would want to study better latest transactions from this blog..preserve posting..
Sep 25, 2021 05:31:05 AM
This is very interesting content! I have thoroughly enjoyed reading your points and have come to the conclusion that you are right about many of them. You are great.
Aug 17, 2022 10:18:41 PM
The Nagaland Board's official website is anticipated to provide its previous exam questions for the academic year 2023. Through its Scholars and Faculty, the Board hopes to further research in the area of education. The NBSE Board's regular courses are briefly described, and they are provided in many streams. Nagaland Board 10th Class Important Question Paper 2023 The Nagaland Board of School Education has released the High School Leaving Certificate HSLC Important Question Paper 2023. For students in the HSLC and 10th class, the Nagaland Board of School Education (NBSE) will administer exams in February to March 2023, much like in the past. They could use the Nagaland NBSE Class 10th Question Paper & New Question Paper & Guess Paper 2023 to plan their test preparation.
Nov 24, 2022 12:39:32 PM
C++ Copy Elision is a compiler optimization technique that eliminates unnecessary copies of objects. This can improve performance by avoiding diamonds rings near me the overhead of making copies of objects that are not actually needed. In some cases, it can also enable the compiler to generate more efficient code.
Dec 11, 2022 02:23:20 PM
C++ Copy Elision is a performance optimization that eliminates the need to copy objects when they are returned from a function. This can result in faster code and reduced memory usage. However, it can also lead to KROQ-FM unexpected behavior if the objects being returned are modified after they are returned.
Apr 23, 2023 03:24:27 PM
crediblebh
Jul 13, 2023 12:50:05 AM
Mon activité aujourd’hui est une pièce qui suit ou enregistre l’activité des utilisateurs qui collecte des informations chaque fois que les utilisateurs disposant d’un compte Google utilisent les services Google quotidiennement. mon activité google chrome Ces services d’enregistrement ou de collecte de Mon activité incluent YouTube, Google Maps et d’autres applications Google, ainsi que votre historique de recherche lorsque vous utilisez le navigateur Google Chrome ou effectuez une recherche Google.
Jul 28, 2023 09:57:03 PM
we Request the Students to Check their Secondary School Certificate (SSC) Exam 2024 Date and Start their Preparation by Downloading the MSBSHSE 10th Previous Paper Pdf. Actually, don’t Forget to refer the important Questions Subject Wise 2024 in order to get good marks in All the Subjects. Hence forth get the MSBSHSE SSC Exam Question Paper 2024 pdf along with the solutions from our page Maha Board 10th Model Paper 2024 Students who have Completed their Preparation can Start Practising the MSBSHSE Board 10th Guess Paper 2024. we Suggest the Students Solved Question Paper for Practice Purpose, in fact by Practising Question Paper Students can get the brief idea about the Public Examination 2024.
Jan 13, 2024 10:31:08 PM
The best article I came across a number of years, write something about it on this pag