Reputation: 35627
I want to sort a list of objects using a value that can take some time to compute. For now I have code like this:
public IEnumerable<Foo> SortFoo(IEnumerable<Foo> original)
{
return foos.OrderByDescending(foo => CalculateBar(foo));
}
private int CalculateBar(Foo foo)
{
//some slow process here
}
The problem with the above code is that it will call calculate the value several times for each item, which is not good. The possible optimization is to use cached value (maybe a dictionary), but it will mean that SortFoo will have to clear the cache after each sorting (to avoid memory leak, and I do want the value to be recalculated on each SortFoo
call).
Is there a cleaner and more elegant solution to this problem?
Upvotes: 3
Views: 5154
Reputation: 7730
It appears that .OrderBy()
is already optimized for slow keySelector
s.
Based on the following, .OrderBy()
seems to cache the result of the keySelector
delegate you supply it.
var random = new Random(0);
var ordered = Enumerable
.Range(0, 10)
.OrderBy(x => {
var result = random.Next(20);
Console.WriteLine("keySelector({0}) => {1}", x, result);
return result;
});
Console.WriteLine(String.Join(", ", ordered));
Here's the output:
keySelector(0) => 14
keySelector(1) => 16
keySelector(2) => 15
keySelector(3) => 11
keySelector(4) => 4
keySelector(5) => 11
keySelector(6) => 18
keySelector(7) => 8
keySelector(8) => 19
keySelector(9) => 5
4, 9, 7, 3, 5, 0, 2, 1, 6, 8
If it were running the delegate once per comparison, I'd see more than just one invocation of my keySelector
delegate per item.
Upvotes: 8
Reputation: 19646
Because each item is compared against other items multiple times in a sort, you can cheaply cache the computation at least one-per-item. If you're often running the calculation against the same values, Memoizing the function would be your best bet,
public IEnumerable<Foo> SortFoo(IEnumerable<Foo> original)
{
return foos
.Select(f => new { Foo = f, SortBy = CalculateBar(f) })
.OrderByDescending(f=> f.SortBy)
.Select(f => f.Foo);
}
This will reduce the calculations to once per item
Upvotes: 3