如何在CPU和内存中优化C 中的重型地图插入

How to optimize heavy map insertion in C++ regarding CPU and memory

本文关键字:重型 地图 插入 优化 CPU 内存      更新时间:2023-10-16

我迭代了一张地图,我需要在该地图上添加元素,这取决于找不到元素的条件(可能是其他任何条件)。

我的主要问题是,要添加大量更新,应用程序会采用整个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%。

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

您的问题是相当一般的,但是我看到要使它运行得更快的东西:

  1. 使用提示的插入/安置。当您添加新元素时,其迭代器将返回。假设这两个地图都是以相同的方式订购的,您可以说出最后一个地图在哪里,因此查找应该更快(可以在此处使用一些基准测试)。
  2. 使用emplace_hint进行更快的插入

示例代码在这里:

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(在琐碎键的情况下使用地图而不是unordered_map有任何优势吗?),但是第一个点不再重要了。

我可以找到一个令人满意的答案,这要归功于该问题的评论。从 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和内存)减少,用户时间增加。

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