![](/img/trans.png)
[英]C++: assignment operator: pass-by-value (copy-and-swap) vs pass-by-reference
[英]c++ pass value by reference vs by copy of POD
我听说按引用传递变量并不总是比按值传递快。 对于大变量,通过引用传递速度更快,但是对于小变量,此问题可能很棘手。
通过值传递需要花费时间来创建副本,但是采用局部变量的值应该更快。 通过引用传递并不会浪费时间来创建变量副本,而是先查看指针,然后查看所需的数据。
我知道这个细节在优化问题中并不那么重要,但是对我来说衡量它很有趣(我知道-O0是为了优化而通过的,但是这段代码很简单,在优化之后我不确定自己在测量什么)
g ++ -std = c ++ 14 -O0 -g3 -DSIZE_OF_DATA_ARRAY = 16 main.cpp && ./a.out
g ++(Ubuntu 6.3.0-12ubuntu2)6.3.0 20170406
我的问题:
为什么执行时间对于复制vs结构大小来说是相当恒定的?
为什么复制的门槛在16到17之间?
猜猜:它与缓存连接
我的代码:
#include <iostream>
#include <vector>
#include <limits>
#include <chrono>
#include <iomanip>
#include <vector>
#include <algorithm>
struct Data {
double x[SIZE_OF_DATA_ARRAY];
};
double workOnData(Data &data) {
for (auto i = 0; i < 10; ++i) {
data.x[0] -= 0.5 * (data.x[0] - 1);
}
return data.x[0];
}
void runTestSuite() {
auto queries = 1000000;
Data data;
for (auto i = 0; i < queries; ++i) {
data.x[0] = i;
auto val = workOnData(data);
if (val == -357)
data.x[0] = 1;
}
}
int main() {
std::cout << "sizeof(Data) = " << sizeof(Data) << "\n";
size_t numberOfTests = 99;
std::vector<std::chrono::duration<double>> timeMeasurements{numberOfTests};
std::chrono::time_point<std::chrono::system_clock> startTime, endTime;
for (auto i = 0; i < numberOfTests; ++i) {
startTime = std::chrono::system_clock::now();
runTestSuite();
endTime = std::chrono::system_clock::now();
timeMeasurements[i] = endTime - startTime;
}
std::sort(timeMeasurements.begin(), timeMeasurements.end());
std::chrono::system_clock::time_point now = std::chrono::system_clock::now();
std::time_t now_c = std::chrono::system_clock::to_time_t(now);
std::cout << std::put_time(std::localtime(&now_c), "%F %T")
<< ": median time = " << timeMeasurements[numberOfTests * 0.5].count() << "s\n";
return 0;
}
为什么执行时间对于复制vs结构大小来说是相当恒定的?
最好的理解是从查看汇编语言到查看编译器发出的指令。 此处的优化将取决于编译器的优化设置,以及您是处于发布配置还是调试配置。
还取决于处理器。 例如,某些处理器可能具有用于复制大块内存的专门指令。 其他处理器可能会根据结构的大小以并行块的形式复制数据。 同样,某些平台可能具有硬件帮助,例如DMA控制器。
,有时候展开操作可能比使用特殊说明或硬件帮助更快(取决于数据大小)。
为什么复制的门槛在16到17之间?
阈值可以在对准边界和非对准之间。
让我们以32位处理器为例。 它喜欢一次访问(获取)4个字节。 访问24个字节需要进行6次访存。 访问16个字节需要4次提取。
但是,访问17、18或19个字节需要5次提取。 它可能会再获取4个字节来获取其余字节。
另一种情况是复制功能的实现。 某些复制功能可能将32位副本用于第一个4字节数量的集合,然后切换到字节比较其余部分。 它可以根据数据大小切换为所有字节的字节复制。 许多可能性。
您系统的真相在于调试汇编语言或复制数据的功能。
缓存命中和未命中
您的性能指标可能会因处理器缓存操作而有所偏差。 如果处理器将您的数据保存在缓存中,则循环会快很多。 通常,首次访问数据会对性能造成影响。 如果您的数据对于高速缓存而言太大或位于数据高速缓存大小的范围之外,则可能会浪费更多的时间来重新加载数据高速缓存。
指令缓存问题
许多处理器具有用于数据指令的大型管线和高速缓存。 当它们遇到分支时(例如for
循环的结尾),处理器可能必须重置指令缓存并从程序中的另一个位置重新加载。 这需要时间。 您可以通过展开不同大小的块并测量性能来演示。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.