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

时间:2014-07-12 19:24:58

标签: c++ performance hashmap

我正在制作一款游戏,但我在制作地图时遇到了麻烦。

在每次渲染/更新调用中,我都必须遍历列表中的每个块。 当我移动块边框时,会添加几个块(只需要添加或删除的块)并将其移除到列表中(逐个)。

这需要太长时间,并且会严重影响我的游戏性能。如果我删除更新循环,我的FPS会增加100!

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

(atm列表中有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;
    }
}

我想知道是否有更快的地图,或者我的实施中是否有错误。

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

另一种可能性是使用XYZ的std::vector列表的某种方式;

注意:

散列键:((x&amp; 0xFFFF))&lt;&lt; 16 | ((y&amp; 0xFFFF)&lt;&lt; 8)| (z&amp; 0xFFFF)。 不是很接近完美,因为它只允许从0到255的值,但它现在有效,然后使用字符串更好,因为字符串的性能甚至更差,根据article

编辑:

在评论中注意到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和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并且正在使用调试模式,这样所有代码都运行得非常慢。

3 个答案:

答案 0 :(得分:0)

我的精神力量表明地图中的项目数量超出了您的想象。也许程序中其他地方的错误不是从地图中删除项目。

修改主循环:

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());

现在打印什么?

答案 1 :(得分:0)

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

顺便说一下,我不知道你在代码的其他地方做了什么,但是在addChunk()中你创建了一个新的块,将它保存在rebuildChunks 中,将它放在loadedChunks映射中。但是,在removeChunk()中,您使用loadedChunks [key]中的指针删除块,但您的代码段中没有任何内容可以从rebuildChunks中删除块。如果它还在那里你有一个悬垂的指针...

答案 2 :(得分:-1)

如果迭代速度非常重要,请尝试使用map而不是unordered_map - unordered_map可以更快地查找/插入/删除在整个地图上使用和减慢迭代。