Benchmarking Count() vs Count(x => true) performance in C#
Using .Count(x => true)
in C# collections is generally redundant because it's equivalent to simply using .Count()
. However, there are some specific use cases where it might be intentionally used:
1. Filtering with Dynamic Predicate
If you have a condition that can change dynamically, you might use .Count(x => condition)
, and in cases where the condition is always true
, it acts as a fallback.
var items = new List<int> { 1, 2, 3, 4, 5 };
int count = items.Count(x => true); // Same as items.Count()
This might happen when a predicate is generated dynamically but isn't filtering anything in a particular scenario.
2. Preserving LINQ Method Chaining
Sometimes, you may want to keep .Count(x => true)
to maintain consistent method chaining style, especially if conditions are optional.
var users = new List { new User("Alice"), new User("Bob") };
bool applyFilter = false;
int count = applyFilter
? users.Count(u => u.Name.StartsWith("A"))
: users.Count(x => true); // Keeps method chaining consistent
3. Placeholder for Readability
It might be used to explicitly indicate that no filter is being applied, making the intention clear.
var totalUsers = users.Count(x => true); // Explicitly counting all users
This can be useful in cases where conditions are frequently changed, and using a predicate ensures the format remains the same.
4. Avoiding Compiler Errors in Some Generic Scenarios
When working with generics where a filtering condition might sometimes be required, using .Count(x => true)
ensures consistency.
public int GetItemCount(IEnumerable collection, Func? predicate = null)
{
return predicate != null ? collection.Count(predicate) : collection.Count(x => true);
}
5. Write Benchmark Code
Install the BenchmarkDotNet NuGet package in your project.
Create a benchmarking class that compares .Count()
and .Count(x => true)
:
public class CountPerformanceTest
{
private readonly List _numbers;
public CountPerformanceTest()
{
_numbers = Enumerable.Range(1, 1_000_000).ToList(); // 1 million items
}
[Benchmark]
public int CountDirect()
{
return _numbers.Count();
}
[Benchmark]
public int CountTrue()
{
return _numbers.Count(x => true);
}
[Benchmark]
public int CountGreaterZero()
{
return _numbers.Count(x => x >= 0);
}
[Benchmark]
public int EF_WhereCount()
{
MyDbContext context = new MyDbContext();
return context.Table1s.Where(x=>x.col1 != "").Count();
}
[Benchmark]
public int EF_SQL_CountTrue()
{
MyDbContext context = new MyDbContext();
return context.Table1s.SqlQuery("SELECT * FROM Table1 WHERE col1 <> ''").Count(x => true);
}
[Benchmark]
public int EF_SQL_Count()
{
MyDbContext context = new MyDbContext();
return context.Table1s.SqlQuery("SELECT * FROM Table1 WHERE col1 <> ''").Count();
}
[Benchmark]
public int EF_SQL_CountWithPredicat()
{
MyDbContext context = new MyDbContext();
return context.Table1s.SqlQuery("SELECT * FROM Table1").Count(x => x.col1 != "");
}
[Benchmark]
public int EF_Count()
{
MyDbContext context = new MyDbContext();
return context.Table1s.Count(x => x.col1 != "");
}
}
6. Explanation
_numbers.Count()
→ UsesICollection<T>.Count
directly if available (O(1) for List)._numbers.Count(x => true)
→ Iterates through the entire collection (O(N)), making it much slower.- The Number's benchmark tests each method on 1 million elements to highlight performance differences.
- The EF's benchmark tests each method on 100k elements
7. Running the Test
Run the program, and BenchmarkDotNet will generate a detailed performance report like:
Intel Core i7-10710U CPU 1.10GHz, 1 CPU, 12 logical and 6 physical cores
Method | Mean | Error | StdDev | Median |
---|---|---|---|---|
CountDirect | 14.36 ns | 0.350 ns | 1.033 ns | 14.27 ns |
CountTrue | 8,756,153.91 ns | 414,524.776 ns | 1,209,188.316 ns | 8,720,592.19 ns |
CountGreaterZero | 7,473,755.17 ns | 149,453.370 ns | 358,080.756 ns | 7,317,334.38 ns |
EntityFramework benchmark results:
Method | Mean | Error | StdDev |
---|---|---|---|
EF_WhereCount | 15.22 ms | 0.284 ms | 0.266 ms |
EF_SQL_CountTrue | 354.04 ms | 10.065 ms | 29.520 ms |
EF_SQL_Count | 349.30 ms | 7.712 ms | 22.495 ms |
EF_SQL_CountWithPredicat | 574.62 ms | 12.181 ms | 35.339 ms |
EF_Count | 15.50 ms | 0.136 ms | 0.114 ms |
Why to Use .Count()
?
- If you don't need a predicate,
.Count()
is more efficient because it directly retrieves the count forICollection<T>
without iterating. .Count(x => true)
forces iteration inIEnumerable<T>
instead of using a direct count, which can be slower.
Conclusion
🚀 Use .Count()
whenever possible.
📌 Use .Count(x => true)
only if:
- You need to maintain consistent method chaining.
- You're working with a dynamically assigned predicate.