填充unordered_set太慢了

时间:2013-07-10 08:46:07

标签: c++ performance unordered-set

我们有一个给定的3D网格,我们试图消除相同的顶点。为此,我们使用一个自定义的结构,包含顶点坐标和相应的法线。

    struct vertice
    {
        float p1,p2,p3,n1,n2,n3;

        bool operator == (const vertice& vert) const
        {
            return (p1 == vert.p1 && p2 == vert.p2 && p3 == vert.p3);
        }
    };

用数据填充顶点后,会将其添加到unordered_set中以删除重复项。

    struct hashVertice
    {
        size_t operator () (const vertice& vert) const
        {
            return(7*vert.p1 + 13*vert.p2 + 11*vert.p3);
        }
    };

    std::unordered_set<vertice,hashVertice> verticesSet;

    vertice vert;

    while(i<(scene->mMeshes[0]->mNumVertices)){

            vert.p1 = (float)scene->mMeshes[0]->mVertices[i].x;
            vert.p2 = (float)scene->mMeshes[0]->mVertices[i].y;
            vert.p3 = (float)scene->mMeshes[0]->mVertices[i].z;

            vert.n1 = (float)scene->mMeshes[0]->mNormals[i].x;
            vert.n2 = (float)scene->mMeshes[0]->mNormals[i].y;
            vert.n3 = (float)scene->mMeshes[0]->mNormals[i].z;

            verticesSet.insert(vert);

            i = i+1;
    }

我们发现它对于像3.000.000顶点这样的数据量来说太慢了。即使运行15分钟后程序也没有完成。是否存在我们未看到的瓶颈,或者是否有更好的数据结构用于此类任务?

3 个答案:

答案 0 :(得分:6)

如果您只是从循环中移除verticesSet.insert(vert);会怎样?

如果它显着加速(正如我预期的那样),你的瓶颈就在std::unordered_set的内部,这是一个哈希表,哈希表的主要潜在性能问题是在那里是过多的哈希冲突

在您当前的实施中,如果p1p2p3 ,则不同哈希码的数量会很小(因为您“崩溃” “浮动到整数”并且会有很多碰撞。

如果上述假设结果为真,我会尝试以不同的方式实现散列函数(例如,乘以更大的系数)。


除此之外,如其他人已经建议的那样,对您的代码进行概要分析。

答案 1 :(得分:1)

散列浮点可能很棘手。特别是你的哈希 例程将哈希计算为浮点值 将其转换为无符号整数类型。这很严重 如果顶点可以很小的问题:如果所有的顶点 在[0...1.0)范围内,例如,您的哈希函数 永远不会返回任何大于13的东西。作为一个未签名的 整数,表示最多有13个不同 哈希码。

散列浮点的常用方法是对二进制进行散列 图像,首先检查特殊情况。 (0.0-0.0 有不同的二进制图像,但必须散列相同。它就是 一个开放的问题你用NaN s做了什么。)对于float这是 特别简单,因为它通常具有相同的尺寸 int,您可以reinterpret_cast

size_t
hash( float f )
{
    assert( /* not a NaN */ );
    return f == 0.0 ? 0.0 : reinterpret_cast( unsigned& )( f );
}

我知道,正式来说,这是未定义的行为。但如果漂浮和 int具有相同的大小,unsigned没有陷印 表示(大多数通用机器上的情况) 今天),然后是一个错误的编译器 故意迟钝。

然后使用任何组合算法合并三个结果; 你使用的那个和其他任何一个一样好(在这种情况下 - 它是 不是一个好的通用算法。)

我可能会补充说,虽然有些评论坚持要进行分析 (这通常是一个很好的建议),如果你需要15分钟 对于300万个值,问题实际上只是一个糟糕的哈希 功能,导致大量的碰撞。没有别的 导致表现不佳。除非你熟悉 通常的std::unordered_set的内部实现 profiler输出可能不会给你提供太多信息。 另一方面,std::unordered_set确实具有功能 例如bucket_countbucket_size,允许分析 哈希函数的质量。在你的情况下,如果你不能 创建一个包含300万个条目的unordered_set,这是您的第一个条目 步骤应该是创建一个小得多的,并使用这些 用于评估哈希码的质量。

答案 2 :(得分:0)

如果存在瓶颈,你肯定没有看到它,因为你没有包括任何时间措施。

使用分析器或仅手动测量算法的时间。这将让你找到瓶颈 - 如果有的话。

这是正确的进行方式。根据我的经验,期待自己,或者StackOverflow用户通过眼睛检查发现瓶颈,而不是实际测量程序中的时间,这是优化尝试失败的最常见原因。