Stringing along in .NET…

20Jul08

Notice: I know the source code on this post is cut off, and in the case of the test code, the comment tags are missing, as well as an instance of a generic list. This is because the pre tag does not format HTML tags. WordPress.com has a sourcecode plug-in, but this post is too complex for it to handle (apparently), and a bug has been filed with WordPress.com to help get it fixed. If you want the source code for the tests, let me know, and I will get it to you.

Two co-workers were having a conversation about which method to compare Strings in .NET is the most efficient. There are several good references and blog posts on the subject matter, but none I felt brought all the material together in one place. This blog aims to do just that by once and for all demonstrating the most efficient method to compare two strings in .NET.

Basic Concepts

In order to determine which method is the most efficient, some basic concepts of string comparison and .NET need to be reviewed.

Literal Strings

In .NET all literal strings that are equal by value point to the same memory location:

var s1 = "Hello, world.";
var s2 = "Hello, world.";
var s3 = s2;
var b1 = ReferenceEquals( s1, s2 );
var b2 = ReferenceEquals( s2, s3 );
Console.Write( "{0},{1}", b1, b2 );

The above code would print out True,True to the console.

The Intern Table

The reason that all literal strings with equivalent values reference the same memory address is because of .NET’s intern table. When code containing a literal string is Just-In-Time (JIT) compiled, the Common Language Runtime (CLR) checks to see if the string’s value is present in an internal (intern) lookup table. If the intern table already contains an equivalent value then the address of the existing value is retuned. If the string’s value is not present then it is added to the intern table and the address of the new element is returned.

The intern table optimizes string performance by ensuring that equivalent string values are only stored once in memory. Literal strings are interned by default in .NET (although .NET 2.0 allows string interning to be disabled by default), but strings constructed at runtime are not.

Runtime Strings

Strings constructed at runtime with equivalent values are not equivalent by reference, that is they do not reference the same memory address.

var s1 = string.Format( "{0}, {1}.", "Hello", "world" );
var s2 = string.Format( "{0}, {1}.", "Hello", "world" );
var b1 = ReferenceEquals( s1, s2 );
Console.WriteLine( "{0}", b1 );

The above code will print False to the console. Runtime strings can, however, be interned manually via the String class’s Inern method.

s1 = String.Intern( s1 );
s2 = String.Intern( s2 );
var b2 = ReferenceEquals( s1, s2 );
Console.WriteLine( "{0}", b2 );

Due to being interned, s1 and s2 now point to the same memory address so the above code will print True to the console. FYI – if a string value is not interned when the Intern method is invoked on it, it will be interned. There is also an IsInterned method which works much the same way as the Intern method, except if the string value given as the argument to IsInterned is not interned, a null value will be returned and the string value remains un-interned.

While it may be tempting to intern all runtime strings in order to reduce the amount of memory required to store new strings, remember that memory is still allocated to create the string before it can be interned. And that memory is not collected until all of its references go out of scope and the garbage collector (GC) is called.

Comparison Methods

There are several methods available with which to compare the equality of two strings, but they are not all equal (pardon the pun) in the efficiency by which the get the job done:

  • The String class’s instance Equals method
  • The String class’s static Equals method
  • The equality operator (==)
  • The String class’s instance CompareTo method
  • The String class’s static Compare method
  • .NET’s Regular Expression static RegEx.IsMatch method

The Instance Equals Method

The String class’s instance Equals method is invoked from an existing string object:

var s = "Hello, world";
Console.WriteLine( "{0}", s.Equals( "Hello, world." ) );

The above code will print True to the console. The instance Equals method compares two string values by comparing their values. This can be verified by looking at the implementation of the instance Equals method:

[ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
public bool Equals(string value)
{
    if ((value == null) && (this != null))
    {
        return false;
    }
    return EqualsHelper(this, value);
}

[ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
private static unsafe bool EqualsHelper(string strA, string strB)
{
    int length = strA.Length;
    if (length != strB.Length)
    {
        return false;
    }
    fixed (char* str = ((char*) strA))
    {
        char* chPtr = str;
        fixed (char* str2 = ((char*) strB))
        {
            char* chPtr2 = str2;
            char* chPtr3 = chPtr;
            char* chPtr4 = chPtr2;
            while (length >= 10)
            {
                if ((((*(((int*) chPtr3)) != *(((int*) chPtr4))) || (*(((int*) (chPtr3 + 2))) != *(((int*) (chPtr4 + 2))))) || ((*(((int*) (chPtr3 + 4))) != *(((int*) (chPtr4 + 4)))) || (*(((int*) (chPtr3 + 6))) != *(((int*) (chPtr4 + 6)))))) || (*(((int*) (chPtr3 + 8))) != *(((int*) (chPtr4 + 8)))))
                {
                    break;
                }
                chPtr3 += 10;
                chPtr4 += 10;
                length -= 10;
            }
            while (length > 0)
            {
                if (*(((int*) chPtr3)) != *(((int*) chPtr4)))
                {
                    break;
                }
                chPtr3 += 2;
                chPtr4 += 2;
                length -= 2;
            }
            return (length < = 0);
        }
    }
}

The instance method first determines if either of the two string values being compared are null, and then if they are not it performs a value comparison using the EqualsHelper method. While this is a perfectly legitimate method of comparing two strings, it is not as efficient as it could be because the two comparators are not checked first for reference equality. I cannot say why the instance method lacks this simple check that would greatly improve its efficiency with regards to comparing literal or interned strings, but I can make an educated guess. It is unlikely that a developer would write the following code:

var b = "Hello, world.".Equals( "Hello, world." );

Even if the first string literal were a constant, more than not I have seen constants compared to other string literals, constants, or runtime strings using the equality operator. Because of this tendency, the developers of the .NET String class probably did not see a reason to implement a reference check in the instance version of the Equals method. That, however, is not the case with the static Equals method.

The Static Equals Method

The static Equals method is invoked directly from the String class:

var s1 = "Hello, world.";
var s2 = "Hello, world.";
var b = string.Equals( s1, s2 );
Console.WriteLine( "{0}", b );

The above code prints out True to the console. The static Equals method is more efficient than the Instance method because the static method performs a reference comparison before comparing the value of the strings:

public static bool Equals(string a, string b)
{
    return ((a == b) || (((a != null) && (b != null)) && EqualsHelper(a, b)));
}

Since determining if two objects reference the same memory location is faster than comparing the data stored in said memory location, the static Equals method is a more efficient way of comparing two string values that share the same memory location.

The Equality Operator (==)

The equality operator, written as a ==, simply invokes the static Equals method in order to compare two strings:

public static bool operator ==(string a, string b)
{
    return Equals(a, b);
}

Although at first glance it may appear that this means the equality operator should be as efficient as the static Equals method, because an extra frame is added to the call stack, there is a slight penalty for using the ==.

Fun Fact: The Java String class does not overload the equality operator to perform a value comparison, so in Java if the equality operator is used to compare two strings, the only comparison occurring is a reference comparison. Be careful! Be sure to use String.equals or String.equalsIgnoreCase when comparing strings in Java.

The Instance CompareTo Method

The most basic signature of the CompareTo method is invoked like so:

var s1 = "Hello, world."
var s2 = "Hello, world."
var i = s1.CompareTo( s2 );
Console.WriteLine( "{0}", i == 0 );

The above code prints True to the console. The CompareTo method offers greater flexibility when comparing two strings than the Equals methods (instance or static) and the equality operator. However, the added features probably add to the amount of time it takes to complete its task as well. Like the instance Equals method, the instance CompareTo method does not compare the two comparators’ references prior to comparing their values:

public int CompareTo(string strB)
{
    if (strB == null)
    {
        return 1;
    }
    return CultureInfo.CurrentCulture.CompareInfo.Compare(this, strB, CompareOptions.None);
}

public virtual unsafe int Compare(string string1, string string2, CompareOptions options)
{
    if (options == CompareOptions.OrdinalIgnoreCase)
    {
        return string.Compare(string1, string2, StringComparison.OrdinalIgnoreCase);
    }
    if ((options & CompareOptions.Ordinal) != CompareOptions.None)
    {
        if (options != CompareOptions.Ordinal)
        {
            throw new ArgumentException(Environment.GetResourceString("Argument_CompareOptionOrdinal"), "options");
        }
        if (string1 == null)
        {
            if (string2 == null)
            {
                return 0;
            }
            return -1;
        }
        if (string2 == null)
        {
            return 1;
        }
        return string.nativeCompareOrdinal(string1, string2, false);
    }
    if ((options & ~(CompareOptions.StringSort | CompareOptions.IgnoreWidth | CompareOptions.IgnoreKanaType | CompareOptions.IgnoreSymbols | CompareOptions.IgnoreNonSpace | CompareOptions.IgnoreCase)) != CompareOptions.None)
    {
        throw new ArgumentException(Environment.GetResourceString("Argument_InvalidFlag"), "options");
    }
    if (string1 == null)
    {
        if (string2 == null)
        {
            return 0;
        }
        return -1;
    }
    if (string2 == null)
    {
        return 1;
    }
    if (!this.IsSynthetic)
    {
        return Compare(this.m_pSortingTable, this.m_sortingLCID, string1, string2, options);
    }
    if (options == CompareOptions.Ordinal)
    {
        return Compare(CultureInfo.InvariantCulture.CompareInfo.m_pSortingTable, this.m_sortingLCID, string1, string2, options);
    }
    return nativeCompareString(this.m_sortingLCID, string1, 0, string1.Length, string2, 0, string2.Length, GetNativeCompareFlags(options));
}

This decreases the efficiency of the CompareTo method even further.

The Static Compare Method

The static Compare method has several overloads that gives it the same flexibility as its instance counter-part, but its basic signature is invoked as such:

var s1 = "Hello, world";
var s2 = "Hello, world";
var i = string.Compare( s1, s2 );
Console.WriteLine( "{0}", i == 0 );

The above code will print True to the console. Like the instance Compare method the static CompareTo method does not compare the references of the strings prior to a value comparison, trading flexibility for efficiency.

The RegEx IsMatch Method

The RegEx IsMatch method uses .NET’s regular expression engine to determine if a string matches a given regular expression pattern:

var pattern = "Hello, world.";
var string_value = "Hello, world.";
var b = Regex.IsMatch( string_value, pattern );
Console.WriteLine( "{0}", b );

The above code will print True to the console. The regular expression engine in .NET is extremely good (it is Perl 5 compliant), but is not the most efficient method of simply comparing two strings. While the IsMatch method provides a way to compare two strings using case insensitivity, so does the CompareTo and Compare methods, and the latter methods are far more efficient than the regular expression engine for simple equality comparisons.

Performance Tests

All of the above methods were put through a series of performance tests in order to determine which method is the most efficient.

The Test Code

The code used for the test is as follows:

namespace st
{
    using System;
    using System.Collections.Generic;
    using System.Diagnostics;
    using System.Text.RegularExpressions;

    /// <summary>
    /// A delegate for a perforance test method.
    /// </summary>
    internal delegate void PerfTestMethod();

    /// <summary>
    /// A structure to hold the results of a performance test.
    /// </summary>
    internal struct PerfTestResult
    {
        /// <summary>
        /// The number of nanoseconds it took for the test to complete.
        /// </summary>
        public long Duration;

        /// <summary>
        /// The number of iterations the performance test completed.
        /// </summary>
        public int Iterations;
    }

    internal class Program
    {
        /// <summary>
        /// The processor this process's main thread should be bound to.
        /// </summary>
        private const int CPU_AFFINITY = 1;

        /// <summary>
        /// The maximum power of ten that specifies the number of iterations
        /// to put a test through.
        /// </summary>
        private const int MAX_POWER = 8;

        private const string s1 = "Hello, world.";
        private const string s2 = "Hello, world.";

        /// <summary>
        /// 10 to the power of 9
        /// </summary>
        private const long TEN_EXP_NINE = 1000000000;

        /// <summary>
        /// An array of timing discrepancies created by the anonymous delegate
        /// dispatch.
        /// </summary>
        private static readonly long[] discs = new long[MAX_POWER];

        private static readonly string s3 =
            string.Format("{0}, {1}.", "Hello", "world");

        private static readonly string s4 =
            string.Format("{0}, {1}.", "Hello", "world");

        private static void Main()
        {
            // Bind this thread to the specified processor.
            Process.GetCurrentProcess().ProcessorAffinity =
                new IntPtr(CPU_AFFINITY);

            // Get the discrepancies.
            GetAnonymousMethodDispatchTimingDiscrepancies(() => { });

            // STRING LITERALS
            TestPerformance(
                "Instance Equals with String Literals",
                () => s1.Equals(s2));

            TestPerformance(
                "Instance Equals with String Literals using Ordinal Comparison",
                () => s1.Equals(s2, StringComparison.Ordinal));

            TestPerformance(
                "Static Equals with String Literals",
                () => string.Equals(s1, s2));

            TestPerformance(
                "Static Equals with String Literals using Ordinal Comparison",
                () => string.Equals(s1, s2, StringComparison.Ordinal));

            TestPerformance(
                "Equality Operator with String Literals",
#pragma warning disable 168
                () => { bool b = s1 == s2; });
#pragma warning restore 168

            TestPerformance(
                "Instance CompareTo with String Literals",
                () => s1.CompareTo(s2));

            TestPerformance(
                "Static Compare with String Literals",
                () => string.Compare(s1, s2));

            TestPerformance(
                "Static Compare with String Literals using Ordinal Comparison",
                () => string.Compare(s1, s2, StringComparison.Ordinal));

            TestPerformance(
                "RegEx IsMatch with String Literals",
                () => Regex.IsMatch(s1, s2));

            // RUNTIME STRINGS

            TestPerformance(
                "Instance Equals with Runtime Strings",
                () => s3.Equals(s4));

            TestPerformance(
                "Static Equals with Runtime Strings",
                () => string.Equals(s3, s4));

            TestPerformance(
                "Equality Operator with Runtime Strings",
#pragma warning disable 168
                () => { bool b = s3 == s4; });
#pragma warning restore 168

            TestPerformance(
                "Instance CompareTo with Runtime Strings",
                () => s3.CompareTo(s4));

            TestPerformance(
                "Static Compare with Runtime Strings",
                () => string.Compare(s3, s4));

            TestPerformance(
                "RegEx IsMatch with Runtime Strings",
                () => Regex.IsMatch(s3, s4));
        }

        /// <summary>
        /// Returns the power of 10 that the given number was raised to.
        /// </summary>
        /// <param name="number">
        /// The number that exists because 10 was raised to a certain power.
        /// </param>
        /// <returns>The power that raised 10 to the given number.</returns>
        private static int GetPower(long number)
        {
            int power = 1;
            for (int x = 1; x < = MAX_POWER; ++x)
            {
                double y = Math.Pow(10, x);
                if (y == number)
                {
                    power = x;
                    break;
                }
            }
            return power;
        }

        /// <summary>
        /// Gets the timing discrepancies created by executing each performance
        /// test as an anonymous method because of the dispatch.
        /// 
        private static void GetAnonymousMethodDispatchTimingDiscrepancies(
            PerfTestMethod ptm)
        {
            for (int x = 10; x < = Math.Pow(10, MAX_POWER); x *= 10)
            {
                /*
                 * JIT the performance test method . Because there is overhead 
                 * associated with JITing, the performance test method should
                 * be JITed before any sequential metrics are generated, 
                 * otherwise the first result will skew high.
                 */
                for (int y = 0; x == 10 && y < 10; ++y) ptm();

                // Start the clock
                Stopwatch sw = Stopwatch.StartNew();

                // Execute a null test.
                for (int y = 0; y < x; ++y) ptm();

                // Stop the clock.
                sw.Stop();

                discs[GetPower(x) - 1] =
                    GetNanoseconds(sw.ElapsedTicks);
            }
        }

        /// <summary>
        /// Converts a given number of ticks to nanoseconds.
        /// 
        /// <param name="ticks">Number of ticks to convert.</param>
        /// The power of ten the current iteration was raised to.
        /// <returns>The number of ticks as nanoseconds.</returns>
        private static long GetNanoseconds(long ticks)
        {
            return ticks * TEN_EXP_NINE / Stopwatch.Frequency;
        }

        /// <summary>
        /// Executes the given performance test.
        /// </summary>
        /// <param name="description">
        /// A short description of the test method.
        /// </param>
        /// <param name="ptm">
        /// A delegate for the performance test to execute.
        /// </param>
        private static void TestPerformance(string description, PerfTestMethod ptm)
        {
            // Create a list to hold the performance test results.
            var lptr = new List<perftestresult>();

            for (int x = 10; x < = Math.Pow(10, MAX_POWER); x *= 10)
            {
                /*
                 * JIT the performance test method . Because there is overhead 
                 * associated with JITing, the performance test method should
                 * be JITed before any sequential metrics are generated, 
                 * otherwise the first result will skew high.
                 */
                for (int y = 0; x == 10 && y < 10; ++y) ptm();

                // Start the clock
                Stopwatch sw = Stopwatch.StartNew();

                // Execute test
                for (int y = 0; y < x; ++y) ptm();

                // Stop the clock.
                sw.Stop();

                // Store the performance test result in the list.
                lptr.Add(new PerfTestResult
                    {
                        // Get the duration of the test in nanoseconds.
                        Duration = GetNanoseconds(sw.ElapsedTicks) -
                                   discs[GetPower(x) - 1],
                        // Get the number of iterations the test was put through.
                        Iterations = x,
                    });
            }

            // Print the results.
            Console.WriteLine(description);
            for (int x = 0; x < lptr.Count; ++x)
            {
                Console.WriteLine(
                    "{0},{1}",
                    lptr[x].Iterations, lptr[x].Duration);
            }
            Console.WriteLine();
        }
    }
}

The Test Results

The test results were pretty straight-forward (click on the image to embiggen):

Comparing the Efficiency of Several String Equality Methods

Comparing the Efficiency of Several String Equality Methods

Each method of comparing a string was tested over a given number of iterations, from 10^1 to 10^8 iterations. The number of iterations is represented by the X axis, and the amount of time, in milliseconds (ms), it took to complete the given number of iterations is represented by the Y axis.

There are two basic types of comparisons: comparing string literals and comparing runtime strings. When comparing string literals the String class’s static Equals method clocks in the fastest time at 604.24260 ms. The equality operator is just a tad slower due to the extra call frame on the stack when it invokes the static Equals methods. The static Equals method are on average 70% faster than the instance Equals method because the static method compares the strings’ reference first and if they are equal then a value comparison is not required. This is juxtaposed to the instance method that does not perform a reference comparison, opting for a value comparison immediately.

When comparing runtime strings the String class’s instance Equals method is the most efficient at 1486.90330 ms, edging out the static Equals method at 1594.20560 ms. The reason for this is because runtime strings never reference the same memory address the reference comparison, the static Equals method performs is simply an extra, unneeded step.

While runtime strings can be interned, the performance tests showed that taking the time to intern runtime strings prior to comparison adds more time than it saves. This is true when there is a 1/10 chance the runtime string will be interned, or when the runtime string is always interned.

The Compare, CompareTo, and Regex IsMatch methods are all fairly inefficient when compared to the Equals methods, so they should only be used when their added feature-set (case insensitive comparisons for example) is required.

Conclusion

In conclusion, the String class’s static Equals method is the most efficient way to compare two string literals and the String class’s instance Equals method is the most efficient way to compare two runtime strings. The kicker is that there must be 10^5 (100,000) string comparisons taking place before one method starts becoming more efficient than the next. Until then, use whichever method floats your boat.

About these ads


11 Responses to “Stringing along in .NET…”

  1. Your … “embiggen” … doesn’t.

  2. 2 akutz

    According to the WordPress.com gods:

    “Notice: Because of an issue with Amazon’s S3 service, some images maybe unavailable for viewing after upload. We are working on resolving this issue and will update the following forum thread with more information as it is available.”

    http://en.forums.wordpress.com/topic.php?id=31951&replies=33

  3. 3 dstar

    What about the instance Equals overload with a StringComparison parameter. It would be interesting to see if there is any difference in performance between s1.Equals(s2) and s1.Equals(s2, StringComparison.Ordinal). And theoretically it should be faster than String.Compare with the same comparison type.

  4. 4 Tim Lloyd

    Akutz,

    Great article – definitely a worthwhile contribution.

    It would be interesting to see how a couple of tweeks affect the results.

    You are using DateTime.Now for timing which has poor resolution (http://msdn.microsoft.com/en-us/library/system.datetime.now.aspx) so will adversley affect small n iterations. Using a StopWatch would be better in this case.

    Also, using a delegate (via an anonymous method) to pass your test to your iteration code will incur an overhead on each iteration because of delegate dispatching. This will have a negative affect on the quickest tests.

    I would be interested to see the results with these changes…

    Thanks, Tim.

  5. 5 Tim Lloyd

    Also worth noting with the “runtime strings that are interned” example is that it is generating new strings on each iteration which is not (hopefully) a good example of how interning of runtime strings would actually be used. I have used interning of runtime strings to good affect, but this has been with a limited number of stable strings (representing a schema) which I know will be compared repeatedly. It saved CPU cycles and memory…

    It is probably worth thinking about why the performance graph suddenly spikes…

  6. 6 akutz

    Tim,

    Thank you very much for your feedback! Please allow me to answer some of your points:

    1) DateTime does have a poorer resolution than StopWatch, and if I had been thinking I would have used StopWatch. However, since StopWatch still uses the same time units to reflect elapsed time as my method (a TimeSpan, which uses ticks and ms for small units of time). The problem that I ran into was not measuring small discrepancies, but rather measuring units of time that infinitely approached zero. StopWatch may be able to measure such a unit, but it does not possess the capabilities to reflect that measurement.

    2) My use of a delegate was to make it easier to add new performance tests in the future without having to create a new method each time. I know it impacts performance slightly (much like the difference between the static Equals method and the equality operator), but since all of the tests are using it, the impact simply becomes Z added to X time. While the value of Z has a greater effect on smaller values of X, Z+X1 – Z+X2 is still equivalent to X1-X2, so comparing the efficiency of the string comparison methods should remain valid using delegates.

    3) I originally interned existing strings from an array at random (an array of 26 elements representing the Latin alphabet). However, I ended up creating the string each time since in a test there is no good way to know how a user will access said strings — will they be accepting user input? Or will they parse values from a config file up front? Since my team at UT writes a lot of web apps, they constantly parse user input, hence the creation of new strings. I’m happy to write another test that only interns existing strings.

    Thank you again for your comments! I’m glad you found the article helpful. Tell your friends!

  7. 7 Tim Lloyd

    Thanks for replying to my comments. I appreciate that you want to defend your results, but I am offering sound advice, and the opportunity of making your article a better article by exploring some flaws that invalidate your specific conclusion. Making your article and conclusion more accurate is a good thing.

    You should at least try my suggestion with DateTime – “do not think, first measure”. It is not a coincidence that you have lots of zero time results and it suddenly jumps to ~15ms.

    If you trully want to reply to my comments, make the changes to your code and present the new results and conclusion. I think it’s important to represent as accurate a picture here as possible, as readers of the article will no doubt be taking the results on faith and this (at present) would be perpetuating flawed results and conclusion. I have already seen someone agree with your specific conclusion on another blog, who incidentally should know better…

    1) You are mis-undestanding how system clock resolution affects DateTime, which is clearly stated in the msdn article – read it. It’s an important point, as it has consequences for your “kicker”… DateTime vs StopWatch is not a new thing when doing micro-benchmarking, it’s a basic performance measuring issue. Google it if you do not wish to believe my comments or the msdn article. You will see you are far from the first to have come a cropper on this issue.

    You will see that your early results hover around multiples ~15ms. This is a clue as to the resolution of DateTime and is to do with the frequency at which the system clock (not to be confused with the processor clock) is generating interupts on your machine. It has nothing to do with the units “it uses”. I suggest you read further on DateTime vs StopWatch, as your understanding of micro-benchmarking and performance will be better for it.

    The StopWatch class can be used to calculate timings that are accuarte to the nano-second. Here’s a decent article demstrating its use:

    http://blogs.msdn.com/amolravande/archive/2007/09/15/timing-managed-code-in-net.aspx

    2) I understand that you have used delegates to make the code elegant and more easily maintainable, but that is not the point of doing performance testing. And yes it adds constant time, but you do not take this into consideration when presenting your conclusion which depends on the total time including delegate overhead.

    At least compensate for it, then you can have elegant code that produces more accurate results. For example, you could have a dry run test at the beginning of your tests (after a warm up) that simply iterates over a delegate with no body the maximum number of times. You can use the timing of this dry run to work out an approximate compensation for the use of the delegate patern. Not perfect, but the results are more accurate. You do want the results to be accurate as far as you know them?

    3) If you want to test runtime interning, then use a sensible scenario. You have merely demonstrated the worst possible use of runtime interning and not described it as such. Do add another test that uses runtime interning that will result in savings because strings are interned once and re-compared. Amongst other things, your test is spamming the heap and GC which is a different performance issue alltogether.

  8. 8 akutz

    Tim,

    Thank you again for your feedback. Please don’t think I am dismissing your advice, I already changed the DateTime to a StopWatch. However, I also do not appreciate your more aggressive tone in the second comment. You offer sound advice, but your tone conveys displeasure and arrogance that I did not leap at it with a thousand thank you’s. You were snippy, rude, and I almost deleted the comment.

    I do appreciate your advice, but implying that my tests are not “sensible” and that people should are taking my article on “faith” is simply abrasive to the point of being nasty. Obviously how people interpret my findings is not a matter of faith, I clearly demonstrated how the results were reached, and several people other than you have validated them.

    Next time you choose to reply, please do so with the courtesy you showed in the first comment, and please do not echo the complete lack of it in the second.

  9. 9 akutz

    Tim,

    One last thing. With regards to how the tests are measured. It is not necessarily my intention to benchmark how long a test takes to absolute precision, but the length of time it takes as compared to other tests. As long as all tests are benchmarked using the same method the order of the results should remain the same.


  1. 1 I know the answer (it's 42) : String equality

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s


Follow

Get every new post delivered to your Inbox.

%d bloggers like this: