Skip to content

Query/Perf: Compile identifier lambdas passed to PopulateIncludeCollection rather than inline #35212

@maumar

Description

@maumar

Part of the bigger perf issue: #35053

PopulateIncludeCollection (as well as couple other methods) take a number of delegate arguments. For scenarios with significant number of entities we see significant perf improvement when these delegates are compiled (like we used to do in EF8), rather than inlined.

Relevant shaper fragment in EF8

ShaperProcessingExpressionVisitor.PopulateIncludeCollection<SalesOrderHeader, SalesOrderDetail>(
	collectionId: 0, 
	queryContext: queryContext, 
	dbDataReader: dataReader, 
	resultCoordinator: resultCoordinator, 
	parentIdentifier: Func<QueryContext, DbDataReader, object[]>, 
	outerIdentifier: Func<QueryContext, DbDataReader, object[]>, 
	selfIdentifier: Func<QueryContext, DbDataReader, object[]>, 
       (...)

and the same fragment in EF9:

ShaperProcessingExpressionVisitor.PopulateIncludeCollection<SalesOrderHeader, SalesOrderDetail>(
	collectionId: 0, 
	queryContext: queryContext, 
	dbDataReader: dataReader, 
	resultCoordinator: resultCoordinator, 
	parentIdentifier: (queryContext, dataReader) => new object[]
	{ 
		(object)(int?)dataReader.GetInt32(0), 
		(object)(int?)dataReader.GetInt32(26) 
	}, 
	outerIdentifier: (queryContext, dataReader) => new object[]
	{ 
		(object)(int?)dataReader.GetInt32(0), 
		(object)(int?)dataReader.GetInt32(26) 
	}, 
	selfIdentifier: (queryContext, dataReader) => new object[]
	{ 
		(object)dataReader.IsDBNull(33) ? default(int?) : (int?)dataReader.GetInt32(33), 
		(object)dataReader.IsDBNull(34) ? default(int?) : (int?)dataReader.GetInt32(34) 
	}, 
       (...)

Benchmark results:

before the fix (but already with invoke fix and no interpretation)

Method Async Mean Error StdDev Op/s Gen0 Gen1 Allocated
MultiInclue False 455.1 ms 8.94 ms 10.29 ms 2.197 11000.0000 6000.0000 67.92 MB
MultiInclue True 435.4 ms 1.77 ms 1.66 ms 2.297 11000.0000 6000.0000 67.92 MB

after the fix:

Method Async Mean Error StdDev Op/s Gen0 Gen1 Allocated
MultiInclue False 363.3 ms 6.72 ms 6.29 ms 2.752 9000.0000 3000.0000 58.51 MB
MultiInclue True 351.9 ms 2.08 ms 1.74 ms 2.842 9000.0000 3000.0000 58.51 MB

This gets us pretty close to the EF8 numbers which were:

Method Async Mean Error StdDev Op/s Gen0 Gen1 Allocated
MultiInclue False 297.1 ms 1.47 ms 1.30 ms 3.365 8000.0000 6000.0000 52.4 MB
MultiInclue True 290.2 ms 3.76 ms 3.52 ms 3.446 8500.0000 6000.0000 52.4 MB

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions