12 Ways to Add an Array of Integers in C# — Part 5: All Together Now
By: Michael Gant
Tags: Coding, Software Development, Programming, Csharp

Photo by Ruiyang Zhang from Pexels

Way back when I first started this series, I promised a total of 12 ways to add an array of integers in C#. So far I’ve delivered 10 of them (plus a JavaScript version and an F# version). To recap:

Part 1: (1) a simple for loop, (2) a variation of that using goto, and (3) another variation using pointers

Part 2: (4) a foreach loop and (5) using Enumerator explicitly

Part 3: (6) Enumerable.Aggregate and (7) Enumerable.Sum

Part 4: Recursively, using (8) arrays, (9) Linq, and (10) pointers

Where else could there possibly be to go from there? As Vizzini said (right before unwittingly poisoning himself): “I’m just getting started!”

Really, though, I’m not. I’m almost out of ideas. In fact, the next one is so silly it almost doesn’t count. But I’m going to use it to lead to something more interesting, so bear with me.

Reading from a queue

There are two snippets here.

Not even all that interesting, is it? It’s useful in coding interviews (remember that’s where this whole thing started) for discussing extension methods. But otherwise it’s just a more complicated way to do the same thing we did in the very first version.

Worse, it’s actually on the slow side compared to the other methods. And not surprising, because it’s a lot more complex than the others, with no real benefit.

But could this lead to something better? What if, for example, we replaced the Queue<T> with a ConcurrentQueue<T> ? Could we multi-thread this baby and get it running in half the time or less?

Parallel using concurrent queue

Let’s give it a shot.

No problem! We just run take the queuing example from before, split it into four parallel tasks, and watch it burn. The Interlocked.Add there keeps us from messing up on the adding.

So does it actually work? Well, sort of. I can only go by my own laptop, which has two decent cores but isn’t setting any speed records. But from my results so far, this method is catastrophically slow. Like 10X or more slower than the average. To be honest, I’m not really sure exactly why. It’s probably just too much scaffolding for such a little operation as adding one number to another. For a longer-running, more complex operation, this approach might actually work.

So technically that’s our 12 ways. But just in case anybody thought #8 and #9 were basically the same — they sort of were, but at some level almost everything here is a different version of the same thing — or that #11 was too ridiculous to count, here’s one more method, which is actually a bit of an improvement on #12.

Parallel using rectangular array

There’s more than one way to provide thread-safe concurrent access to a list of things.

There’s a lot to this, but to summarize: we take the one-dimensional array and rearrange it into a multi-dimensional array, and then process all of the dimensions in parallel. It’s more complicated than the concurrent queue version, but it’s also vastly better performing. In fact, it’s slightly above average among the other methods. Sadly, it’s nowhere near the fastest, probably because of the setup costs.

Conclusion

So what’s the point of all this? Superficially, it’s a way to spelunk the depths of a potential team member’s knowledge of the C# language, for whatever that’s worth. More importantly, talking through these examples is a way of gauging the breadth and depth of a person’s understanding of how computer programming works. These various methods demonstrate important concepts in software development generally, not just with C#. A person that understands them well is likely to be a positive contributor to your team. A person that is hazy on the concepts but asks good questions and shows a real interest is in a good position to learn and grow.

To recap, here’s the complete list:

(1) for loop: the most commonly understood and among the fastest

(2) goto : about as fast as for , not really any faster, and way more confusing

(3) pointers: pretty fast, but not the fastest; no good reason to do it

(4) foreach : another common solution, almost as fast as for

(5) Enumerator : same basic idea as foreach , but more complicated and not any faster

(6) Enumerable.Aggregate : a great way to do it in JavaScript (reduce), but not so good in C#; one of the slower methods

(7) Enumerable.Sum : Not quite the fastest, but pretty close, and by far the simplest. Sometimes fails on larger data sets.

(8) Recursive with arrays: a good idea in F# due to tail call optimization, but not in C#; stack overflow on larger data sets, fair performance on smaller data sets

(9) Recursive with Linq: same drawbacks as recursive with arrays, worse performance

(10) Recursive with pointers: surprisingly good performance, but still fails on large data sets

(11a) Queue: needlessly complex and below-average performance

(11b) Concurrent queue: surprisingly abysmal performance

(12) Rectangular array: only about average performance, for a lot more complexity

Thanks for coming along with me on this journey. You’d never do most of the stuff I’ve described here, but hopefully there are some techniques in here that you’ll find helpful for more appropriate applications.

Any other suggestions? I’d love to hear your comments.