哪个更快:
(*connectionsById)[connection->nextConnectionId].reachable = calculationId;
(*connectionsById)[connection->nextConnectionId].numBoardings = connection->numBoardings;
(*connectionsById)[connection->nextConnectionId].journeySteps = connection->journeySteps;
或者:
Connection& tmpConnection = (*connectionsById)[connection->nextConnectionId];
tmpConnection.reachable = calculationId;
tmpConnection.numBoardings = connection->numBoardings;
tmpConnection.journeySteps = connection->journeySteps;
编译器是否会解决这个问题?
(我是C ++的初学者)
答案 0 :(得分:4)
这些不相等:
(*connectionsById)[connection->nextConnectionId].reachable = calculationId;
(*connectionsById)[connection->nextConnectionId].numBoardings = connection->numBoardings;
(*connectionsById)[connection->nextConnectionId].journeySteps = connection->journeySteps;
这将取消引用connectionsById
3次并相应地设置它的成员变量。
Connection tmpConnection = (*connectionsById)[connection->nextConnectionId];
tmpConnection.reachable = calculationId;
tmpConnection.numBoardings = connection->numBoardings;
tmpConnection.journeySteps = connection->journeySteps;
这将创建一个新对象tmpConnection
,从connectionsById
指向的任何对象复制。然后它将修改副本而不是原始对象。
如果要修改原始对象,则应使用引用:(使用&
)
Connection& tmpConnection = (*connectionsById)[connection->nextConnectionId];
tmpConnection.reachable = calculationId;
tmpConnection.numBoardings = connection->numBoardings;
tmpConnection.journeySteps = connection->journeySteps;
现在,用重新编写的代码片段提出相同的问题,两者在引擎盖下相似/相等,因此性能差异将是最小/无。
答案 1 :(得分:1)
第二个会更快,但如果你真的想要更改地图中的值而不仅仅是那个副本,tmpConnection
需要作为参考。
如果您确实使用了引用(Connection& tmpConnection = ...
),我怀疑编译器是否足够聪明,可以使第一个代码与第二个代码一样快。第一个代码按地图中的键执行3次搜索,而第二个代码执行一次搜索并缓存结果。编译器必须承担很多事情来进行这种优化。 (例如,您可以使用具有自己的比较函数的映射来排序和搜索键相等。没有什么能阻止您更改该函数在不同搜索之间的行为。因此,编译器不能假设搜索将实现相同的结果)。
但是,如果地图相对较小,这种差异可能可以忽略不计,编写更清晰的代码可能更为重要。
这是一个夸张的测试,显示了差异:
#include <iostream>
#include <map>
#include <chrono>
using namespace std;
int main()
{
int niter = 100000;
int nsearch = 1000;
map<int, size_t> m = {{0,0}, {1,0}, {2,0}, {3,0}, {4,0}, {5,0}, {6,0}, {7,0}, {8,0}, {9,0}};
auto t1 = chrono::steady_clock::now();
for(size_t iter=0; iter<niter; ++iter) {
for(size_t i=0; i<m.size(); ++i) {
for(int k=0; k<nsearch; ++k) {
m[i] += k;
}
}
}
auto t2 = chrono::steady_clock::now();
map<int, size_t> m2 = {{0,0}, {1,0}, {2,0}, {3,0}, {4,0}, {5,0}, {6,0}, {7,0}, {8,0}, {9,0}};
auto t3 = chrono::steady_clock::now();
for(size_t iter=0; iter<niter; ++iter) {
for(size_t i=0; i<m.size(); ++i) {
size_t& val = m2[i];
for(int k=0; k<nsearch; ++k) {
val += k;
}
}
}
auto t4 = chrono::steady_clock::now();
cout << "time 1: " << chrono::duration_cast<chrono::milliseconds>(t2-t1).count() << " ms" << endl;
cout << "time 2: " << chrono::duration_cast<chrono::milliseconds>(t4-t3).count() << " ms" << endl;
return 0;
}
使用clang对-O3
进行编译,得到以下结果:
time 1: 2915 ms
time 2: 3 ms
同样,对于大多数情况,它可能是微不足道的,但在某些情况下缓存密钥查找会产生很大的影响。
答案 2 :(得分:0)
很难说优化器会做什么,所以通常规则只是为了清晰起见而编写代码。如果您处于内循环中,缓存结构可能会有所帮助,但您需要确定它的时间。