Performance: Enum ToString or GetName
Performance: Enum ToString or GetName
Usually I am too lazy to bother using anything else than the ordinary ToString
when working with enum
s, 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 an enum
value made me want to know if there is a performance difference between the two.
First, the tested Enum.GetName
method, for the sake of argument is 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);
}
}
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
}
I decided to test 2 different enum
s 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
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. :)
Until next time: Work to Live, Don’t Live to Work