如何优化C ++中有关CPU和内存的重图插入

时间:2017-09-08 13:16:59

标签: c++ c++11 unordered-map

我正在迭代一个地图,我需要在该地图上添加元素,具体取决于未找到元素的条件(可能是任何其他条件)。

我的主要问题是,要添加大量更新,应用程序将占用整个CPU和所有内存。

州级:

class State {

    int id;

    int timeStamp;

    int state;

}

状态方法:

void State::updateStateIfTimeStampIsHigher(const State& state) {
    if (this->id == state.getId() && state.getTimeStamp() > this->getTimeStamp()) {
        this->timeStamp = state.getTimeStamp();
        this->state = state.getState();
    }
}

循环代码:

std::map<int, State> data;

const std::map<int, State>& update;

for (auto const& updatePos : update) {
    if (updatePos.first != this->toNodeId) {
        std::map<int, State>::iterator message = data.find(updatePos.first);
        if (message != data.end() && message->first) {
            message->second.updateStateIfTimeStampIsHigher(updatePos.second);
        } else {
            data.insert(std::make_pair(updatePos.first, updatePos.second));
        }
    }
}

观察KCacheGrind数据看起来像 data.insert()行需要大部分时间/内存。我是KCacheGrind的新手,但这条线似乎约占成本的72%。

您对如何改善这一点有什么建议吗?

2 个答案:

答案 0 :(得分:1)

你的问题非常笼统,但我看到了让它运行得更快的事情:

  1. 使用暗示插入/放置。添加新元素时,将返回其迭代器。假设两个地图以相同的方式排序,你可以知道最后一个地图的插入位置,因此查找应该更快(可以在这里使用一些基准测试)。
  2. 使用emplace_hint加快插入速度
  3. 示例代码:

    std::map<int, long> data;
    
    const std::map<int, long> update;
    auto recent = data.begin();
    
    for (auto const& updatePos : update) {
        if (updateElemNotFound) { 
            recent = data.emplace_hint(recent, updatePos);
        }
    }
    

    另外,如果你想用内存交换CPU,可以使用unordered_map(Is there any advantage of using map over unordered_map in case of trivial keys?),但第一个点不再重要了。

答案 1 :(得分:0)

通过研究对问题的评论,我能找到一个令人满意的答案。它确实有助于从 map 更改为 unordered_map ,但我仍然得到了令人不满意的结果。

我最终使用Google的sparsehash提供了更好的资源使用,尽管擦除条目存在一些缺点(我这样做)。

代码解决方案如下。首先,我包括所需的库:

#include <sparsehash/sparse_hash_map>

然后,我的新数据定义如下:

struct eqint {
    bool operator()(int i1, int i2) const {
        return i1 == i2;
    }
};

google::sparse_hash_map<int, State, std::tr1::hash<int>, eqint> data;

由于我必须使用“擦除”,所以我必须在 sparsemap 构造之后执行此操作:

data.clear_deleted_key();
data.set_deleted_key(-1);

最后我的循环代码变化很小:

for (auto const& updatePos : update) {
    if (updatePos.first != this->toNodeId) {
        google::sparse_hash_map<int, State, std::tr1::hash<int>, eqint>::iterator msgIt = data.find(updatePos.first);
        if (msgIt != data.end() && msgIt->first) {
            msgIt->second.updateStateIfTimeStampIsHigher(updatePos.second);
        } else {
            data[updatePos.first] = updatePos.second;
        }
    }
}

之前在特定参数下运行整个应用程序的更改的时间是:

real    0m28,592s
user    0m27,912s
sys     0m0,676s

相同的特定参数下运行整个应用程序的更改之后的时间是:

real    0m37,464s
user    0m37,032s
sys     0m0,428s

我用其他案例运行它,结果类似(从定性的角度来看)。系统时间和资源使用(CPU和内存)减少,用户时间增加。

总体而言,我对这种权衡感到满意,因为我更关注资源使用而不是执行时间(应用程序是一个模拟器,它无法完成并在非常重的负载下获得结果,现在确实如此)。