Skip to content

LINQ queries triggers JIT a bit too much #2947

Closed
@razzmatazz

Description

@razzmatazz

I was profiling our CPU-heavy application and apparently there are a lot of traces where jit is invoked to compile some lambdas when issuing a LINQ ISession.Query<TRecord>

This is noticable especially when using dotnet counters monitor -p1, example:

[System.Runtime]
    % Time in GC since last GC (%)                                 8    
    Allocation Rate (B / 1 sec)                               3.2411e+08   
    CPU Usage (%)                                                 69    
    Exception Count (Count / 1 sec)                                9    
    GC Committed Bytes (MB)                                    3,389    
    GC Fragmentation (%)                                          27.191
    GC Heap Size (MB)                                          2,344    
    Gen 0 GC Count (Count / 1 sec)                                 0    
    Gen 0 Size (B)                                               192    
    Gen 1 GC Count (Count / 1 sec)                                 0    
    Gen 1 Size (B)                                            1.1532e+08   
    Gen 2 GC Count (Count / 1 sec)                                 0    
    Gen 2 Size (B)                                            8.8658e+08   
    IL Bytes Jitted (B)                                       2.3988e+08   
    LOH Size (B)                                              4.3898e+08   
    Monitor Lock Contention Count (Count / 1 sec)                116    
    Number of Active Timers                                      312    
    Number of Assemblies Loaded                                  305    
    Number of Methods Jitted                               8,407,800    
    POH (Pinned Object Heap) Size (B)                        898,696    
    ThreadPool Completed Work Item Count (Count / 1 sec)       7,637    
    ThreadPool Queue Length                                        0    
    ThreadPool Thread Count                                       20    
    Time spent in JIT (ms / 1 sec)                               639.439
    Working Set (MB)                                           3,943    

Note Number of Methods Jitted and Time spent in JIT (ms / 1 sec)

The traces point to NhPartialEvaluatingExpressionVisitor.EvaluateSubtree as the culprit. See:

Stack trace example where application code looks like:

                var record = await db.Query<TRecord>().SingleOrDefaultAsync(
                    s => s.ColumnA == entityA
                    && s.ColumnB == entityB);
Thread (0x71E):
  [Native Frames]
  System.Private.CoreLib!System.Reflection.Emit.DynamicMethod.CreateDelegate(class System.Type,class System.Object)
  System.Linq.Expressions!System.Linq.Expressions.Compiler.LambdaCompiler.Compile(class System.Linq.Expressions.LambdaExpression)
  NHibernate!NHibernate.Linq.Visitors.NhPartialEvaluatingExpressionVisitor.EvaluateSubtree(class System.Linq.Expressions.Expression)
  NHibernate!NHibernate.Linq.Visitors.NhPartialEvaluatingExpressionVisitor.Visit(class System.Linq.Expressions.Expression)
  System.Linq.Expressions!System.Linq.Expressions.ExpressionVisitor.VisitBinary(class System.Linq.Expressions.BinaryExpression)
  NHibernate!NHibernate.Linq.Visitors.NhPartialEvaluatingExpressionVisitor.Visit(class System.Linq.Expressions.Expression)
  System.Linq.Expressions!System.Linq.Expressions.ExpressionVisitor.VisitBinary(class System.Linq.Expressions.BinaryExpression)
  NHibernate!NHibernate.Linq.Visitors.NhPartialEvaluatingExpressionVisitor.Visit(class System.Linq.Expressions.Expression)
  System.Linq.Expressions!System.Linq.Expressions.ExpressionVisitor.VisitLambda(class System.Linq.Expressions.Expression`1<!!0>)
  NHibernate!NHibernate.Linq.Visitors.NhPartialEvaluatingExpressionVisitor.Visit(class System.Linq.Expressions.Expression)
  System.Linq.Expressions!System.Linq.Expressions.ExpressionVisitor.VisitUnary(class System.Linq.Expressions.UnaryExpression)
  NHibernate!NHibernate.Linq.Visitors.NhPartialEvaluatingExpressionVisitor.Visit(class System.Linq.Expressions.Expression)
  System.Linq.Expressions!System.Dynamic.Utils.ExpressionVisitorUtils.VisitArguments(class System.Linq.Expressions.ExpressionVisitor,class System.Linq.Expressions.IArgumentProvider)
  System.Linq.Expressions!System.Linq.Expressions.ExpressionVisitor.VisitMethodCall(class System.Linq.Expressions.MethodCallExpression)
  NHibernate!NHibernate.Linq.Visitors.NhPartialEvaluatingExpressionVisitor.Visit(class System.Linq.Expressions.Expression)
  NHibernate!NHibernate.Linq.NhRelinqQueryParser.PreTransform(class System.Linq.Expressions.Expression,class NHibernate.Linq.Visitors.PreTransformationParameters)
  NHibernate!NHibernate.Linq.NhLinqExpression..ctor(value class NHibernate.Linq.QueryMode,class System.Linq.Expressions.Expression,class NHibernate.Engine.ISessionFactoryImplementor)
  NHibernate!NHibernate.Linq.DefaultQueryProvider.PrepareQuery(class System.Linq.Expressions.Expression,class NHibernate.IQuery&)
  NHibernate!NHibernate.Linq.DefaultQueryProvider.ExecuteAsync(class System.Linq.Expressions.Expression,value class System.Threading.CancellationToken)
  NHibernate!NHibernate.Linq.DefaultQueryProvider+<ExecuteAsync>d__25`1[System.__Canon].MoveNext()
  System.Private.CoreLib!System.Runtime.CompilerServices.AsyncMethodBuilderCore.Start(!!0&)
  NHibernate!NHibernate.Linq.DefaultQueryProvider.ExecuteAsync(class System.Linq.Expressions.Expression,value class System.Threading.CancellationToken)
  NHibernate!NHibernate.Linq.LinqExtensionMethods.SingleOrDefaultAsync(class System.Linq.IQueryable`1<!!0>,class System.Linq.Expressions.Expression`1<class System.Func`2<!!0,bool>>,value class System.Threading.CancellationToken)

(( private application code/scrubbed ))

  System.Private.CoreLib!System.Threading.Tasks.TaskSchedulerAwaitTaskContinuation+<>c.<Run>b__2_0(class System.Object)
  System.Private.CoreLib!System.Threading.Tasks.Task.ExecuteWithThreadLocal(class System.Threading.Tasks.Task&,class System.Threading.Thread)
  System.Private.CoreLib!System.Threading.Tasks.TaskScheduler.TryExecuteTask(class System.Threading.Tasks.Task)
  Akka!Akka.Actor.ActorCell.HandleActorTaskSchedulerMessage(class Akka.Dispatch.SysMsg.ActorTaskSchedulerMessage)
  Akka!Akka.Actor.ActorCell.SysMsgInvokeAll(value class Akka.Dispatch.SysMsg.EarliestFirstSystemMessageList,int32)
  Akka!Akka.Dispatch.Mailbox.ProcessAllSystemMessages()
  Akka!Akka.Dispatch.Mailbox.<Run>b__36_0()
  Akka!Akka.Actor.ActorCell.UseThreadContext(class System.Action)
  Akka!Akka.Dispatch.Mailbox.Run()
  System.Private.CoreLib!System.Threading.QueueUserWorkItemCallback.Execute()
  System.Private.CoreLib!System.Threading.ThreadPoolWorkQueue.Dispatch()
  System.Private.CoreLib!System.Threading.PortableThreadPool+WorkerThread.WorkerThreadStart()

Is this performance/jit profile intended, maybe people have workarounds?

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions