C ++ map:每次创建临时变量或使用键访问值是否更快

时间:2017-02-06 15:38:41

标签: c++ dictionary key-value

哪个更快:

(*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 ++的初学者)

3 个答案:

答案 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)

很难说优化器会做什么,所以通常规则只是为了清晰起见而编写代码。如果您处于内循环中,缓存结构可能会有所帮助,但您需要确定它的时间。