我一直在编写图像处理算法,并且在某些时候我需要收集一些有关转换像素的统计信息,以获得更多关于我应该跟随进一步开发的方向的更多信息。我需要收集什么信息是格式为:可悲的unordered_map插入性能/散列函数
key: RGB value
value: int
我做了什么,被我打开转换后的图像,并通过它迭代,节省了我需要std::unordered_map
具有以下特征值:
typedef std::unordered_map<boost::gil::rgb8_pixel_t, unsigned int> pixel_map_t;
在一个循环:
for(int y = 0; y < vi.height(); y++) {
SrcView::x_iterator dst_it = src.row_begin(y);
for(int x = 0; x < vi.width(); x++, hits++) {
diff_map.insert(std::make_pair(dst_it[x], /* some uint32 */));
}
我也写一个自定义的散列函数(这是一个完美的哈希函数:256^2 x R + 256 x G + B
- 所以科利斯离子应该是最小的,而不管桶和散列表的布局如何(在合理的范围内)。
我注意到的是插入太慢了! - 到达第11次迭代后,插入速度降低了大约100倍。我有大量的碰撞! 尽管图像中有很少的重复颜色。
之后,我想消除我的代码中的任何可能的错误,并开始使用带有原始键类型(如int)的STL哈希函数对unordered_map
进行基准测试。
为基准的代码是:
std::size_t hits = 0, colls = 0;
for(int y = 0; y < vi.height(); y++) {
SrcView::x_iterator dst_it = src.row_begin(y);
for(int x = 0; x < vi.width(); x++, hits++) {
if(diff_map.find(x*y) != diff_map.cend())
colls++;
diff_map.insert(std::make_pair(x*y, 10));
}
std::cout << y << "/" << vi.height() << " -> buckets: "
<< diff_map.bucket_count() << "("
<< std::floor(diff_map.load_factor() * 100)
<< "% load factor) [ " << colls << " collisions/" << hits << " hits ]" << std::endl;
}
...这里是外循环的第一个20次迭代的结果(仅使用STL的散列函数对于int类型的键):
0/480 -> buckets: 8(12% load factor) [ 639 collisions/640 hits ]
1/480 -> buckets: 4096(15% load factor) [ 640 collisions/1280 hits ]
2/480 -> buckets: 4096(23% load factor) [ 960 collisions/1920 hits ]
3/480 -> buckets: 4096(31% load factor) [ 1281 collisions/2560 hits ]
4/480 -> buckets: 4096(37% load factor) [ 1654 collisions/3200 hits ]
5/480 -> buckets: 4096(45% load factor) [ 1964 collisions/3840 hits ]
6/480 -> buckets: 4096(51% load factor) [ 2370 collisions/4480 hits ]
7/480 -> buckets: 4096(59% load factor) [ 2674 collisions/5120 hits ]
8/480 -> buckets: 4096(65% load factor) [ 3083 collisions/5760 hits ]
9/480 -> buckets: 4096(71% load factor) [ 3460 collisions/6400 hits ]
10/480 -> buckets: 4096(77% load factor) [ 3872 collisions/7040 hits ]
11/480 -> buckets: 4096(85% load factor) [ 4161 collisions/7680 hits ]
12/480 -> buckets: 4096(90% load factor) [ 4612 collisions/8320 hits ]
13/480 -> buckets: 4096(99% load factor) [ 4901 collisions/8960 hits ]
14/480 -> buckets: 32768(13% load factor) [ 5315 collisions/9600 hits ]
15/480 -> buckets: 32768(13% load factor) [ 5719 collisions/10240 hits ]
16/480 -> buckets: 32768(14% load factor) [ 6148 collisions/10880 hits ]
17/480 -> buckets: 32768(15% load factor) [ 6420 collisions/11520 hits ]
18/480 -> buckets: 32768(16% load factor) [ 6870 collisions/12160 hits ]
19/480 -> buckets: 32768(17% load factor) [ 7135 collisions/12800 hits ]
20/480 -> buckets: 32768(17% load factor) [ 7584 collisions/13440 hits ]
21/480 -> buckets: 32768(18% load factor) [ 7993 collisions/14080 hits ]
这种情况下碰撞的数量是不是太高?一般来说,STL库的质量很高,但对于简单的基于int的关键声音,至少有奇怪的是639/640和640/1280。 或者我做错了什么?
这是我的散列函数(理论上,应该没有冲突可言 - 但数字非常接近):
template<>
struct std::hash<boost::gil::rgb8_pixel_t> :
public std::unary_function<const boost::gil::rgb8_pixel_t&, size_t>
{
size_t operator()(const boost::gil::rgb8_pixel_t& key) const
{
size_t ret = (static_cast<size_t>(key[0]) << 16) |
(static_cast<size_t>(key[1]) << 8) |
(static_cast<size_t>(key[2]));
//return 256 * 256 * key[0] + 256 * key[1] + key[2];
return ret;
}
};
现在,这是不好笑了。 ..
我写了这个哈希函数:
template<>
struct std::hash<int> :
public std::unary_function<const int&, size_t>
{
size_t operator()(const int& key) const
{
return 5;
}
};
理论上,我应该有100%的碰撞率,对吧?但结果如下:
0/480 -> buckets: 8(12% load factor) [ 639 collisions/640 hits ]
1/480 -> buckets: 4096(15% load factor) [ 640 collisions/1280 hits ]
2/480 -> buckets: 4096(23% load factor) [ 960 collisions/1920 hits ]
3/480 -> buckets: 4096(31% load factor) [ 1281 collisions/2560 hits ]
4/480 -> buckets: 4096(37% load factor) [ 1654 collisions/3200 hits ]
5/480 -> buckets: 4096(45% load factor) [ 1964 collisions/3840 hits ]
6/480 -> buckets: 4096(51% load factor) [ 2370 collisions/4480 hits ]
7/480 -> buckets: 4096(59% load factor) [ 2674 collisions/5120 hits ]
8/480 -> buckets: 4096(65% load factor) [ 3083 collisions/5760 hits ]
9/480 -> buckets: 4096(71% load factor) [ 3460 collisions/6400 hits ]
为什么?
ENV:MSVS2010
你的基准是有缺陷的。对于y == 0,您插入640次'std :: make_pair(x * y,10)',它是'make_pair(0,10)'。预计有639次碰撞。没有更进一步,假设你的代码的残留代码同样有问题 – hirschhornsalz 2011-05-22 00:22:28
除非你有一个2^24存储桶散列表,否则你提供的散列表距离完美的散列函数很远。对于任何两个幂的表(似乎是这种情况),你正在做的是忽略红色部分和部分绿色。如果你有一个包含所有红色阴影的图像,所有的像素将会发生碰撞,例如... – 2011-05-22 01:05:52
(((r + 2)^ 37)%4294967291)^(((g + 2)^ 37)%4294967291 )^(((b + 2)^ 37)%4294967291)会很慢,但会导致出色的混合。 :-) – Omnifarious 2011-05-22 05:25:22