Performance Issues When Using Let in LINQ

Let is a powerful keyword in LINQ which allows us to assign a value to a temporary variable which can be reused in the LINQ statement. However, there can be performance penalties for using Let incorrectly.

Examples of Using Let in LINQ

static void TestBaseline()
{
    var q = from c in Customer.AllCustomers
        select c;
    int count = q.Count();
}
static void TestWithAllocation()
{
    var q = from c in Customer.AllCustomers
        select new Customer() { CustomerID = c.CustomerID, CompanyName = c.CompanyName,
                    ContactName = c.ContactName, ContactTitle = c.ContactTitle,
                    Address = c.Address, City = c.City, Country = c.Country };
    int count = q.Count();
}
static void TestWithSingleLet()
{
    var q = from c in Customer.AllCustomers
        let customerId = c.CustomerID
        select new Customer() { CustomerID = customerId, CompanyName = c.CompanyName,
                    ContactName = c.ContactName, ContactTitle = c.ContactTitle,
                    Address = c.Address, City = c.City, Country = c.Country };
    int count = q.Count();
}
static void TestWithMultipleLet()
{
    var q = from c in Customer.AllCustomers
        let customerId = c.CustomerID
        let companyName = c.CompanyName
        let contactName = c.ContactName
        let contactTitle = c.ContactTitle
        let address = c.Address
        let city = c.City
        let country = c.Country
        select new Customer() { CustomerID = customerId, CompanyName = companyName,
                    ContactName = contactName, ContactTitle = contactTitle,
                    Address = address, City = city, Country = country };
    int count = q.Count();
}

The first of these tests is a simple count over a list of customers. The second adds in an allocation for comparison with the other tests. The third test adds a single let statement, and the final test goes all out with let statements to exaggerate the effect. I have deliberately kept the tests simple (e.g. avoided group by/where statements) to maximize the effect of the let statement in each case. I ran each test 10,000 times and had the following results:

Test Time per test (ms)
TestBaseLine 0.0051
TestWithAllocation 0.0085
TestSingleLet 0.0155
TestMultipleLet 0.0640

Comparing the test with allocation to the test with the multiple let statements, there is clearly an overhead to using the let keyword, but bear in mind that these tests have been designed to emphasise the effects of using let. Real-world queries are likely to include filtering/grouping etc that will diminish the relative effect of using let. Also, even with seven let keywords the query time is measured in hundreths of a millisecond.

The let keyword can also be used to increase performance. Suppose you have a value that is relatively expensive to calculate, but that needs to appear in the where clause multiple times:

static decimal SumOrders(Customer customer)
{
    var q = from order in customer.Orders
        from Order_Detail orderDetail in order.Order_Details
        select orderDetail.Quantity * orderDetail.UnitPrice;
    GC.KeepAlive(q);
    return q.Sum();
}
static void CalcNoLet()
{
    var q = from c in AllCustomers
        where SumOrders(c) < 10000 && SumOrders(c) > 1000
        select c;
    int count = q.Count();
}
static void CalcWithLet()
{
    var q = from c in AllCustomers
        let expensiveValue = SumOrders(c)
        where expensiveValue < 10000 && expensiveValue > 100
        select c;
    int count = q.Count();
}

 

Advertisement

 

In this example, we're summing the total value of the orders for each customer and only want customers where this value is between 1,000 and 10,000. Without let, this value gets calculated twice for each customer. We can use let to effectively give us query-local storage for the calculation so that it only performs the calculation once per customer. The results for these two queries are:

Test Time per test (ms)
CalcNoLet 0.9387
CalcWithLet 0.7352


The results show that, in this case, the cost of performing the calculation outweighs the cost of the extra query that is generated by the let statement. So despite the addition of the extra query, using let reduces the query execution time.

 

Visit the LINQ Exchange Forum to post your comments, questions, or suggestions. 

GD Star Rating
loading...
GD Star Rating
loading...
Performance Issues When Using Let in LINQ, 4.0 out of 5 based on 1 rating
  • Share/Bookmark

No related posts.

Related posts brought to you by Yet Another Related Posts Plugin.

Leave a Reply