为什么按值传递QStringView比引用常量更快?

Why passing a QStringView by value is faster than a reference-to-const?

本文关键字:常量 引用 按值传递 QStringView 为什么      更新时间:2023-10-16

我在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 传递它实际上更慢(取消引用变量两次实际上没有任何可观察的效果在现代应用程序中(。我真的希望看到一些支持这一说法的测试。