efcore: .NET Native - Perf: LINQ queries run slower on Release mode
Friends, I was doing some performance testing before I publish the app on to the store. However here is one critical issue I am seeing.
The below code takes around 8 second to run in Release mode, but runs within just 2 seconds in debug mode. (where the related tables are very small tables and the number of records in the main table is 150)
If the code runs within 2 seconds in debug mode (with debug overhead), I would expect to run this within milliseconds in release mode. If I double the records (150=>300), the amount of time it takes also doubles (as one would expect). I know from the docs using Anonymous LINQ query can result in poor performance in RC1, and I believe it is fixed in RC2. Even it did not, wondering if the “Include” comes under this category, and this is the reason for the performance issue. If so, do we know how we can refactor the “include” that gives me better performance (may be more than debug)?
public async Task<IList<Student>> GetStudentsAsync()
{
var students = await _context.Students
.Include(b => b.Addresses)
.Include(b => b.Emails)
.Include(b => b.Titles)
.Include(b => b.Phones)
.Include(b => b.Websites)
.ToListAsync();
return students;
}
I am using Visual Studio 2015, UWP 5.1.0, Native Tools 1.3.1, Entityframework Core RC2 and SQLite.
About this issue
- Original URL
- State: closed
- Created 8 years ago
- Comments: 99 (31 by maintainers)
Oh goodness. I agreed to take a look at this and then got pulled away on a family emergency… Consider this my public shaming and I’ll push this to the front of my work queue.
That said, System.Linq.Expression are all interpreted on .NET Native and their runtime performance is general: faster if you’re only calling them once or twice, slower if you’re doing things in a hot loop. It’s possible that there’s some bottleneck somewhere we can get patched up but it’ll probably need to be worked around for the time being.
Based on @MichalStrehovsky’s update and data reported by users we believe this issue is resolved by the 5.3.0 version of .NET Native currently in beta.
Note that a user also reported the performance on Debug being 50% worse in the new version. If this becomes an issue for development of it turns out to affect other kinds of application we will deal with it as a new bug.
@abdu292 If you would like to, we have a beta version of the .NET Native compiler toolchain ready to try out.
You’ll need Visual Studio 2017 RC. By default, VS 2017 RC comes with pretty much the same .NET Native compiler that shipped with VS 2015 Update 3, but your project can opt in to get the beta:
Microsoft.NETCore.UniversalWindowsPlatform
(version5.3.0-beta
). Install that.After rebuilding your app, you should see the perf improvements. For your
SQLitePerf.zip
demo, I’m seeing these numbers:OK, I have a workaround for you that makes things better. The performance bug is in a code path that is only used when the shared library feature of .NET Native is enabled. At the time the blog post I’m linking to was written, this feature was opt in, but since Update 2 (?) it’s opt out (because most people would want it).
If you opt out, you’ll avoid the perf bug. It’s still not as fast as CoreCLR, but from what I measured on my machine, we’re within 25% (we haven’t done focused performance work on .NET Native yet - for v1, our focus was “get it working!” - we plan to beat CoreCLR on all perf aspects eventually).
As to the workaround:
Unload project
.Edit
.PropertyGroup
you see:Save the project, right click and select
Reload
. Rebuild and check out the perf.I was able to get this on a phone and get a trace. I am still seeing times on the phone of about 1300 milliseconds versus 350 milliseconds on CoreCLR. The majority of that time is being spent in the interpreter running the LINQ expressions. In particular, we are spending a large amount of time getting the return type of the MethodInfo inside the interpreter. The majority of this time is being spent in TryGetNamedTypeForTypeReference. This seems like a place were we could do some caching to speed this up, but I am not an expert on reflection. Adding @MichalStrehovsky.
Yep. There are workarounds you can hack into your project.json but wanted to make sure you didn’t get caught by more issues.
@abdu292 if
.FromSql
doesn’t work, EF provides API to expose the lower-level ADO.NET components. As @divega suggests, you can write your SQL queries directly as an ADO.NET DbCommand and execute it on the same connection DbContext is using.