我看了this question和another question。在这两个问题中的一个中,我还读到Guid Structure包含以下四个字段:Int32,Int16,Int16和Byte [8],因此两个Guid之间的比较应该更快。
好吧,我只使用Guid Structure来生成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);
}
得到以下结果。
为什么FastUuid比较最慢?
由于UUID应该是Dictionary
的关键,有没有办法在两个long之间进行性能比较,同时用一个小的(16字节)对象/结构实现UUID?
修改: 实际上,我执行的测试测量了两个UUID之间的比较性能,因此它们对于评估访问Dictionary的性能没什么意义,因为当我调用ContainsKey method时,它会计算UUID的哈希值。 。 我应该在计算Guid,String,FastUuid等的哈希值时评估性能吗? ContainsKey方法如何工作?
答案 0 :(得分:5)
结构的Equals的默认实现使用反射进行比较,这比较慢。 (有关详细信息,请参阅MSDN上的ValueType.Equals
。)
您应该覆盖Equals
并提供自己的实施:
public override bool Equals(object other)
{
var fastOther = other as FastUuid?;
return fastOther.HasValue &&
fastOther.Value._part1 == _part1 &&
fastOther.Value._part2 == _part2;
}
然而,这并不能完全解决问题。由于这是一个结构,您还应该实现IEquatable<FastUuid>
以避免必须对其他项进行装箱/取消装箱:
public bool Equals(FastUuid other)
{
return
other._part1 == _part1 &&
other._part2 == _part2;
}
等于是:
public override bool Equals(object other)
{
var fastOther = other as FastUuid?;
return fastOther.HasValue && Equals(fastOther.Value);
}
答案 1 :(得分:3)
结构使用运行时反射来确定需要比较的内容。使用您自己的比较方法覆盖等于以获得更快的结果。
请参阅http://msdn.microsoft.com/en-us/library/2dts52z7.aspx
更新 - 覆盖结构的等于最终会产生拳击开销(将struct转换为object并返回struct),但是如果你定义一个直接获取FastUuid结构的Equals,它运行得更快:
public bool Equals(FastUuid value)
{
return this._part1 == value._part1
&& this._part2 == value._part2;
}
这比我的盒子上的Guid实现稍微快一点(4.2秒对5.8秒,但仍比1.7秒的长对比慢)。
PS。请注意仅使用发布版本运行测试,因为调试版本会严重影响时序。