Test system for comparing code performance.
C#
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Permalink
Failed to load latest commit information.
PerformanceStubs
.gitignore
LICENSE
PerformanceStubs.sln
README.markdown

README.markdown

#Performance Stubs

For a while, I was writing blog posts around identifing the fastest method for accomplishing particular goals. They can be found on my blog under the "performance stub" tag. This is the repository where I will collect all of the test code along with the framework I used to run the tests.

##Background

As I code, I like to make some light notes of alternatives while driving forward with the first implementation that makes it from my brain to my fingers. When I get the chance, I can go back and flesh out the two versions and drop them into some basic Stopwatch timing to determine which is better in terms of raw speed. Factoring those results with clarity of code, I have a method I will likely choose the next time I need the same feature.

##Disclaimer

Don't take these test results as any end-all answer. These are the numbers I got on some random computer I was using at the time. Feel free to use them as a starting point. If you are really digging into performance, you already know that everything can change once you put the code in question in its natural surroundings (e.g., Windows .NET vs. MonoTouch) or put it under load.

###Last Run

  • Intel(R) Core(TM) i7-4850HQ CPU @ 2.30GHz [NOTE: Windows 8.1 running under VMware Fusion]
    • Cores: 4
    • Current Clock Speed: 2295
    • Max Clock Speed: 2295

##Tests (so far)

###Getting object properties by name at runtime (concrete type)

"Getting object properties by name at runtime (concrete type)" (For 100000 iterations.)
AverageMethodRatio
0.42 ticksObjectAccessorLookup12.4X
5.26 ticksIDictionaryRouteValueDictionaryLookup1.0X

###Getting object properties by name at runtime (dynamic ExpandoObject)

"Getting object properties by name at runtime (dynamic ExpandoObject)" (For 100000 iterations.)
AverageMethodRatio
0.23 ticksIDictionaryRouteValueDictionaryLookup1.9X
0.43 ticksObjectAccessorLookup1.0X

###Getting object properties by name at runtime (anonymous type)

"Getting object properties by name at runtime (anonymous type)" (For 100000 iterations.)
AverageMethodRatio
0.44 ticksObjectAccessorLookup13.1X
5.72 ticksIDictionaryRouteValueDictionaryLookup1.0X

###Getting first subtype item from a list

"Getting first subtype item from a list" (For n=1,000,000 (first subtype at 999,900), 100 iterations.)
AverageMethodRatio
44,991.67 ticksSimpleLoop1.6X
47,769.28 ticksFirstOrDefaultAs1.5X
61,196.91 ticksOfTypeFirstOrDefault1.2X
69,718.62 ticksSelectAsFirstOrDefaultNotNull1.0X
71,449.59 ticksSelectAsWhereNotNullFirstOrDefault1.0X

###Getting all subtype items from a list

"Getting all subtype items from a list" (For n=1,000,000 (half items are subtype), 100 iterations.)
AverageMethodRatio
73,176.10 ticksYieldReturnLoop1.4X
88,929.40 ticksWhereIsCast1.1X
89,636.05 ticksOfType1.1X
99,422.73 ticksSelectAsWhereNotNull1.0X

###Converting a byte array to a hexadecimal string

"Converting array of bytes into hexadecimal string representation" (Text (n=1,214,268 bytes), 1000 iterations.)
AverageMethodRatio
4,727.85 ticksByteArrayToHexViaLookup32UnsafeDirect105.2X
10,853.96 ticksByteArrayToHexViaLookupPerByte45.8X
12,967.69 ticksByteArrayToHexViaByteManipulation238.4X
16,846.64 ticksByteArrayToHexViaByteManipulation29.5X
23,201.23 ticksByteArrayToHexViaLookupAndShift21.4X
23,879.41 ticksByteArrayToHexViaLookup20.8X
113,269.34 ticksByteArrayToHexStringViaBitConverter4.4X
178,601.39 ticksByteArrayToHexViaSoapHexBinary2.8X
203,871.66 ticksByteArrayToHexStringViaStringBuilderForEachByteToString2.4X
227,942.39 ticksByteArrayToHexStringViaStringBuilderAggregateByteToString2.2X
452,639.34 ticksByteArrayToHexStringViaStringBuilderForEachAppendFormat1.1X
479,832.66 ticksByteArrayToHexStringViaStringBuilderAggregateAppendFormat1.0X
484,575.84 ticksByteArrayToHexStringViaStringConcatArrayConvertAll1.0X
497,343.99 ticksByteArrayToHexStringViaStringJoinArrayConvertAll1.0X
"Converting array of bytes into hexadecimal string representation" (Text (n=61 bytes), 1000 iterations.)
AverageMethodRatio
0.28 ticksByteArrayToHexViaLookup32UnsafeDirect99.7X
0.65 ticksByteArrayToHexViaLookupPerByte42.7X
0.70 ticksByteArrayToHexViaByteManipulation39.5X
0.73 ticksByteArrayToHexViaByteManipulation237.9X
1.15 ticksByteArrayToHexViaLookup23.9X
1.24 ticksByteArrayToHexViaLookupAndShift22.3X
9.98 ticksByteArrayToHexStringViaBitConverter2.8X
9.98 ticksByteArrayToHexStringViaStringBuilderForEachByteToString2.8X
10.68 ticksByteArrayToHexViaSoapHexBinary2.6X
14.27 ticksByteArrayToHexStringViaStringBuilderAggregateByteToString1.9X
14.88 ticksByteArrayToHexStringViaStringJoinArrayConvertAll1.8X
23.74 ticksByteArrayToHexStringViaStringConcatArrayConvertAll1.2X
24.93 ticksByteArrayToHexStringViaStringBuilderAggregateAppendFormat1.1X
27.51 ticksByteArrayToHexStringViaStringBuilderForEachAppendFormat1.0X

Lookup tables have taken the lead over byte manipulation, especially if you are willing to play in the unsafe realm. Basically, there is some form of precomputing what any given nibble or byte will be in hex. Then, as you rip through the data, you simply look up the next portion to see what hex string it would be. That value is then added to the resulting string output in some fashion. For a long time byte manipulation, potentially harder to read by some developers, was the top-performing approach.

Your best bet is still going to be finding some representative data and trying it out in a production-like environment. If you have different memory constraints, you may prefer a method with fewer allocations to one that would be faster but consume more memory.

##Contributions

If you find something wrong with this stuff or have recommendations for the testing framework, don't hesitate to bring it up. If you have a test idea that would add some value to the world, feel free to write up something that implements IPerformanceTest and submit it. New issues and pull requests are always welcome. If you submit it, I will assume you don't mind it becoming part of this project and subject to its MIT license.

##License

MIT license. If you do something cool with it, though, I'd love to hear about it.