Showing posts with label String handling. Show all posts
Showing posts with label String handling. Show all posts

Thursday, December 29, 2016

Performance: Enum ToString or GetName


Usually I am too lazy to bother using anything else than the ordinary ToString when working with enums, but I always have that filthy feeling that I am cheating.
If there is a dedicated method in the framework to do something, there probably is a reason for it. And knowing that the Enum class contains a GetName method to get the string representation of a enum value made me want to know if there is a performance difference between the two.

First, the tested Enum.GetName method, for sake of argument wrapped up as an Extension for easier and cleaner usage.

public static class Extension
{
    public static string GetEnumName<T>(this T t) where T : struct, IConvertible
    {
        return Enum.GetName(typeof (T), t);
    }
}

Back to extensions list

The test rigg

The test rigg is basically the same that I used in Performance of different lock methods in .net.
Uses RunningAverage to calculate the average without storing each individual number.
Just cleaned up and simplified for this scenario:

Data

private enum SmallEnum
{
    Small,
    Larger
}
private enum LargerEnum
{
    a, b, c, d, e, f, g, h,
    aaaaa, bbbbb, ccccc, ddddd,
    xxxxx, yyyyy, zzzzz,
    VeryLongNameThisIs,
    OtherName, Test, oooo, olf,
    eowind, eeeee, ss, qqqqq,
    mu, miu, bach, escher,
    godel, code, number, last
}

Decided to test 2 different enums with different number of elements to see if that has any impact. The small has 2 elements and the larger has 32.

Code

private void ExecuteTestSmall(int iterations)
{
    var type = "small";
    string s = string.Empty;
    try
    {
        var testData = GenerateTestDataSmall();
        var toStringTimings = new RunningAverage();
        var getNameTimings = new RunningAverage();
        for (int i = 0; i < iterations; i++)
        {
            for (int dataIndex = 0; dataIndex < testData.Count; dataIndex++)
            {
                var item = testData[dataIndex];
                toStringTimings.Add(TimeAction(() =>
                {
                    s = item.ToString();
                }));
                if (!string.IsNullOrEmpty(s))
                    s = string.Empty;
                getNameTimings.Add(TimeAction(() =>
                {
                    s = item.GetEnumName();
                }));
                if (!string.IsNullOrEmpty(s))
                    s = string.Empty;
            }
        }
        Log($"{type}ToString\tDataCount\t2\tIterations:\t{iterations}\tAverage:\t{toStringTimings.Average:0.000}\tticks");
        Log($"{type}GetName\tDataCount\t2\tIterations:\t{iterations}\tAverage:\t{getNameTimings.Average:0.000}\tticks");
    }
    catch (Exception ex)
    {
        Log($"{type}Fail\tDataCount\t2\tIterations:\t{iterations}\tFailed\t{ex.Message}");
    }
}

Ok, I was lazy. I created 2 methods instead of putting the effort to reuse it. The above is for the SmallEnum. Exactly the same code for the LargerEnum tester with difference in GenerateTestDataSmall to return a List<LargerEnum> instead of List<SmallEnum>.

This was executed as follows:
public void Execute()
{
 Log("--------------------------------------");
 Log("... single thread");
 ExecuteTestSmall(250000);
 ExecuteTestLarger(250000);
}

250000 times each.

Results

ToString enum size 2 Iterations: 250000 Average: 1.231 ticks
GetName enum size 2 Iterations: 250000 Average: 0.662 ticks
ToString enum size 32 Iterations: 250000 Average: 1.357 ticks
GetName enum size 32 Iterations: 250000 Average: 0.692 ticks

Conclusion

Using the dedicated Enum.GetName function to get the string representation instead of the lazy object.ToString() seems to be twice faster. But in the end, we are talking around 1 tick, so maybe not the first place to start optimizing if you have a performance issue in you application. As I wrote above, it just feels a little bit lazy to not do it the correct way ;)

All code provided as-is. This is copied from my own code-base, May need some additional programming to work. Hope this helps someone out there :)



Friday, November 4, 2016

String extension for Contains, with StringComparison

So, many of the built in string methods have the possibility to set the StringComparison in code, to for example OrdinalIgnoreCase wich is my favorite for most back-end processing.
Sad thing is, the Contains does not have it. So here is a small extension that fixes it with the help of IndexOf method

Some source-code (C#) to copy into your utilities class.

public static bool Contains(this string s, string sub, StringComparison comparison)
{
    return s.IndexOf(sub, comparison) != -1;
}

Hope this helps someone out there :)

Back to extensions list

Friday, October 7, 2016

Transform string to and from camel case.

Small piece of code to transform a string to and from camel case form. For anyone who find that kind of things useful.

private static readonly char[] Delimeters = {' '}; 
public static string ToCamelCase(string input)
{
 var sb = new StringBuilder();
 input = input.ToLowerInvariant();
 var split = input.Split(Delimeters, StringSplitOptions.RemoveEmptyEntries);
 foreach (var s in split)
 {
  for(int i = 0; i < s.Length; i++)
  {
   var c = s[i];
   sb.Append(i == 0 ? char.ToUpperInvariant(c) : c);
  }
 }
 return sb.ToString().Trim();
}

public static string FromCamelCaseToNormal(string input)
{
 var sb = new StringBuilder();
 foreach (var c in input)
 {
  if (char.IsUpper(c) 
   || char.IsDigit(c))
   sb.Append(" ");
  sb.Append(c);
 }
 return sb.ToString().Trim();
}

Wednesday, September 21, 2016

String to Enum parsing


The normal way of parsing an string containing an enum value:

var text = "Unknown";
var parsed = (KnownType)Enum.Parse(typeof(KnownType), text, true);

I've always found this to be ugly when scattered in the code base and usually I solve it by adding the following string extension to the projects utility class:

public static <T> ParseEnum(this string s)
{
    return (T) Enum.Parse(typeof (T), s, true);
}

Usage:

var text = "Unknown";
var parsed = text.ParseEnum<KnownType>();


Hope this helps someone out there