我想在concurentDictionary
中使用字节数组作为查找键。
目前,我使用自定义EqualityComparer<byte[]>
来解决此问题。
这很好用,但我确实认识到我的哈希码生成器会产生很多重叠,事情最终会在同一个哈希桶中。
public class ByteArrayEqualityComparer : EqualityComparer<byte[]>
{
public override bool Equals(byte[] x, byte[] y)
{
//fast buffer compare
return UnsafeCompare(x, y);
}
public override int GetHashCode(byte[] obj)
{
int hash = 0;
for (int i = 0; i < obj.Length; i += 2)
{
hash += obj[i]; //xor? shift? black magic?
}
return hash;
}
}
从字节数组创建相对快速的哈希是一个很好的公式?
我的想法是我可以通过跳过每个x字节的速度来计算哈希码。 由于最终的比较仍在整个数据集上进行,因此多次比较所有字节似乎毫无意义。
我认为一些xor魔法和改变哈希变量会使事情变得更好。
这对性能至关重要,因此也欢迎任何可以使用的快捷方式。
[编辑] 我最终使用了这个解决方案。 我使用结构来包装字节数组,以便我可以使用缓存的哈希码,而不是为每次比较计算它。 这导致了非常好的性能提升。
public struct ByteArrayKey
{
public readonly byte[] Bytes;
private readonly int _hashCode;
public override bool Equals(object obj)
{
var other = (ByteArrayKey) obj;
return Compare(Bytes, other.Bytes);
}
public override int GetHashCode()
{
return _hashCode;
}
private static int GetHashCode([NotNull] byte[] bytes)
{
unchecked
{
var hash = 17;
for (var i = 0; i < bytes.Length; i++)
{
hash = hash*23 + bytes[i];
}
return hash;
}
}
public ByteArrayKey(byte[] bytes)
{
Bytes = bytes;
_hashCode = GetHashCode(bytes);
}
public static ByteArrayKey Create(byte[] bytes)
{
return new ByteArrayKey(bytes);
}
public static unsafe bool Compare(byte[] a1, byte[] a2)
{
if (a1 == null || a2 == null || a1.Length != a2.Length)
return false;
fixed (byte* p1 = a1, p2 = a2)
{
byte* x1 = p1, x2 = p2;
var l = a1.Length;
for (var i = 0; i < l/8; i++, x1 += 8, x2 += 8)
if (*(long*) x1 != *(long*) x2) return false;
if ((l & 4) != 0)
{
if (*(int*) x1 != *(int*) x2) return false;
x1 += 4;
x2 += 4;
}
if ((l & 2) != 0)
{
if (*(short*) x1 != *(short*) x2) return false;
x1 += 2;
x2 += 2;
}
if ((l & 1) != 0) if (*x1 != *x2) return false;
return true;
}
}
}
答案 0 :(得分:1)
哈希的更好选择可能是这样的:
public override int GetHashCode(byte[] obj)
{
int hash = 0;
for (int i = 0; i < obj.Length; i++)
{
exponents = [0, 8, 16, 24];
exponent = exponents[i % 4];
unchecked
{
hash += obj[i] * (1 << i);
}
}
return hash;
}
从概念上讲,这会将每个4字节的块转换为int,因为它们都是32位,然后将它们与标准整数溢出相加。因此,长度为4或更小的所有唯一字节数组将映射到不同的哈希码,并且(给定随机数据)较大的数组应该在哈希空间中很好地分布。如果你期望有很多非常相似的数组,或者每4个重复一次的数组,这可能不是最好的策略。
答案 1 :(得分:0)
MurmurHash非常快且非常简单。有许多基于.NET的实现,但我不知道它们的性能如何。