16

which would be faster?

bool same=(Guid)Identifier==id;

bool same=String.Equals(string1,string2, StringComparison.OrdinalIgnoreCase);
Stan R.
  • 15,757
  • 4
  • 50
  • 58
zsharp
  • 13,656
  • 29
  • 86
  • 152
  • 3
    similar (but not the same) question here http://stackoverflow.com/questions/713109/performance-using-guid-object-or-guid-string-as-key – Chris Shouts Jan 22 '10 at 20:20
  • 2
    When, ever, would this matter? This sounds like a case of micro optimizing, which should be avoided at all costs. Remember, first make it work, than (if needed) make it faster. – Chris Kannon Jan 22 '10 at 20:39

5 Answers5

32

I used this code:

object victim = Guid.Empty;
Guid target = Guid.NewGuid();

Stopwatch sw = new Stopwatch();
sw.Start();
for (int i = 0; i < 10000000; i++){
    bool equal = ((Guid) victim) == target;
}
Console.WriteLine("Direct cast   : {0}", sw.Elapsed);

sw.Reset(); sw.Start();
for (int i = 0; i < 10000000; i++)
{
    bool equal = Guid.Equals(victim, target);
}
Console.WriteLine("Guid.Equals   : {0}", sw.Elapsed);

sw.Reset(); sw.Start();
string a = victim.ToString(); // as suggested by Mikael
string b = target.ToString();
for (int i = 0; i < 10000000; i++)
{
    bool equal = String.Equals(a, b, StringComparison.OrdinalIgnoreCase);
}
Console.WriteLine("String.Equals : {0}", sw.Elapsed);

Console.ReadLine();

And got this result for different values (best scenario):

object victim = Guid.Empty;
Guid target   = Guid.NewGuid();
// Direct cast   : 00:00:00.1164198
// Guid.Equals   : 00:00:02.1268147
// String.Equals : 00:00:00.4129527  // oh my!

And this result for same value (worse scenario)

object victim = Guid.Empty;
Guid target   = Guid.Empty;
// Direct cast   : 00:00:00.2793173
// Guid.Equals   : 00:00:03.5625948
// String.Equals : 00:00:01.7564302
Rubens Farias
  • 57,174
  • 8
  • 131
  • 162
  • 3
    Your benchmark exits comparison on byte 1 which is the best effort. If you compare even more bytes the times would differ even more. But the time diff is not as huge as you claim, since you have included the .ToString() in the loop. string a = victim.ToString(); string b = target.ToString(); for (int i = 0; i < 10000000; i++) { bool equal = String.Equals(a, b, StringComparison.OrdinalIgnoreCase); } – Mikael Svenson Jan 22 '10 at 20:38
  • @Mikael, nice tips; your suggestion lead a huge improvement on string.equals performance. – Rubens Farias Jan 22 '10 at 20:43
14

In my testing doing a straight UUID-UUID comparison VS String-String comparison, the UUID comparison takes about 1/4 the time as the String comparison.

However, the casting of String->UUID is expensive. Much more expensive than the UUID->String conversion. Both are more expensive than either of the comparison methods.

So: If you've got two UUIDs compare the UUIDs directly. If you've got two Strings compare the Strings directly. If you've got one String and one UUID, convert the UUID to a String and compare the Strings.

Michael Krauklis
  • 3,914
  • 2
  • 28
  • 28
1

A Guid == Guid will use code like:

public bool Equals(Guid g)
{
if (g._a != this._a)
{
    return false;
}
if (g._b != this._b)
{
    return false;
}

while the string compare in your example will use an unsafe pointer comparison.

Without benchmarking it, I suspect the Guid will be faster, but we're talking marginal. And you really need to crank up the number of comparisons to the multi millions for it to matter.

Both comparisons will break out early, meaning left to right, so that will also impact the speed. The string compare has more checks before the comparison occurs and one more method call as well.

Mikael Svenson
  • 39,181
  • 7
  • 73
  • 79
  • he said faster, not safer, though I'd be curious to see how a case-insensitive string comparison could use a pointer comparison. – Adam Robinson Jan 22 '10 at 20:25
  • The "unsafe" keyword is not necessarily unsafe to use, but it allows you to use memory pointers like you would in C++. Many functions in the .Net framework are implemented this way. So both ways are equally safe to use :) I merely refer to the actual function in the framework - private static unsafe int CompareOrdinalIgnoreCaseHelper(string strA, string strB) – Mikael Svenson Jan 22 '10 at 20:27
  • @Adam Robinson Check out private static unsafe int CompareOrdinalIgnoreCaseHelper(string strA, string strB) with reflector and they use char pointers and ints. – Mikael Svenson Jan 22 '10 at 20:28
1

A GUID compare is a memcmp of 16 bytes. It isn't going to be worse worse than a string compare, but if you care about performance that much you shouldn't be using managed code.

John Knoeller
  • 33,512
  • 4
  • 61
  • 92
  • His string comparison is case-insensitive. While the comparison shouldn't be much different in terms of time, it's not a byte-for-byte comparison like a Guid would be. – Adam Robinson Jan 22 '10 at 20:26
  • It's not. The .Net guid consists of Int32+Int16+Int16+(bytes*8). And it compares one against the other until the last byte. Meaning a max of 11 comparisons. – Mikael Svenson Jan 22 '10 at 20:31
  • Managed code is actually pretty fast - about 25-50% slower than C++ last I checked, about the same as Java... compare that with 8,000% slower for Python/Ruby, and 50,000% slower for PHP... – BlueRaja - Danny Pflughoeft Jan 22 '10 at 20:31
  • @BlueRaja: It can't possibly be faster, managed code is it's own universe, in order to touch _anything_ real - files, the internet, etc, you have to _leave_ managed code. Better to just stay outside the app domain in the first place if you need the absolute maximum performance. – John Knoeller Jan 22 '10 at 20:55
  • @Mikael: Does it really do 11 comparisons? This be done in 4 comparisons in unmanaged code, and without the overhead of a function call if memcmp was an intrinsic. – John Knoeller Jan 22 '10 at 20:58
  • @John Knoeller I was surprised myself, but fire up Reflector and check the code :) 11 if's below each other. – Mikael Svenson Jan 22 '10 at 21:05
  • @John: I said it's slower, not faster. – BlueRaja - Danny Pflughoeft Jan 22 '10 at 21:37
1

.NET Guid is a 16 byte structure which when represented as a string will be formatted in this pattern "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" which is about 32 characters.

So represented as a GUID it would take 16 bytes and represented as a string it would take 32*2 = 64 bytes.

So GUID.Equals() should perform better.

Also GUID.Equals(GUID) would perform better then guid1 == guid2 because there is no boxing involved in the former.

Santhosh
  • 6,547
  • 15
  • 56
  • 63
  • 1
    `Guid.Equals(object, object)` so you'll have un/boxing involved; please, see my benchmark up there – Rubens Farias Jan 22 '10 at 20:50
  • Guid.Equals(Guid) is described here. http://msdn.microsoft.com/en-us/library/asw89aw8.aspx. Looks like there is no boxing involved for this overload. – Santhosh Jan 22 '10 at 20:54
  • oh sorry, I thought you're talking about static version; but, anyways, you'll need to use Guid.Equals(object) as whole point is about casting from object – Rubens Farias Jan 22 '10 at 21:00
  • My quick test shows that Guid.Equals(object, object) is pretty damn slow. In fact it is about 5X slower than guid1 == guid2. The fastest I have found is guid1.Equals(guid2). This is 20% faster than plan old == – ProVega May 13 '14 at 03:59