C# Code Performance — List vs. Array
Now, everyone and their momma knows that code performance is more crucial in today’s world, than it ever has been before. Slow code = death in modern day applications.
Given that to be the case, have you ever stopped to think is some bit of code in my code base fast enough? Could it be faster? Does this small bit of inefficient code really matter? The answer to all of the above is yes, absolutely.
Look, I’ve been coding a while. I don’t know everything — nor do I claim to know everything; but I’ll give this my honest analysis and see what I can find that can be faster given strict criteria. There are a number of factors that play into speed in code but I’ll try to benchmark my ideas here the best I can.
Let’s start with my favorite and most frustrating things to code with: lists and arrays.
Why do I like lists?
For one they are super duper simple to use. Instantiate, add your ‘stuff’, and you’re off! They have nice and easy to use accessors and modifiers built right into it. They can be of any size that you need (take that with a grain of salt because duh, yes you can make them too large and break things, but c'mon be real).
They’re absolutely great and sometimes magical to use when performance isn’t an absolute necessity.
Aren’t lists just fancy arrays?
No. A list does not derive from an array. A list encapsulates an array and arrays are of a fixed size.
Why do I not like arrays?
Have you ever used one? They’re just a bit more complex to use. They have to be fixed in size, it’s not so easy to just plop data into them. You have to do a good amount of memory movement to add more data to the array. It’s complicated overall, but given this data structure doesn’t come with all the overhead of list they are extremely performant.
Prove It…
I’m glad you said that!
Let’s take, for example, a Console App. We can use this to prove the point that array preform better than lists. We can prove this using the C# stopwatch class to show timing stats.
Let’s test under these specific conditions:
The collections will be instantiated empty.
The collections will be populated after instantiation.
The collections will hold integers 1 through 10.
The collection will then be looped over.
The time to do this entire process will be displayed after the loop ends.
Awesome let’s code this!
class Program{ static void Main(string[] args) { ProcessList(); ProcessArray(); } static void ProcessList() { Stopwatch stopwatch = new Stopwatch(); stopwatch.Start(); List<int> intList = new List<int>(); intList.Add(1); intList.Add(2); intList.Add(3); intList.Add(4); intList.Add(5); intList.Add(6); intList.Add(7); intList.Add(8); intList.Add(9); intList.Add(10); foreach (var i in intList) { Console.WriteLine(i); } stopwatch.Stop(); Console.WriteLine($"It took {stopwatch.Elapsed} seconds to process the list."); }static void ProcessArray(){ Stopwatch stopwatch = new Stopwatch(); stopwatch.Start(); int[] intArr = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; foreach(var i in intArr) { Console.WriteLine(i); } stopwatch.Stop(); Console.WriteLine($"It took {stopwatch.Elapsed} seconds to process the array."); }}
forgive medium’s ability to display code…
So what is really going on here?
We have a basic console app that has two methods in them:
ProcessList()
ProcessArray()
Each process method starts a stopwatch, instantiates and populates their collections, loops through them, and finally displays the results.
I was pretty floored during this benchmark I thought maybe it would be a 10% performance gain, 20% tops.
It took 00:00:00.0230056 seconds to process the list.
It took 00:00:00.0008005 seconds to process the array.
That is .0150006 (-96%) decrease in just messing around with 10 numbers.
Let’s see with numbers 1 through 100.
It took 00:00:00.0618995 seconds to process the list.
It took 00:00:00.0564329 seconds to process the array.
Not as big a difference in performance.
For these results I didn’t add these numbers by hand, I used the Linq Enumerable to add the range 1 to 100. I counted that instantiation time in the stop watch count.
Moving the instantiation out of that and just looping through the data yields the following:
It took 00:00:00.0703843 seconds to process the list.
It took 00:00:00.0446986 seconds to process the array.
That’s a -42.86% increase in performance (that sounds weird to say it that way), but the time spent crunching numbers in a loop was a much smaller number for the array. That is just mind blowing.
I know that I’ve worked in code bases (and contributed my fair share of lists all over the place) with lists all over but seeing results like this I don’t know that I can continue on using lists so often.
What do you think of these results?