其中之一更快吗?
inline int ProcessByValue(int i)
{
// process i somehow
}
inline int ProcessByReference(const int& i)
{
// process i somehow
}
我知道整数类型应该按值传递。但是,我担心编译器可能会内联 ProcessByValue 以包含副本。有这方面的规则吗?
这没什么区别。在这两种情况下,代码都将被内联。编译器将消除不必要地复制 int(按值传递),并且不必要地创建对 int 的引用,并在访问 int 时遵循该间接层,也将被消除。
您的问题似乎是基于一些错误的假设:
我知道整数类型应该按值传递。但是,我担心编译器可能会内联 ProcessByValue 以包含副本。有这方面的规则吗?
是的,它会创建一个副本。就像通过引用传递会创建一个引用一样。然后,至少对于像 int 这样的简单类型,编译器会再次消除两者。内联函数不允许改变函数的行为。如果你创建函数来接受一个值参数,它的行为就好像它被赋予了一个值参数,无论它是否被内联。如果您将函数定义为获取引用,则无论它是否内联,它都会表现得好像传递了一个引用。所以做导致正确行为的事情。
应根据对函数有意义的内容键入参数。
如果函数采用原始类型,则按值传递是有意义的。我认识的一些人会抱怨如果它被 const ref 通过(因为它是“不必要的”),但我认为我不会抱怨。如果函数采用用户定义的类型并且不修改参数,那么通过 const ref 传递将是有意义的。
如果它是用户定义的类型并且参数被修改,那么函数的语义将决定它应该如何传递。
编译器应该能够优化内联函数,以便任一方法生成相同的代码。做一个最清楚的。
如果有疑问,请尝试一下。打开编译器的汇编列表输出,看看是否有区别。
如果类型小于或可比指针,则按值传递;例如,int、char、double、小型结构、...
通过引用传递较大的对象;例如,STL 容器。我已经阅读了很多关于编译器能够对其进行优化的信息,但在我接下来的简单基准测试中它们并没有。除非您想浪费时间测试用例,否则请使用const T& obj
.
奖励:从 c99 使用更快的速度restrict
(这样你就可以赶上 fortran,它限制指针别名;用例:f(const T&__restrict__ obj)
。C++ 标准不允许restrict
关键字,但编译器使用内部关键字 - g++ 使用__restrict__
。如果在代码,没有速度增益。
g++ 4.9.2 的基准测试:
通过引用传递向量:
> cat inpoint.cpp
#include <vector>
#include <iostream>
using namespace std;
inline int show_size(const vector<int> &v) {
return v.size();
}
int main(){
vector<int> v(100000000);
cout << show_size(v) << endl;
return 0;
}
> g++ -std=c++14 -O2 inpoint.cpp; time ./a.out
100000000
real 0m0.330s
user 0m0.072s
sys 0m0.256s
按值传递向量需要两倍的时间:
> cat invalue.cpp
#include <vector>
#include <iostream>
using namespace std;
inline int show_size(vector<int> v) {
return v.size();
}
int main(){
vector<int> v(100000000);
cout << show_size(v) << endl;
return 0;
}
> g++ -std=c++14 -O2 invalue.cpp; time ./a.out
100000000
real 0m0.985s
user 0m0.204s
sys 0m0.776s
解决这个问题的最好方法是创建一个测试平台,它可以同时执行这两种操作,构建代码的优化版本,然后检查它的程序集。您将立即看到您的特定编译器和特定用例发生了什么。
当它真正开始时,做你认为你的类的用户对接口的期望。当你把它全部建成并工作时,测量并找出你的瓶颈在哪里。很有可能,这可能产生的任何差异(而且不太可能产生任何差异)将被代码中其他地方更大的性能问题所淹没。
如果您的编译器不够聪明,无法优化掉未修改的本地副本,那么它可能不够聪明,无法优化掉本地引用。在这种情况下,它将为传递引用的情况生成更糟糕的代码(导致每次访问都是间接的)。
一个非常简短的回答:在决定是按引用传递还是按值传递时,将内联和非内联函数视为相同。
在原语的情况下,这并不重要,因为您只传递了 4 个字节。
传递引用的原因是因为它的大小为 4 个字节,并且在自定义类型和大字符串的情况下,这会大大减少大小。
争论是为了速度……通常。
对于内联函数,您希望所有不是原语的类型都通过引用传递,因为您首先告诉编译器内联它。
一般来说,
仅将输出原语声明为引用。
如果您需要禁止表达式,则仅将输入原语声明为引用或 const ref:
int two = plus1( 1 ); // compile error if plus1 is declared as "int plus1( int& )"
double y = sqrt( 1.1 * 2 ); // compile error if sqrt is declared as "double sqrt( const double& )"