LINQ performance and pitfalls

LINQ is beautiful and you may tempted to do some cool queries, but beware, it might perform poorly. Learn from these examples.

Warning: A non-numeric value encountered in /storage/content/29/141529/devblog.gosso.se/public_html/wp-content/plugins/voting/themes/flat/index.php on line 27

Published 9 March 2021

Example 1 – Use ToLookup instead of where clause.

Original code before optimization

LINQ Explained under the hood: First filters all with specific warehouseid, potentially many then Select will run the Map function, ToList is there to actually run/execute the query. So in short a LINQ to populate/map some stock data on store object.

Instead we use ToLookup and if statement

ToLookup is a flexible variant of Dictionary, read more further down.

Performance metrics

before: 7 sec

after: down to 80 ms to max 700 ms in all cases

Example 2 – Replace Where clause with ToLookup

Code before optimization

The code was called foreach variant foreach stock warehouse.

Instead use cache and ToLookup

First i tried ListAllStores.FirstOrDefault(x => x.WarehouseId == warehouseId) which was faster but not optimal, finally used ToLookup like this:

Since the function was called inside loops for potentially 100 warehouse per variant i wanted just to call the lookup maximum once per request since ToLookup has a overhead too. Cache to the rescue, even for only 15 seconds.

Performance metrics

before: 700ms to 1s

after: 480ms

ToLookup vs ToDictionary

ToLookup is more forgiving. ToLookup allows null as key. But you may also query a lookup on a key that doesn’t exist, and you’ll get an empty sequence. Do the same with a dictionary and you’ll get an exception.

A dictionary is a 1:1 map (each key is mapped to a single value), and a dictionary is mutable (editable) after the fact.

A lookup is a 1:many map (multi-map; each key is mapped to an IEnumerable<> of the values with that key), and there is no mutate on the ILookup<,> interface.

An overly simplified way of looking at it is that a Lookup<TKey,TValue> is roughly comparable to a Dictionary<TKey,IEnumerable<TValue>>

Conclusions

  • Don’t use where clause with FirstOrDefault, put the critera in the FirstOrDefault instead
  • if using FirstOrDefault with criteria in an loop, but it in a ToLookup dict, and query with contains
  • use cache, even for some seconds

SEO Terms

  • LINQ danger
  • LINQ Performance traps
  • LINQ poor performance
  • LINQ better performance tips

About the author

Luc Gosso
– Independent Senior Web Developer
working with Azure and Episerver

Twitter: @LucGosso
LinkedIn: linkedin.com/in/luc-gosso/
Github: github.com/lucgosso

Like it? please up vote
4

Leave a Reply

Your email address will not be published. Required fields are marked *