6

Fastest Way to Compare Strings in C# .Net

Fastest Way to Compare Strings in C# .Net

What is the fastest way to compare strings in C# .Net? Everyone’s had to do it. A lot of people instinctively use the C# “==” operator. In several programming forums I’ve posted to, quite a few haven’t been aware of other built in methods or techniques that can be used, such as SequenceEqual, comparing ByteArrays, or simply using the favorite Dictionary/Hashset objects to check against the keys.

The Background:

Comparing strings is one of the basic fundamentals in any programming language. The most common syntax across multiple languages is simply the “==” operator. In C# .Net, the “==” operator isn’t always appropriate to use. Thus, Microsoft has included additional methods to compare string values. Then there are ways of comparing strings, which a lot of C# programmers have done, but may not realized. For example, checking a Dictionary object to determine if it contains a particular string as a key. That in itself is doing a string comparison.

With that said, what is the fastest way to compare strings in C# .Net? Is anyone else as curious as this Curious Consultant?

So let’s get started.

The Gist of it All:

I implemented what I think would be the most common ways of doing string comparisons. I chose the Dictionary and Hashset objects as the choice when comparing against keys because they proved to be the fastest in my test on collections for fastest string look ups (http://cc.davelozinski.com/c-sharp/fastest-collection-for-string-lookups) test.

If there’s another method people have, let me know so I can include it in the code!

Here’s the gist of the tests I included:
 

 

Code Snippet:

Case Sensitive:

 

==

String.Compare

String.CompareOrdinal

String.Equals

char by char

Byte Arrays

Dictionary Key

Hashset Key

IndexOf

CommonMethod

SequenceEqual

Case Insensitive:

 

==

String.Compare

String.CompareOrdinal

String.Equals

ToLower()

ToUpper()

char by char

Byte Arrays

Dictionary Key

Hashset Key

IndexOf

CommonMethod

SequenceEqual

 
The code is written in Visual Studio 2013 targeting .Net Framework version 4.5 x64. The source code is available at the end of this blog so you can benchmark it on your own system if you wish.

In a nutshell, the code does the following:

  1. Creates the storage objects, pre-sizing them where possible.
  2. Generates the random strings to compare of varying lengths.
  3. Also generates the strings to search for, purposely copying 1 of every 3 random strings to search to make sure we have at least a few matches.
  4. Performs the comparisons, implementing any of the various techniques in both a case and case-insensitive manner.
  5. Whenever a match is found, a counter is incremented. This is to ensure every technique matches the exact same amount.

The exe file was run on an Alienware M17X R3 running Windows 7 64-bit with 16 GB memory on an i7-2820QM processor.

The test was run for the following comparisons:

  • 100 strings
  • 1,000 strings
  • 10,000 strings
  • 100,000 strings
  • 1,000,000 strings

Off To The Races!

My prediction is the Dictionary or Common Custom method will be the fastest. I’ve also heard good things about the String.Compare functions, but haven’t used them enough to form an opinion.

Let’s see what happened on my machine. I ran this test 3 times, only taking the times from the 3rd run.

All times are indicated in seconds.milliseconds format. Lower numbers indicate faster runtime performance.

Winners are highlighted in green; there are no points for second place.
 


Comparison Performance:


# Strings Being Compared:

100

1,000

10,000

100,000

1,000,000

Case Sensitive
Comparisons:

         

==

0

0

0

0.0030001

0.0156000

String.Compare

0

0

0

0.0160009

0.0936001

String.CompareOrdinal

0

0

0

0

0.0156000

String.Equals

0

0

0

0

0.0156001

char by char

0

0

0

0

0.0312001

Using Byte array

0

0

0.0156001

0.0624001

0.4130236

Dictionary Keycheck

0

0

0

0

0.1800103

Hashset Keycheck

0.0030002

0

0

0

0.1860107

IndexOf

0

0

0.0060004

0.0156000

0.1570090

Common Custom Method

0

0

0.0020001

0.0070004

0.0624001

SequenceEqual Method

0.0070004

0.0010001

0.0120007

0.0250015

0.1716003

           

Case Insensitive
Comparisons:

         

==

0

0

0

0.0550032

0.2652005

String.Compare

0

0

0

0.0200012

0.0936002

String.CompareOrdinal

0

0

0

0.0020001

0.0156000

String.Equals

0

0

0.0010000

0.0150009

0.0468001

ToLower()

0

0.0010000

0.0060004

0.0500028

0.2652005

ToUpper()

0

0.0010001

0.0180010

0.0300017

0.2496004

char by char

0

0

0.0060003

0.0156000

0.2028004

Using Byte array

0

0.0010001

0.0070004

0.0312001

0.3890223

Dictionary Keycheck

0

0

0

0.0156000

0.1810103

Hashset Keycheck

0

0

0

0.0156000

0.1820104

IndexOf

0

0

0

0.0156000

0.1290074

Common Custom Method

0

0

0

0.0312000

0.2808005

SequenceEqual Method

0

0

0

0.0156001

0.1092001

The Results:

Most of the board remained green up through 10,000 comparisons and didn’t register any time.

At the 100,000 and 1,000,000 marks, things started to get a bit more interesting in terms of time differences.

String.CompareOrdinal was the constant superstar. What surprised me is for the case-insensitive comparisons, String.CompareOrdinal outperformed most other methods by a whole decimal place.

For case sensitive comparisons, most programmers can probably stick with the “==” operator.

In Summary:

On my system, unless someone spots a flaw in my test code, if you are doing just a few calls or searches here and there, almost anything should suit your needs and perform fine, especially if you only have a few comparisons to do. Even though there are a few technicalities and times when it shouldn’t be used, I personally like using the “==” operator because it’s simple, straight forward, and incredibly easy to read at just a glance.

However, if you have a “need for speed”, you should go with the String.CompareOrdinal from the outset to cover all your bases.

The Code:

  • good

  • YewChoong

    I don’t quite understand why the CompareOrdinal during case-insensitive comparison will have the same code. Isn’t it will compare with case-sensitive when you directly compare it with String.CompareOrdinal?

  • Dung DajHjep

    Nice test !

  • Robert Morley

    DateTime.Now is a very low-resolution method of timing code. You’d be better off using a StopWatch and measuring milliseconds or even ticks.

  • Todd

    Nice work! Just a suggestion, you could make this much more complete by including the various .Equals() flags, such as StringComparison.CompareOrdinal, etc. as this author did in his post: https://rhale78.wordpress.com/2011/05/16/string-equality-and-performance-in-c/

  • Ahkam Nihardeen

    Thanks for this post so much! I was doing some code to find the biggest palindrome, and I needed to keep turning a char array to string and back thousands of times. Hope you continue with your work 😀