In this post I’ll walk through replacing existing, expensive synchronous loop code with an easy to consume generic wrapper to run the code in parallel and increase performance.
I had an interesting challenge today. I was debugging a bit of code related to RSS feed item retrieval and aggregation and saw an opportunity to increase performance by multithreading my calls to different feed urls.
The code in question got List<string> urls which were links to various RSS feeds and returned a single SyndicationFeed item containing all the items from these various feeds ordered by the PublicationDate in descending order. The problem was the loop of the urls was being done one at a time and as anyone who has had to work with WebRequest and WebResponse objects knows, this can be a little costly for one request, let alone many. I knew I needed to get these requests going in parallel to cut down on all the overhead and wait time.
The existing code to do the requests and retrieve items was already in a self-contained object. The object contained the url strings, and a property which was just a nice front for the expensive request and aggregation function. This function contained a small LINQ statement that basically passed each string through a helper function which would load and return the SyndicationFeed at that url, grab the SyndicationItem collection, sort them descending by PublicationDate and passed that into the constructor of a new SyndicationFeed object before returning it to the caller.
Pretty straight forward.
The helper function took care of creating the HttpWebRequest calls to the url destination, getting the response, using a FeedFormatter to translate the raw XML into and returned a SyndicationFeed.
Since all this code was already written and tested I didn’t really want to rewrite it all from scratch. I wanted to leverage what was there and make it more responsive. I was already talking to a coworker about a similar approach in the context of preparing a talk about multithreading and asynchronous processing in our SharePoint environment to increase our responsiveness. Part of that talk would include some helper objects and approaches that would minimize the amount of code changes required to implement.
I decided the easiest approach would be to replace the LINQ commands that called out to the helper function that did the heavy loading. This meant I’d have List<string> and I would need to return SyndicationFeed. This wouldn’t be very generic but it was easy to get started.
private SyndicationItem AggregateItems(List<string> urls);
For round two I needed a way to put in a delegate that could do something to the strings that were passed in and somehow return the SyndicationItems of a SyndicationFeed. Since a feed is just a collection of items, I changed my return to be IEnumerable<SyndicationItem> to make this easier.
private List<SyndicationItem> AggregateItems(
Func<string, IEnumerable<SyndicationItem> transform);
Now with this it’s easier to see how to convert it to fully generic. The strings would become TSource. No sweat. But I know my delegate is going to return an IEnumerable of SyndicationItem FOR EACH URL! Every TSource parameter is going to result in a collection of SyndicationItem objects. I have to combine them. Well, that’s really only a problem for the implementation but it tells us how we need to decorate the Func<> types and return type for the method.
private List<TResult> AggregateItems<TSource, TResult>(
Func<TSource, IEnumerable<TResult>> transform);
Now we have a method that returns a collection of items of the same TResult type returned by the Func delegate when given a collection of TSource type.
From here the body isn’t so complicated though it might appear to be if you’re not familiar with using the ThreadPool and ManualResetEvent objects. Basically what we’re doing is queuing up on the ThreadPool a delegate that wraps the Func<> delegate to handle the threading. The call to the Func<> delegate is called in the try part of this try / finally and its result is taken. We have a generic List of type TResult already instantiated where we’re going to store the resulting items.
Because it’s not thread safe to alter the collection of items in a generic List, we cast this to ICollection and lock on its SyncRoot object. This will ensure only one thread at a time can be adding items. After a new wrapper has been queued up on the ThreadPool for each TSource item, we check to see if all our threads are complete. If not, we call WaitOne on the ManualResetEvent. This call will block until the final thread calls Set in the finally of the try/finally code block.
Now that we have this in place, how the heck do I use it?
Since it was designed to replace the LINQ instructions, I do just that!
var items = from url in urls
from item in Load(url).Items
orderby item.PublishDate descending
SyndicationFeed feed = new SyndicationFeed(items);
var items = AggregateItems(urls, s => Load(s).Items);
return new SyndicationFeed(items.OrderByDescending(x => x.PublishDate));
Using a quick test harness I was able to reduce from 6 seconds to 4 the time it took to retrieve 3 RSS feeds. For 10 feeds I dropped from 9 seconds to 5. I was getting some strange numbers at first and even though my client was clearly taking a long time, it was showing less than 1/10th of a second to get results even for the same number of items. It turns out even though I was getting a SyndicationFeed, its data source hadn’t been evaluated! It wasn’t until I tried to get the count which was outside my timing code that the true cost was seen. Tricky! For a minute I thought I’d wasted my whole night.
Now, imagine this generic function was already written and available to you. How much effort is it to implement in your code? Not much, I’d say. And that’s the point – to make things available to developers who are less comfortable working with threads. I could give this code to anyone on my team and let them use it. Better yet, I could make it an extension function, throw it in a common library, put it in our local NuGet repository and anyone could add it to their project with a couple clicks and be off and running.
Could this code be more efficient? Sure. The point isn’t this is the epitome of efficient code but rather the effort for any developer to implement is nearly zero. Compared to a more specialized optimization of this particular case which wouldn’t be highly re-usable and would require a great deal more time to write, test, and maintain later, this approach is a quick win and a pretty good solution.
I hope this will serve as inspiration of how to easily increase performance in existing code without requiring major overhauls.