通过引用传递基元类型 C++

Passing primitive type by reference C++

提问人:Joeger-Bahar 提问时间:3/25/2023 更新时间:3/25/2023 访问量:94

问:

我发现很多人说,按值传递原始值(例如 int)比按引用传递要快。

但是,我写了下面的代码,并按值传递,它的平均运行时间为 2 秒。

#include <iostream>
#include <chrono>

using namespace std;
using namespace chrono;

int func(unsigned long long x) {
  ++x;
  return x;
}

int main()
{
  for (int b = 0; b < 5; b++) {
    
    unsigned long long x = 0;
  
    auto start = high_resolution_clock::now();
    for (long long j = 0; j < 500000; j++) {
      for (long long i = 0; i < 180000000; i++) {
        x = func(x);
      }
    }
  
    auto stop = high_resolution_clock::now();
    auto elapsed = std::chrono::high_resolution_clock::now() - start;
  
    long long microseconds = std::chrono::duration_cast<std::chrono::microseconds>(elapsed).count();
    cout << x <<" iterations took " << microseconds << " microseconds\n";
  }
}

然后,当我通过引用时,它的平均时间不到 1 微秒。

#include <iostream>
#include <chrono>

using namespace std;
using namespace chrono;

void func(unsigned long long& x) {
  ++x;
}

int main()
{
  for (int b = 0; b < 5; b++) {
    
    unsigned long long x = 0;
  
    auto start = high_resolution_clock::now();
    for (long long j = 0; j < 500000; j++) {
      for (long long i = 0; i < 180000000; i++) {
        func(x);
      }
    }
  
    auto stop = high_resolution_clock::now();
    auto elapsed = std::chrono::high_resolution_clock::now() - start;
  
    long long microseconds = std::chrono::duration_cast<std::chrono::microseconds>(elapsed).count();
    cout << x <<" Took " << microseconds << " microseconds\n";
  }
}

我想解释为什么会发生这种情况。

免责声明:我对C++相当陌生

C++ 性能测试 通过引用

评论

1赞 tadman 3/25/2023
与其考虑使用更具体的类型来传达意图,不如考虑使用过多的冗长和歧义,例如 .unsigned long longuint64_t
4赞 tadman 3/25/2023
这是调试版本还是完全优化(例如)版本?我希望优化的构建只是将两个数字相乘,实际上根本不会循环。使用代码的程序集转储进行检查,例如来自 godbolt-O3
0赞 tadman 3/25/2023
值得一提的是,第一个版本在 M1 机器上使用 clang 并不需要几秒钟,但话又说回来,如果它实际上是循环的,那就是 90 万亿次迭代。要使运行时间少于几分钟或几小时,必须进行一些编译器优化。
3赞 PaulMcKenzie 3/25/2023
启用优化后,该嵌套循环将消失
0赞 Paul Sanders 3/25/2023
为了详细说明为什么按值传递基元(和其他一些东西)通常更快,因为它们适合一个(或两个)寄存器,因此副本不会花费您任何费用。OTOH,如果您通过引用传递,那么访问被调用函数中的变量涉及额外的取消引用。有时编译器可以对此进行优化。有时不是。

答: 暂无答案