IT技术博客大学习 共学习 共进步
全部 移动开发 后端 数据库 AI 算法 安全 DevOps 前端 设计 开发者

关于哈希map奇慢无比的原因定位

Vimer 2011-01-27 22:55:08 累计浏览 4,901 次
本机暂存

最近有一个server在重启的时候总要花费5分钟左右来加载配置文件,导致外网服务不可用,今天和几个同事一起研究了一下,总算找到了问题所在.
抽象出代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
#include <sys/time.h>
#include <stdio.h>
#include <memory.h>
#include <map>
#include <string>
 
#if 0
#include <hash_map.h>
#else
#include <tr1/unordered_map>
#define hash_map std::tr1::unordered_map
#endif
 
using namespace std;
 
class CTimer
{
public:
    CTimer()
    {
        memset(&tpStart, 0, sizeof(tpStart));
        memset(&tpEnd, 0, sizeof(tpEnd));
    }
    void Begin()
    {
        gettimeofday(&tpStart, NULL);
    }
    float GetElapseTime()
    {
 
        gettimeofday(&tpEnd, NULL);
 
        float timecost = 0.0f;
        timecost = tpEnd.tv_sec - tpStart.tv_sec + (float)(tpEnd.tv_usec-tpStart.tv_usec)/1000000;
 
        return timecost;
    }
private:
    struct timeval tpStart;
    struct timeval tpEnd;
};
 
struct StTest
{
    int dd;
 
    size_t operator()()const
    {
        return dd;
    }
    bool operator == (const StTest &lhs)const
    {
        return this->dd == lhs.dd;
    }
    bool operator()(const StTest &lhs)const
    {
        return this->dd == lhs.dd;
    }
};
 
const int cnt = 40000;
int main()
{
    hash_map<int, int> imap1;
    hash_map<StTest, int, StTest> StTestMap1;
 
 
    CTimer t1;
    t1.Begin();
    for (size_t i=0; i<cnt; ++i)
    {
        imap1[i] = i;
    }
    printf("cost time : %5.5f\n", t1.GetElapseTime());
 
    StTest st;
    t1.Begin();
    for (size_t i=0; i<cnt; ++i)
    {
        st.dd = i;
        StTestMap1[st] = i;
    }
    printf("StTestMap1 time cost : %5.5f\n",t1.GetElapseTime());
 
    return 0;
}

对imap1和StTestMap1两个map分别插入40000次,运行结果如下:

cost time : 0.02598
StTestMap1 time cost : 27.84836

StTestMap1的插入居然消耗了27秒,太奇怪了!难道真的hash_map的实现有问题?我们来换成map试一下.
先重载一下StTest的小于符号:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
struct StTest
{
    int dd;
 
    size_t operator()()const
    {
        return dd;
    }
    bool operator == (const StTest &lhs)const
    {
        return this->dd == lhs.dd;
    }
    bool operator()(const StTest &lhs)const
    {
        return this->dd == lhs.dd;
    }
    bool operator < (const StTest &lhs)const
    {
        return this->dd < lhs.dd;
    }
};

两个map的定义改为:

1
2
map<int, int> imap1;
map<StTest, int> StTestMap1;

运行结果如下:

cost time : 0.04806
StTestMap1 time cost : 0.06981

用map就没问题,所以怀疑到了哈希函数的身上,我们发现StTest的哈希函数居然是如下定义:

1
2
3
4
bool operator()(const StTest &lhs)const
{
    return this->dd == lhs.dd;
}

bool型,一直返回1!也就是说所有的节点都放到了一个桶里!
找到了原因,我们来改一下代码:

1
2
3
4
5
6
7
8
9
10
11
12
struct StTest
{
    int dd;
    bool operator == (const StTest &lhs)const
    {
        return this->dd == lhs.dd;
    }
    size_t operator()(const StTest &lhs)const
    {
        return lhs.dd;
    }
};

运行结果如下:

cost time : 0.02406
StTestMap1 time cost : 0.02213

结果正常了,可能由于之前的同事错把

1
2
3
4
size_t operator()()const
{
    return dd;
}

当成哈希函数,所以才会出现如下问题。使用哈希map的时候,哈希函数的离散情况是很关键的,希望这篇文章能帮助大家避免犯类似的错误。
另外用红黑树map其实大部分情况够用了,不必过分追求效率。
另:
代码下载

同分类推荐文章

  1. 等了十年的 Go 链式管道,终于来了:seq 让你像写 Scala 一样写 Go (2026-06-25 18:38:18)
  2. Go 实验特性详解 (2026-06-21 10:05:27)
  3. amd64 微架构级别对 Go 程序性能提升多少? (2026-06-21 09:38:49)

查看更多 后端 文章 →

建议继续学习

  1. Fix Bug的五个阶段 (累计阅读 42,973)
  2. Java开发岗位面试题归类汇总 (累计阅读 22,155)
  3. 记录一个软中断问题 (累计阅读 16,954)
  4. 调试工具之GDB (累计阅读 14,829)
  5. Go Reflect 性能 (累计阅读 14,155)
  6. HashMap解决hash冲突的方法 (累计阅读 12,654)
  7. gdb的基本工作原理是什么? (累计阅读 11,682)
  8. 深入理解Nginx之调试优化技巧 (累计阅读 8,227)
  9. 内存越界的概念和调试方法 (累计阅读 7,277)
  10. webapp网页调试工具Chrome Devtools (累计阅读 6,984)