【发布时间】:2012-02-02 09:18:35
【问题描述】:
我读过this question 和another question。在这两个问题之一中,我还读到 Guid Structure 由以下四个字段组成:Int32、Int16、Int16 和 Byte[8],因此两个 Guid 之间的比较应该更快。
好吧,我只使用 Guid 结构来生成 UUID,然后我必须只比较那些以前生成的 UUID。因此,我想将快速生成的每个 UUID 转换为可比较的格式。
我使用以下代码运行了一些测试(我的灵感来自this question)。
struct FastUuid
{
public long _part1;
public long _part2;
public FastUuid(byte[] value)
{
_part1 = BitConverter.ToInt64(value, 0);
_part2 = BitConverter.ToInt64(value, 8);
}
}
static void Main(string[] args)
{
TestUuidCompare(1000000000);
Console.ReadLine();
}
public static void TestUuidCompare(uint numberOfChecks)
{
Guid uuid1 = Guid.NewGuid();
Guid uuid2 = new Guid(uuid1.ToByteArray());
byte[] a1 = uuid1.ToByteArray();
byte[] a2 = uuid2.ToByteArray();
string s1 = uuid1.ToString();
string s2 = uuid2.ToString();
BigInteger b1 = new BigInteger(uuid1.ToByteArray());
BigInteger b2 = new BigInteger(uuid2.ToByteArray());
long l1part1 = BitConverter.ToInt64(uuid1.ToByteArray(), 0); // Parts 1 and 2
long l1part2 = BitConverter.ToInt64(uuid1.ToByteArray(), 8); // of uuid1.
long l2part1 = BitConverter.ToInt64(uuid2.ToByteArray(), 0); // Parts 1 and 2
long l2part2 = BitConverter.ToInt64(uuid2.ToByteArray(), 8); // of uuid2.
long[] la1 = { l1part1, l1part2 }; // Parts 1 and 2 of uuid1.
long[] la2 = { l2part1, l2part2 }; // Parts 1 and 2 of uuid2.
int i1part1 = BitConverter.ToInt32(uuid1.ToByteArray(), 0); // Parts 1, 2, 3
int i1part2 = BitConverter.ToInt32(uuid1.ToByteArray(), 4); // and 4
int i1part3 = BitConverter.ToInt32(uuid1.ToByteArray(), 8); // of
int i1part4 = BitConverter.ToInt32(uuid1.ToByteArray(), 12); // uuid1.
int i2part1 = BitConverter.ToInt32(uuid2.ToByteArray(), 0); // Parts 1, 2, 3
int i2part2 = BitConverter.ToInt32(uuid2.ToByteArray(), 4); // and 4
int i2part3 = BitConverter.ToInt32(uuid2.ToByteArray(), 8); // of
int i2part4 = BitConverter.ToInt32(uuid2.ToByteArray(), 12); // uuid2
FastUuid fast1 = new FastUuid(uuid1.ToByteArray());
FastUuid fast2 = new FastUuid(uuid2.ToByteArray());
// UUID are equal (worse scenario)
Stopwatch sw = new Stopwatch();
bool result;
sw.Reset(); sw.Start();
for (int i = 0; i < numberOfChecks; i++)
{
result = (uuid1 == uuid2);
}
Console.WriteLine("- Guid.Equals: \t{0}", sw.Elapsed);
sw.Reset(); sw.Start();
for (int i = 0; i < numberOfChecks; i++)
{
result = Array.Equals(a1, a2);
}
Console.WriteLine("- Array.Equals(byte): \t{0}", sw.Elapsed);
sw.Reset(); sw.Start();
for (int i = 0; i < numberOfChecks; i++)
{
result = s1.Equals(s2);
}
Console.WriteLine("- String.Equals: \t{0}", sw.Elapsed);
sw.Reset(); sw.Start();
for (int i = 0; i < numberOfChecks; i++)
{
result = b1.Equals(b2);
}
Console.WriteLine("- BigInteger.Equals: \t{0}", sw.Elapsed);
sw.Reset(); sw.Start();
for (int i = 0; i < numberOfChecks; i++)
{
result = (l1part1 == l2part1 && l1part2 == l2part2);
}
Console.WriteLine("- Two long compare: \t{0}", sw.Elapsed);
sw.Reset(); sw.Start();
for (int i = 0; i < numberOfChecks; i++)
{
result = Array.Equals(la1, la2);
}
Console.WriteLine("- Array.Equals(long): \t{0}", sw.Elapsed);
sw.Reset(); sw.Start();
for (int i = 0; i < numberOfChecks; i++)
{
result = (i1part1 == i2part1 && i1part2 == i2part2 && i1part3 == i2part3 && i1part4 == i2part4);
}
Console.WriteLine("- Four int compare: \t{0}", sw.Elapsed);
sw.Reset(); sw.Start();
for (int i = 0; i < numberOfChecks; i++)
{
result = fast1.Equals(fast2);
}
Console.WriteLine("- FastUuid: \t{0}", sw.Elapsed);
}
有以下结果。
- Guid.Equals:18.911 秒
- Array.Equals(byte): 12.003 s
- String.Equals:26.159 秒
- BigInteger.Equals:22.652 秒
- 两个长比较:6.530 s
- Array.Equals(long): 11.930 s
- 四个整数比较:6.795 秒
- FastUuid:1m 26.636 s
为什么 FastUuid 比较最慢?
既然 UUID 应该是 Dictionary 的关键,有没有办法得到两个 long 之间的性能比较,同时用一个小的(16 字节)对象/结构来实现 UUID?
编辑: 实际上,我执行的测试是衡量两个 UUID 之间的比较性能,因此它们对评估访问 Dictionary 的性能几乎没有意义,因为当我调用 ContainsKey method 时,它会计算 UUID 的哈希值。 我应该评估计算 Guid、String、FastUuid 等的哈希值的性能吗? ContainsKey 方法是如何工作的?
【问题讨论】:
-
致对 OP 问题投反对票的人,为什么?建设性的反馈总是受欢迎的!
-
你的测试很有问题。您正在测试具有 1000000000 个相等 guid 的极不可能的情况。 Guid.Equals() 针对更可能的情况进行了优化,即它们不会相等。
-
我设置了 1000000000 次检查以获得更好的比较。