将地图与矢量和缓存影响结合使用

Using map in conjunction with vector and cache impact

本文关键字:影响 结合 缓存 地图      更新时间:2023-10-16

我正在考虑使用map将自定义ID映射到vector索引,例如;

struct Mesh
{
    GLsizei mIndices;
    GLuint mVBO;
    GLuint mIndexBuffer;
    GLuint mVAO;
    size_t vertexDataSize;
    size_t normalDataSize;
};
typedef uint32_t MeshID;
std::map<MeshID, uint16_t> gMeshIDIndexMap;
std::vector<Mesh> gMeshes;

std::vector<MeshID> drawCmds = {1, 1, 2, 3, 4, 5, 8, ,8 ,8, 9, 10, ...};  //sorted
std::for_each(drawCmds.begin(), drawCmds.end(), [](MeshID& id) 
{
    Mesh& mesh = gMeshes[gMeshIDIndexMap.at(id)];
    glBindVertexArray(mesh.mVAO);
    glDrawElements(GL_TRIANGLES, mesh.mIndexBuffer, GL_UNSIGNED_INT, 0);
    glBindVertexArray(0);
    ....  
}     

由于 std::map 不将其元素存储在连续内存中,因此在每次新的迭代中,gMeshIDIndexMap.at(id)都必须将一个全新的缓存行加载到缓存中。这会破坏缓存并导致大量缓存未命中,对吗?我该怎么做才能改进这一点并尽可能少地获得缓存未命中?

如果您 (1( 首先用数据填充地图,然后 (2( 使用它进行查找,(3( 最后当你完成后销毁它,那么boost::container::flat_map可能对你来说是一个不错的选择。它基本上是一个排序向量。优点(无耻地从网站上窃取(:

  • 查找速度比标准关联容器更快
  • 迭代速度比标准关联容器快得多
  • 减少小对象的内存消耗
  • (如果shrink_to_fit,则减少大对象的内存消耗被使用(
  • 改进了缓存性能(数据存储在连续内存中(

潜在的缺点:

  • 最坏情况下的线性时间插入和线性时间删除

观察结果是,容器通常根据上述模式使用(填充数据 - 用于查找 - 销毁(。如果您的用法符合此模式,则排序向量可能是一个不错的选择。

您需要将网格ID放入网格中。如果您知道或者可以对地图中的元素数量给出合理的估计,您甚至可以提前预留空间,这样就不必由于底层矢量的缓冲区重新分配而移动元素。

我的建议与ComicSansMS的回答基本相同;我只建议你使用提升容器,而不是滚动自己的flat_map。


当然,您的使用模式可能如下所示:您执行 (a( 查找或 (b( 在随机位置删除或 (c( 在随机位置插入;在地图的整个生命周期中,您执行 (a( 或 (b( 或 (c( 看似随机的操作。在这种情况下,基于节点的容器(如 std::map(可能是更好的选择。