Comparing speed of ToUpper, ToUpperInvariant, ToLower and ToLowerInvariant in .NET Framework and .NET Core

While I was reviewing some code week or two back, I got idea to test what’s the speed difference between ToUpper , ToUpperInvariant , ToLower and ToLowerInvariant . Of course, these methods are not doing the same thing, but sometimes it doesn’t really matter and if any is sufficient then, maybe, performance wins. Or at least I thought it’s going to be interesting to see whether there’s a difference and how much.

Setup

Obviously, the problem is that the outcome is very likely dependent on the string being processed and unless I want to test all possible combinations I have to choose some subset. My first, self-imposed, constraint was to consider only strings containing A to Z , a to z and 0 to 9 , basically only letters and numbers from ASCII. Once you start mixing in national characters it’s really a different game and this comparison starts to fall apart. From these characters I made strings of length between 1 to 10 and then 100 and 255. I made bunch of permutations from these and used the trusty BenchmarkDotNet to execute it multiple times to get even more data averaging all the results at the end (one run took almost 3 hours). Thus for i.e. strings of length 1 I ended up testing these: 6 , 8 , E , f , H , I , P , Q , r , U .

The CPU was Intel Xeon E5-2673 v3 2.40GHz. The .NET Framework used was .NET Framework 4.7.2 (CLR 4.0.30319.42000), 64bit RyuJIT-v4.7.3163.0 and .NET Core was .NET Core 2.1.4 (CoreCLR 4.6.26814.03, CoreFX 4.6.26814.02), 64bit RyuJIT . All times are in ns .

Numbers

Let’s first talk about regular .NET Framework.

ToLower ToLowerInvariant ToUpper ToUpperInvariant 1 121,821 78,557 79,566 79,983 2 125,223 81,275 82,549 82,751 3 126,422 83,580 85,302 86,042 4 129,046 86,878 89,030 88,969 5 131,579 90,169 91,681 91,847 6 134,335 92,991 94,046 94,389 7 136,308 95,596 96,728 97,531 8 140,079 98,496 99,832 100,498 9 143,711 102,215 102,904 102,866 10 147,154 104,303 105,456 105,646 100 420,101 373,882 373,715 374,270 255 867,178 816,733 818,960 818,539

Clearly the ToLower is slowest. The other three are mostly the same. But in general, ToLowerInvariant is fastest, then ToUpper and then ToUpperInvariant .

Surprisingly, the .NET Core is roughly 2,6× faster across the results.

ToLower ToLowerInvariant ToUpper ToUpperInvariant 1 46,337 29,144 25,825 25,827 2 48,319 33,005 32,178 32,254 3 48,521 33,319 35,515 35,391 4 55,158 38,244 37,282 36,977 5 55,721 40,372 40,028 39,932 6 56,587 40,790 43,153 42,883 7 59,739 42,180 40,165 40,204 8 64,512 46,196 43,814 44,070 9 65,980 47,958 43,545 43,631 10 63,752 46,374 49,083 49,179 100 212,262 193,184 198,214 198,144 255 486,015 463,655 469,989 468,428

I was not expecting that. The ToLower is still slowest and then the remaining methods. Compared to .NET Framework the pattern on remaining three is not consistent. But if you’d ask me, I would order it ToUpper fastest, then ToUpperInvariant and then ToLowerInvariant .

The memory allocations seem to be same for both .NET Framework and .NET Core for all methods for given string lengths.

Summary

Given the speed of the method also depends on the string itself I don’t think we can come to a one-size-fits-all conclusion. But as long as you have a choice, in general, avoiding ToLower seems to be a safe bet. Especially given that string comparisons in upper case are preferred (but one should not forget about case insensitive string handling in the first place).

Now I have the temptation to explore where does the speed difference come from (but I have a feeling I would burn a lot of time exploring that). Or, if you know, teach me in the comments.