为什么按值传递QStringView比引用常量更快?
Why passing a QStringView by value is faster than a reference-to-const?
我在Qt的文档中找到了这个:
QStringViews应该按值传递,而不是通过对const的引用:
他们给出了以下示例:
void myfun1(QStringView sv); // preferred
void myfun2(const QStringView &sv); // compiles and works, but slower
这怎么可能?
QStringView
通常用于高性能代码,其中由于涉及内存分配,创建实际QString
对象会很慢。我优化了QStringView
,使其性能与手动处理(const QChar*, size_t)
一样。我什至将成员函数调用内联移交给自由函数,按值传递*this
。所有这些都是为了避免强制QStringView
对象到堆栈上,或者更一般地说,强制到内存中。
在几乎所有C++编译器中,QStringView
对象都表示为一对 CPU 寄存器,许多C++ ABI(遗憾的是,不包括 Windows(都支持将 CPU 寄存器中的此类类型传递给函数。如果您天真地编写了成员函数,并使用隐式this
参数作为对象的地址,则外联调用此类函数将强制编译器在堆栈上分配一个QStringView
对象,以便能够将其地址作为成员函数的第一个参数传递。
按值传递还有第二个参数:混叠问题较少。作为引用类型,QStringView
无论如何都会表现出这个问题,但请考虑std::complex
:
std::complex &operator*=(std::complex &lhs, const std::complex &rhs);
(为简洁起见,省略了模板参数(。这可以这样调用:
std::complex c = 3 + 4i;
c *= c;
如果你天真地实现operator*=
,就好像它是一个数学函数一样:
auto r = real(), i = imag();
m_real = r * other.real() - i * other.imag();
m_imag = r * other.imag() + i * other.real();
你会在第一行之后破坏other.real()
,从而计算出错误的结果(是的,人们确实在生产中编写了这段代码(。按值传递rhs
会使问题消失。
他们在文档中说它应该按值传递,因为QStringView
本身不是一个字符串,它只是一种接口(嗯,这就是为什么它被称为"视图"(,给你对引用的字符串的读取访问权限。因此,QStringView
的大小很可能与引用的大小相似。根据QT源代码,QStringView
只有2个字段:
private:
qsizetype m_size;
const storage_type *m_data;
在我的 gcc 6.3 的 Debian9 x64 机器上,这个类的大小是 8(指针(+ 4(整数(= 12 字节。在我的例子中,引用大小是 8 字节,因此在函数调用时的数据复制方面只有很小的差异。请记住,在不同的机器上,引用可能以另一种方式实现,并且可能更大。
虽然我同意这种接口应该通过复制(而不是 const ref(传递,但我不明白为什么他们声称通过 const ref 传递它实际上更慢(取消引用变量两次实际上没有任何可观察的效果在现代应用程序中(。我真的希望看到一些支持这一说法的测试。
- 什么时候在C++中返回常量引用是个好主意
- 通过常量引用传递参数的矩阵模板类
- 在C++中使用非常量引用作为常量
- 具有常量引用参数的函数模板专用化
- 多个"常量引用"变量可以共享同一个内存吗?
- 为什么 STL 容器适配器堆栈中的 top 返回常量引用?
- 为什么常量方法可以采用非常量引用?
- 为什么当我们有常量引用时创建临时对象?
- 如何返回向量的常量引用?
- C++:常量引用参数
- 不同于按值传递和常量引用传递的程序集
- 为什么const_cast和static_cast常量引用没有效果?
- C++ 获取函数在常量引用中按值返回的结果
- 从 BubbleSort* 类型的右值初始化 'AssortedSorter&' 类型的非常量引用无效"
- C++ 在类中使用常量引用文本时 O2 内存泄漏
- 是否可以跨 dll 边界返回常量引用/指向 std::vectors?
- C++中大多数/所有 setter 函数的参数是否应该写为常量引用?
- 通过非常量引用参数修改常量引用参数
- 将常量引用传递给线程
- 为什么C++中没有常量引用,就像常量指针一样?