Stuart Leeks

Stuart Leeks - Application Development Consultant

Using let in LINQ to Objects - Part 2

Using let in LINQ to Objects - Part 2

Rate This
  • Comments 5

In my previous post, I looked at what the compiler generates when you use the let keyword in LINQ to Objects. This is a follow-up post slanted towards performance. To this end, I set up four tests:

 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've deliberately kept the tests simply (e.g. avoided group by/where statements) to maximise the effect of the let statement in each case. I ran each test 10,000 times and got 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. I wouldn't necessarily recommend using let everywhere but the performance overhead is likely to be minimal, so if it makes the code easier to read then it's probably worth it.

Aside from code readability, the let keyword can 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();
        }

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. An alternative approach is to define a Between function

 static bool Between(decimal value, decimal lower, decimal upper)
        {
                return value < upper && value < lower;
        }

and then re-write the query as

 static void CalcBetween()
        {
            var q = from c in AllCustomers
                    where Between(SumOrders(c), 1000, 10000)
                    select c;
            int count = q.Count();
        }

For comparison, this query came out as 0.7077ms/test, so it's marginally faster that the let query above but it shows that as you start making your query more complicated the overhead of let is less significant.

  • This is a follow-up to my two previous posts on the let keyword Using let in LINQ to Objects Using let

  • Nice post! I guess the efficiency concurs with what people assume intuitively regarding the usage of 'let'

  • Nice article Stuartle. Specially about efficiency.

Page 1 of 1 (5 items)
Leave a Comment
  • Please add 2 and 8 and type the answer here:
  • Post