c++(哈希)映射速度优化

C++ (hash) map speed optimization

本文关键字:速度 优化 映射 哈希 c++      更新时间:2023-10-16

我正在制作一款游戏,但是我在地图性能方面遇到了麻烦。

在每次渲染/更新调用中,我必须循环遍历列表中的每个块。当我移动到块边界时,一些块(只是那些需要添加或删除的块)将被添加和删除到列表中(一个接一个)。

这需要花费很长时间,并且会严重影响我的游戏表现。如果我移除更新循环,我的FPS将增加100!

for(pair<const DWORD, Chunk*>& pair : loadedChunks){
    Chunk* c = pair.second;
    if(c != NULL){
        c->update(delta); //Does totally nothing
    }
}

(目前列表中有205个块)

这是我当前的实现:

unordered_map<DWORD, Chunk*> loadedChunks;
void ChunkManager::addChunk(int x, int y, int z){
    Chunk* chunk = new Chunk(this, x, y, z);
    rebuildChunks.push_back(chunk);
    loadedChunks.emplace(((x & 0xFFFF)) << 16 | ((y & 0xFFFF) << 8) | (z & 0xFFFF), chunk);
}
void ChunkManager::removeChunk(int x, int y, int z){
    long key = ((x & 0xFFFF)) << 16 | ((y & 0xFFFF) << 8) | (z & 0xFFFF);
    delete loadedChunks[key];
    loadedChunks.erase(key);
}
Chunk* ChunkManager::getChunkAt(int x, int y, int z){
    if(loadedChunks.find(((x & 0xFFFF)) << 16 | ((y & 0xFFFF) << 8) | (z & 0xFFFF)) != loadedChunks.end()){
        return loadedChunks[((x & 0xFFFF)) << 16 | ((y & 0xFFFF) << 8) | (z & 0xFFFF)];
    }else{
        return NULL;
    }
}

我想知道是否有一个更快的地图,或者如果我在我的实现中做错了什么。

如果这是唯一可能的方法,我愿意使用外部库。

另一种可能性是使用std::vector列表与XYZ;

注意:

哈希键:((x &0 xffff) & lt; & lt;16 | (y &0 xffff) & lt; & lt;8) | (z &0 xffff)。它不是完美的,因为它只允许0 - 255的值,但它现在可以工作,它比使用字符串更好,因为字符串的性能甚至更差,根据这篇文章

.

编辑:

在评论中,有人指出FPS不是一个很好的指标,所以我使用了这个计时器:

unsigned int start = clock();
for(pair<const DWORD, Chunk*>& pair : loadedChunks){
    Chunk* c = pair.second;
    if(c != NULL){
        c->update(delta);
    }
}
unsigned int timepassed = clock() - start;
OutputDebugString((LPCWSTR) wstring(to_wstring(timepassed) + L"n").c_str());

,输出在1 &2毫秒。(这大约是我的渲染所需要的时间…仅用于循环遍历包含205个项目的列表)

编辑2:

我决定通过添加计时器来减少循环的数量:

tickTimer+=delta;
if(tickTimer >= 0.05){
    for(pair<const DWORD, Chunk*>& pair : loadedChunks){
        pair.second->update(delta);
    }
    tickTimer = 0;
}

这样每秒只运行20次。

编辑3:

@brianbeuning有一个非常好的观点,我正在使用MSVS并使用调试模式,这样所有的代码都运行得很慢。

我的超能力告诉我地图上的物品比你想象的要多。可能是程序中其他地方的错误没有从映射中删除项目。

修改主循环:

int count = 0;
int nullcount = 0;
for(pair<const DWORD, Chunk*>& pair : loadedChunks){
    Chunk* c = pair.second;
    if(c != NULL){
        c->update(delta);
        count++;
    }
    else {
        nullcount++;
    }
}
unsigned int timepassed = clock() - start;
OutputDebugString((LPCWSTR) wstring(to_wstring(timepassed) + L"  items:" +  to_wstring(count) + L"   nullitems" +  to_wstring(nullcount) + L"n").c_str());

现在打印什么?

嗯,我不知道你在优化时使用的编译器有多好,但是你在removeChunk()和getChunkAt()中都评估了同样的事情两次。例如,在getChunkAt中,首先应该存储由loadedChunks.find()返回的迭代器,然后在单独的语句中测试它是否为NULL,如果不是,则返回其->second。你可以在removecchunk()中做类似的优化。

顺便说一句,我不知道你在代码的其他地方做什么,但在addChunk()你创建一个新的块,保存在rebuildChunks 把它放在loaddchunks地图。然而,在removeChunk()中,您使用loadedChunks[key]中的指针删除块,但是在您的代码片段中没有任何内容可以从rebuildChunks中删除块。如果它还在,你就有了一个悬浮指针

如果迭代速度很重要,请尝试使用map而不是unordered_mapunordered_map以更大的内存使用和更慢的迭代为代价为您提供更快的查找/插入/删除。