Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Using Futures takes a lot of JIT compilation time #779

Closed
ArtemAvramenko opened this issue Oct 5, 2023 · 8 comments
Closed

Using Futures takes a lot of JIT compilation time #779

ArtemAvramenko opened this issue Oct 5, 2023 · 8 comments
Assignees

Comments

@ArtemAvramenko
Copy link

ArtemAvramenko commented Oct 5, 2023

1. Description

I've been checking what resources are being spent on in my application and found that a significant amount of time is spent on JIT compilation. As it turns out, the reason is the use of Futures.

2. Exception

Test application metrics (the same thing happens in a real application):

Time spent in JIT (ms / 1 sec)                                        98.998

It would be great if EntityFramework-Plus would cache compilation results so that performance would increase dramatically.

3. Fiddle or Project

Even the simplest example reveals the problem:

        static void Test()
        {
            using var context = new MyContext();
            var usersFuture = context.Users.Future();
            var companiesFuture = context.Companies.Future();
            _ = usersFuture.ToList();
            _ = companiesFuture.ToList();
        }

Full code - https://gist.github.com/ArtemAvramenko/46afef540b1def14d42a9c24f272c745

4. Any further technical details

I used dotnet-counters to collect the metrics.

Update: If you remove Futures from the code, the "Number of Methods Jitted" metric stops growing and "Time spent in JIT" becomes zero.

@JonathanMagnan JonathanMagnan self-assigned this Oct 5, 2023
@JonathanMagnan
Copy link
Member

Hello @ArtemAvramenko ,

Are you including the time that EF Core use to JIT compile itself? Our library is very small compared to everything that EF Core had to compile to execute the first query. Even if you use our library, we still use EF Core and you cannot include the part when calculating the time of JIT.

Best Regards,

Jon

@ArtemAvramenko
Copy link
Author

ArtemAvramenko commented Oct 5, 2023

Hello @JonathanMagnan,

I certainly removed Futures for comparison. CPU load drops several times after that and Time spent in JIT becomes equal to zero.

@JonathanMagnan
Copy link
Member

I certainly removed Futures for comparison

Did you add these 2 lines to have a good comparison?

var users = context.Users.ToList();
var companies = context.Companies.ToList();

From what I remember, this is when you make the first query that EF Core will JIT compile most of his method. I feel it weird that the JIT compile will be close to zero without our library. We will surely investigate more if you tell me this is the behavior you still have.

@ArtemAvramenko
Copy link
Author

ArtemAvramenko commented Oct 6, 2023

@JonathanMagnan Yes, that's exactly the code I was comparing it to. With it, JIT compilation is called only at the first call. But if Futures are added, then each time Test() is called, new +4 methods are generated.

@ArtemAvramenko
Copy link
Author

The problem remains in version 8.x

  • EF version: [EF Core v8.0.0]
  • EF Plus version: [EF Plus Core v8.101.1.3]
  • Database Server version: [SQL Server 2022]
  • Database Provider version (NuGet): [Microsoft.Data.SqlClient v5.1.1]

@JonathanMagnan
Copy link
Member

Hello @ArtemAvramenko ,

We made some progress in finding out exactly where the JIT issue started, but to be honest, I'm not sure if we will be able to fix it or not.

We will look one more time to find out exactly if we can skip a line of code that causes it or not.

@JonathanMagnan
Copy link
Member

Hello @ArtemAvramenko ,

We made more investigation this week, and I believe we will simply close as work as intended

When creating a query, EF Core compiles it and caches the query plan. So the next time the same query is called, it doesn't longer require JIT compile or at least calls some method that causes a JIT compile, such as the one found here: https://github.com/dotnet/efcore/blob/397dc68aa9599f3fd7bf04b5e447614dfd3f90c2/src/EFCore/Query/QueryCompilationContext.cs#L169

So it explain why the JIT compile is 0 when running the query multiple times without our library.

On our side, the query is always re-created as we combine them. We don't cache the plan either. So we always need to execute some of the code and the visitor found in my previous link every time.

Could it be possible to improve this to reduce the JIT compile time? Surely, but at this point, I believe we are currently too deep in methods specific to EF Core to really make it happen.

Let me know if at least the explanation makes sense.

Best Regards,

Jon

@ArtemAvramenko
Copy link
Author

Hello @JonathanMagnan,

I imagined it was possible to cache the result without recompiling again. But obviously there will be a lot of pitfalls, such as different SQL that EF generates depending on the parameter values. This and many other things will have to be taken into account when creating the cache.

At the moment, I will try to remove the use of EntityFramework-Plus and see how negatively it will affect the load on the database. A slight increase in the load on the database will be acceptable to me, as it will significantly reduce the application's load on the CPU.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

No branches or pull requests

2 participants