【问题标题】:Timed vector vs map vs unordered_map lookup定时向量 vs 地图 vs unordered_map 查找
【发布时间】:2013-01-30 01:08:34
【问题描述】:

我对矢量查找和地图查找很好奇,并为它编写了一个小测试程序。它似乎矢量总是比我使用它的方式更快。。还有什么我应该考虑的吗?测试是否有任何偏见?运行结果在底部.. 以纳秒为单位,但 gcc 在我的平台上似乎不支持它。

使用字符串进行查找当然会改变很多。

我使用的编译行是这样的:g++ -O3 --std=c++0x -o lookup lookup.cpp

#include <iostream>
#include <vector>
#include <map>
#include <unordered_map>
#include <chrono>
#include <algorithm>

unsigned dummy = 0;

class A
{
public:
    A(unsigned id) : m_id(id){}

    unsigned id(){ return m_id; }
    void func()
    {
        //making sure its not optimized away
        dummy++;
    }
private:
    unsigned m_id;
};

class B
{
public:
    void func()
    {
        //making sure its not optimized away
        dummy++;
    }
};

int main()
{
    std::vector<A> v;
    std::unordered_map<unsigned, B> u;
    std::map<unsigned, B> m;

    unsigned elementCount = 1;

    struct Times
    {
        unsigned long long v;
        unsigned long long u;
        unsigned long long m;
    };
    std::map<unsigned, Times> timesMap;

    while(elementCount != 10000000)
    {
        elementCount *= 10;
        for(unsigned i = 0; i < elementCount; ++i)
        {
            v.emplace_back(A(i));
            u.insert(std::make_pair(i, B()));
            m.insert(std::make_pair(i, B()));
        }


        std::chrono::time_point<std::chrono::steady_clock> start = std::chrono::high_resolution_clock::now();
        for(unsigned i = 0; i < elementCount; ++i)
        {
            auto findItr = std::find_if(std::begin(v), std::end(v),
                                        [&i](A & a){ return a.id() == i; });

            findItr->func();
        }
        auto tp0 = std::chrono::high_resolution_clock::now()- start;
        unsigned long long vTime = std::chrono::duration_cast<std::chrono::nanoseconds>(tp0).count();

        start = std::chrono::high_resolution_clock::now();
        for(unsigned i = 0; i < elementCount; ++i)
        {
            u[i].func();
        }
        auto tp1 = std::chrono::high_resolution_clock::now()- start;
        unsigned long long uTime = std::chrono::duration_cast<std::chrono::nanoseconds>(tp1).count();

        start = std::chrono::high_resolution_clock::now();
        for(unsigned i = 0; i < elementCount; ++i)
        {
            m[i].func();
        }
        auto tp2 = std::chrono::high_resolution_clock::now()- start;
        unsigned long long mTime = std::chrono::duration_cast<std::chrono::nanoseconds>(tp2).count();

        timesMap.insert(std::make_pair(elementCount ,Times{vTime, uTime, mTime}));
    }

    for(auto & itr : timesMap)
    {
        std::cout << "Element count: " << itr.first << std::endl; 
        std::cout << "std::vector time:        " << itr.second.v << std::endl;
        std::cout << "std::unordered_map time: " << itr.second.u << std::endl;
        std::cout << "std::map time:           " << itr.second.m << std::endl;
        std::cout << "-----------------------------------" << std::endl;
    }

    std::cout << dummy;
}

./lookup 
Element count: 10
std::vector time:        0
std::unordered_map time: 0
std::map time:           1000
-----------------------------------
Element count: 100
std::vector time:        0
std::unordered_map time: 3000
std::map time:           13000
-----------------------------------
Element count: 1000
std::vector time:        2000
std::unordered_map time: 29000
std::map time:           138000
-----------------------------------
Element count: 10000
std::vector time:        22000
std::unordered_map time: 287000
std::map time:           1610000
-----------------------------------
Element count: 100000
std::vector time:        72000
std::unordered_map time: 1539000
std::map time:           8994000
-----------------------------------
Element count: 1000000
std::vector time:        746000
std::unordered_map time: 12654000
std::map time:           154060000
-----------------------------------
Element count: 10000000
std::vector time:        8001000
std::unordered_map time: 123608000
std::map time:           2279362000
-----------------------------------
33333330

【问题讨论】:

  • 标准库容器对操作时间没有任何限制,只有复杂性。您可以查看参考 such as this one 以了解不同容器的复杂性限制。
  • 我不太清楚你的意思?
  • 顺便说一句。在上面的帖子中添加了编译行
  • 只是为了搞笑,在 -O0 运行它
  • 这是因为 1000 万个项目上的 O(N*N) 可笑不出来

标签: c++ map vector unordered-map


【解决方案1】:

我一点也不震惊矢量测试比其他任何东西都好。它的 asm 代码(实际反汇编)分解为这个(在我的 Apple LLVM 4.2 上完全选择):

0x100001205:  callq  0x100002696               ; symbol stub for: std::__1::chrono::steady_clock::now()
0x10000120a:  testl  %r13d, %r13d
0x10000120d:  leaq   -272(%rbp), %rbx
0x100001214:  je     0x100001224               ; main + 328 at main.cpp:78
0x100001216:  imull  $10, %r14d, %ecx
0x10000121a:  incl   7896(%rip)                ; dummy
0x100001220:  decl   %ecx
0x100001222:  jne    0x10000121a               ; main + 318 [inlined] A::func() at main.cpp:83
main + 318 at main.cpp:83
0x100001224:  movq   %rax, -280(%rbp)
0x10000122b:  callq  0x100002696               ; symbol stub for: std::__1::chrono::

注意“循环”(jne 0x10000121a)。 “find_if”已被完全优化,结果实际上是使用递减寄存器对数组进行扫描,以计算增加全局的次数。 这就是正在做的一切;没有进行任何类型的搜索。

是的,这就是你使用它的方式。

【讨论】:

  • 我不习惯阅读反汇编,这是什么部分的代码?
  • 这实际上是第一个时钟开始和第一个时钟停止之间的组合。 (注意右侧的 cmets 作为提示)。然而,循环的核心是0x10000121a - 0x100001222,只不过是一个增量,然后是计数器的减量,然后测试零是否再次跳转。换句话说,除了增量之外,几乎所有内容都已完全优化。
  • 是的,对.. 当我使用 -O0 时事情发生了变化.. 但是容器的优化也可能不同..
  • @bitgregor 但话说回来,没有人关心非优化代码中的任何时间测量。而是搜索随机值而不是增量值,或者只使用 dummy += m_id 而不是 ++dummy
  • 是的,我添加了随机值,现在我也做了 dummy += m_id .. 更新了主帖中的版本。
【解决方案2】:

首先,您似乎没有在测试之间清除您的容器。所以他们不包含你认为他们所做的事情。

其次,根据您的时间,您的向量表现出线性时间,这是不可能的,因为在您的算法中复杂度为 O(N*N)。可能它被优化了。我建议不要尝试与优化作斗争,而是将其关闭。

第三,你的值对于向量来说太容易预测了。这会对其产生巨大影响。尝试随机值(或 random_shuffle())

【讨论】:

  • 是的,但是使用 -O0 进行优化不会使测试产生偏差吗?由于不同的容器可能在内部进行了不同的优化?
  • 不确定 GCC,但在 MSVC 中,可以将部分代码包装到特定的优化设置中。您将需要优化包含标题的部分,但不是您的代码。
猜你喜欢
  • 2011-05-29
  • 1970-01-01
  • 2022-08-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-12-26
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多