30335

Surprisingly bad c# switch performance

Question:

I have created a TypeSwitch class to cast my fields using a code similar to the following shortened sample:

static Dictionary<Type, int> TypeDefs = new Dictionary<Type, int>() { {typeof(Int16), 1}, {typeof(Int32), 2}, {typeof(Int64), 3}, {typeof(IntPtr), 4}, ... {typeof(String), 18} }; public static object ConvertFromDBValue(Type type, object value) { try { switch (TypeDefs[type]) { case 1: // {typeof(Int16), 1}, { return Convert.ToInt16(value); } case 2: // {typeof(Int32), 2}, { return Convert.ToInt32(value); } case 3: // {typeof(Int64), 3}, { return Convert.ToInt64(value); } ... ... ... case 17: // {typeof(Char), 17}, case 18: // {typeof(String), 18}, { return value.ToString().Trim(); } default: { return value; } } } catch (Exception ex) { throw ex; } }

Using instrumentation tool, I see over 60% of the time is spent in the function body of the above ConvertFromDBValue, i.e. I spend more time because of switch (or try-catch) than look up the Type value in Dictionary.get_Item and converting the value (e.g. Convert.ToInt32). Actually, I spend 3 times more time in the function body than the Dictionary.get_Item...

This is somehow surprising to me - can anyone confirm the switch is so much slower, or is there any other reason for this?!

<strong>UPDATE</strong> I removed the try-catch part but this didn't really much...

Answer1:

As others mentioned you are paying the boxing/unboxing penalty and if possible you should try to eliminate it.

As for the body of the method you should get rid of the dictionary and use a chain of if if elses - it should give you a measurable improvement in performance.

<hr />

EDIT

After Hogan's comment I've tested it and to my surprise the difference between <em>dict+catch</em> vs <em>if</em> is less was between 5%-15% (<em>if</em> was slightly faster) in my imperfect test below.

Dict+case: 987.0945ms Ifs: 937.5104ms Hogan's array of funcs: 854.4887ms

Test:

class Program { static Dictionary<Type, int> TypeDefs = new Dictionary<Type, int>() { {typeof(Int16), 1}, {typeof(Int32), 2}, {typeof(Int64), 3}, {typeof(IntPtr), 4}, {typeof(char), 5}, {typeof(String), 6} }; static KeyValuePair<Type,object>[] _Types = new[] { new KeyValuePair<Type,object> ( typeof(Int16),5 ), new KeyValuePair<Type,object> (typeof(Int32),57 ), new KeyValuePair<Type,object> (typeof(Int64),157 ), new KeyValuePair<Type,object> (typeof(IntPtr),new IntPtr(6) ), new KeyValuePair<Type,object> (typeof(String),"Hello!" ), }; public static object ConvertFromDBValue(Type type, object value) { try { switch (TypeDefs[type]) { case 1: // {typeof(Int16), 1}, { return Convert.ToInt16(value); } case 2: // {typeof(Int32), 2}, { return Convert.ToInt32(value); } case 3: // {typeof(Int64), 3}, { return Convert.ToInt64(value); } case 4: // {typeof(IntPtr), 4}, { return value; } case 5: // {typeof(Char), 17}, case 6: // {typeof(String), 18}, { return value; } default: { return value; } } } catch (Exception ex) { throw ex; } } public static object ConvertFromDBValue2(Type type, object value) { try { if (type == typeof(Int16)) { return Convert.ToInt16(value); } if (type == typeof(Int32)) { return Convert.ToInt32(value); } if (type == typeof(Int64)) { return Convert.ToInt64(value); } if (type == typeof(IntPtr)) { return (IntPtr)value; } if (type == typeof(Char) || type == typeof(String)) { return value.ToString().Trim(); } return value; } catch (Exception ex) { throw ex; } } static Func<object, object>[] funcList = { (value) => value, //0 (value) => Convert.ToInt16(value), //1 (value) => Convert.ToInt32(value), //2 (value) => Convert.ToInt64(value), //3 (value) => value, //4 (value) => value, //5 (value) => value, //6 (value) => value, //7 (value) => value, //8 (value) => value, //9 (value) => value, //10 (value) => value, //11 (value) => value, //12 (value) => value, //13 (value) => value, //14 (value) => value, //15 (value) => value, //16 (value) => value, //17 (value) => value.ToString().Trim() //18 }; public static object ConvertFromDBValueHogan(Type type, object value) { return funcList[TypeDefs[type]](value); } static void Main(string[] args) { var sw = new System.Diagnostics.Stopwatch(); Random random = new Random(113453113); sw.Start(); for (int i = 0; i < 10000000; i++) { var x = random.Next(5); var testValue = _Types[x]; var p = ConvertFromDBValue(testValue.Key, testValue.Value); } var elapsed = sw.Elapsed; Console.WriteLine($"Dict+case: {elapsed.TotalMilliseconds}ms"); sw.Restart(); for (int i = 0; i < 10000000; i++) { var x = random.Next(5); var testValue = _Types[x]; var p2 = ConvertFromDBValue2(testValue.Key, testValue.Value); } elapsed = sw.Elapsed; Console.WriteLine($"Ifs: {elapsed.TotalMilliseconds}ms"); sw.Restart(); for (int i = 0; i < 10000000; i++) { var x = random.Next(5); var testValue = _Types[x]; var p3 = ConvertFromDBValueHogan(testValue.Key, testValue.Value); } elapsed = sw.Elapsed; Console.WriteLine($"Hogan's array of funcs: {elapsed.TotalMilliseconds}ms"); Console.ReadLine(); } }

Answer2:

Here is some sample code if my "array of functions and array index to pick" was not clear.

Func<object, object>[] funcList = { (value) => value, //0 (value) => Convert.ToInt16(value), //1 (value) => Convert.ToInt32(value), //2 (value) => Convert.ToInt64(value), //3 (value) => value, //4 (value) => value, //5 (value) => value, //6 (value) => value, //7 (value) => value, //8 (value) => value, //9 (value) => value, //10 (value) => value, //11 (value) => value, //12 (value) => value, //13 (value) => value, //14 (value) => value, //15 (value) => value, //16 (value) => value, //17 (value) => value.ToString().Trim() //18 }; public static object ConvertFromDBValue(Type type, object value) { if (TypeDefs[type] <= 18) return funcList[TypeDefs[type]](value); else return value; }

To make this even faster take out the if (TypeDefs[type] <= 18) if statement if you can guarantee there are no values greater than 18.

<blockquote>

This is sample non-tested code.

</blockquote>

Recommend

  • iPhone - Escape charecter issue in JSON parsing
  • SolrNet - The given key was not present in the dictionary
  • Single db connection that does not get initialised per task
  • Accessing the properties of Win32_OperatingSystem
  • Update All Rows in DataBase with a hash value [closed]
  • convert .net 4.0 syntax OfType to .net 3.5
  • Which is Android/Java corresponding method to the C#/C++ method GetTickCount()?
  • What is the best SIMPLE replacement for VS Setup-project Installer for WinXP + WPF + .NET 4.0? [clos
  • How to make Node.js handle multiple requests efficiently?
  • Why does Redshift need to do a full table scan to find the max value of the DIST/SORT key?
  • Pandas: merge_asof() sum multiple rows / don't duplicate
  • PHP multiple file uploads
  • Alamofire and Reachability.swift not working on xCode8-beta5
  • Implementing “partial void” in VB
  • Convert Type Decimal to Hex (string) in .NET 3.5
  • What's the purpose of QString?
  • Alternative To body {overflow:scroll;} That Will Prevent Page Jostling/Wriggling?
  • Converting a WriteableBitmap image ToArray in UWP
  • Play WS (2.2.1): post/put large request
  • Atlas images wrong size on iPad iOS 9
  • Reading JSON from a file using C++ REST SDK (Casablanca)
  • Why value captured by reference in lambda is broken? [duplicate]
  • NetLogo BehaviorSpace - Measure runs using reporters
  • Why HTML5 Canvas with a larger size stretch a drawn line?
  • Spray.io: When (not) to use non-blocking route handling?
  • Does CUDA 5 support STL or THRUST inside the device code?
  • Modifying destination and filename of gulp-svg-sprite
  • javascript inside java/jsp code
  • ActionScript 2 vs ActionScript 3 performance
  • Warning: Can't call setState (or forceUpdate) on an unmounted component
  • GridView Sorting works once only
  • Can Visual Studio XAML designer handle font family names with spaces as a resource?
  • How can I remove ASP.NET Designer.cs files?
  • Are Kotlin's Float, Int etc optimised to built-in types in the JVM? [duplicate]
  • Binding checkboxes to object values in AngularJs
  • How to Embed XSL into XML
  • java string with new operator and a literal
  • Net Present Value in Excel for Grouped Recurring CF
  • jQuery Masonry / Isotope and fluid images: Momentary overlap on window resize
  • How to load view controller without button in storyboard?