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

Expression compilation is slower in Net7. #76058

Closed
lsoft opened this issue Sep 23, 2022 · 7 comments
Closed

Expression compilation is slower in Net7. #76058

lsoft opened this issue Sep 23, 2022 · 7 comments

Comments

@lsoft
Copy link

lsoft commented Sep 23, 2022

Description

Our app needs to compile more than 50000 expressions and the start. We noticed that the app starts slower when running under Net7 for more than 20 seconds.

Configuration

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFrameworks>net5.0;net6.0;net7.0</TargetFrameworks>
    <ImplicitUsings>enable</ImplicitUsings>
    <LangVersion>Latest</LangVersion>
    <Nullable>enable</Nullable>
    <TieredCompilation>false</TieredCompilation>
    <TieredCompilationQuickJit>false</TieredCompilationQuickJit>
    <TieredCompilationQuickJitForLoops>false</TieredCompilationQuickJitForLoops>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="BenchmarkDotNet" Version="0.13.2" />
  </ItemGroup>

</Project>
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Jobs;
using BenchmarkDotNet.Running;
using System.Linq.Expressions;
using System.Reflection;

BenchmarkSwitcher.FromAssembly(typeof(Program).Assembly).RunAll();

[SimpleJob(RuntimeMoniker.Net50)]
[SimpleJob(RuntimeMoniker.Net60)]
[SimpleJob(RuntimeMoniker.Net70)]
public class Fixture
{
    private PropertyInfo _propertyInfo;

    public delegate object GetterFunc(object instance);

    public Fixture()
    {
        _propertyInfo = typeof(Subject).GetProperty(nameof(Subject.MyProperty))!;
    }

    [Benchmark]
    public GetterFunc Getter()
    {
        ParameterExpression instance = Expression.Parameter(typeof(Object), "instance");
        UnaryExpression castedInstance = Expression.ConvertChecked(instance, _propertyInfo.DeclaringType!);
        MemberExpression property = Expression.Property(castedInstance, _propertyInfo);
        UnaryExpression convert = Expression.Convert(property, typeof(object));
        LambdaExpression expression = Expression.Lambda<GetterFunc>(convert, instance);

        return (GetterFunc)expression.Compile();
    }

}

public class Subject
{
    public string MyProperty
    {
        get;
        set;
    }
}

Regression?

Looks so. Net6 compiles expression faster.

Data

BenchmarkDotNet=v0.13.2, OS=Windows 10 (10.0.19044.1586/21H2/November2021Update)
Intel Core i7-8550U CPU 1.80GHz (Kaby Lake R), 1 CPU, 8 logical and 4 physical cores
.NET SDK=7.0.100-rc.1.22431.12
  [Host]   : .NET 7.0.0 (7.0.22.42610), X64 RyuJIT AVX2
  .NET 5.0 : .NET 5.0.17 (5.0.1722.21314), X64 RyuJIT AVX2
  .NET 6.0 : .NET 6.0.8 (6.0.822.36306), X64 RyuJIT AVX2
  .NET 7.0 : .NET 7.0.0 (7.0.22.42610), X64 RyuJIT AVX2


| Method |      Job |  Runtime |      Mean |    Error |   StdDev |
|------- |--------- |--------- |----------:|---------:|---------:|
| Getter | .NET 5.0 | .NET 5.0 |  85.19 us | 1.356 us | 1.393 us |
| Getter | .NET 6.0 | .NET 6.0 |  80.47 us | 1.578 us | 1.476 us |
| Getter | .NET 7.0 | .NET 7.0 | 115.02 us | 2.291 us | 3.499 us |
@lsoft lsoft added the tenet-performance Performance related issue label Sep 23, 2022
@ghost ghost added the untriaged New issue has not been triaged by the area owner label Sep 23, 2022
@ghost
Copy link

ghost commented Sep 23, 2022

Tagging subscribers to this area: @cston
See info in area-owners.md if you want to be subscribed.

Issue Details

Description

Our app needs to compile more than 50000 expressions and the start. We noticed that the app starts slower when running under Net7 for more than 20 seconds.

Configuration

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFrameworks>net5.0;net6.0;net7.0</TargetFrameworks>
    <ImplicitUsings>enable</ImplicitUsings>
    <LangVersion>Latest</LangVersion>
    <Nullable>enable</Nullable>
    <TieredCompilation>false</TieredCompilation>
    <TieredCompilationQuickJit>false</TieredCompilationQuickJit>
    <TieredCompilationQuickJitForLoops>false</TieredCompilationQuickJitForLoops>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="BenchmarkDotNet" Version="0.13.2" />
  </ItemGroup>

</Project>
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Jobs;
using BenchmarkDotNet.Running;
using System.Linq.Expressions;
using System.Reflection;

BenchmarkSwitcher.FromAssembly(typeof(Program).Assembly).RunAll();

[SimpleJob(RuntimeMoniker.Net50)]
[SimpleJob(RuntimeMoniker.Net60)]
[SimpleJob(RuntimeMoniker.Net70)]
public class Fixture
{
    private PropertyInfo _propertyInfo;

    public delegate object GetterFunc(object instance);

    public Fixture()
    {
        _propertyInfo = typeof(Subject).GetProperty(nameof(Subject.MyProperty))!;
    }

    [Benchmark]
    public GetterFunc Getter()
    {
        ParameterExpression instance = Expression.Parameter(typeof(Object), "instance");
        UnaryExpression castedInstance = Expression.ConvertChecked(instance, _propertyInfo.DeclaringType!);
        MemberExpression property = Expression.Property(castedInstance, _propertyInfo);
        UnaryExpression convert = Expression.Convert(property, typeof(object));
        LambdaExpression expression = Expression.Lambda<GetterFunc>(convert, instance);

        return (GetterFunc)expression.Compile();
    }

}

public class Subject
{
    public string MyProperty
    {
        get;
        set;
    }
}

Regression?

Looks so. Net6 compiles expression faster.

Data

BenchmarkDotNet=v0.13.2, OS=Windows 10 (10.0.19044.1586/21H2/November2021Update)
Intel Core i7-8550U CPU 1.80GHz (Kaby Lake R), 1 CPU, 8 logical and 4 physical cores
.NET SDK=7.0.100-rc.1.22431.12
  [Host]   : .NET 7.0.0 (7.0.22.42610), X64 RyuJIT AVX2
  .NET 5.0 : .NET 5.0.17 (5.0.1722.21314), X64 RyuJIT AVX2
  .NET 6.0 : .NET 6.0.8 (6.0.822.36306), X64 RyuJIT AVX2
  .NET 7.0 : .NET 7.0.0 (7.0.22.42610), X64 RyuJIT AVX2


| Method |      Job |  Runtime |      Mean |    Error |   StdDev |
|------- |--------- |--------- |----------:|---------:|---------:|
| Getter | .NET 5.0 | .NET 5.0 |  85.19 us | 1.356 us | 1.393 us |
| Getter | .NET 6.0 | .NET 6.0 |  80.47 us | 1.578 us | 1.476 us |
| Getter | .NET 7.0 | .NET 7.0 | 115.02 us | 2.291 us | 3.499 us |
Author: lsoft
Assignees: -
Labels:

area-System.Linq.Expressions, tenet-performance

Milestone: -

@reflectronic
Copy link
Contributor

Related to #75891? Can you try this on the latest .NET 7 builds in dotnet/installer: https://github.com/dotnet/installer/tree/release/7.0.1xx

@lsoft
Copy link
Author

lsoft commented Sep 23, 2022

@reflectronic sorry, I'm not so qualified to build dotnet tools from scratch.

@lsoft
Copy link
Author

lsoft commented Sep 23, 2022

@reflectronic I found the link in the table)) Looks like it's fixed:

BenchmarkDotNet=v0.13.2, OS=Windows 10 (10.0.19044.1586/21H2/November2021Update)
Intel Core i7-8550U CPU 1.80GHz (Kaby Lake R), 1 CPU, 8 logical and 4 physical cores
.NET SDK=7.0.100-rtm.22472.21
  [Host]   : .NET 7.0.0 (7.0.22.46606), X64 RyuJIT AVX2
  .NET 7.0 : .NET 7.0.0 (7.0.22.46606), X64 RyuJIT AVX2

| Getter | .NET 7.0 | .NET 7.0 | 86.33 us | 0.675 us | 0.563 us |

(note, net6 is faster a bit yet)

Thank you!

@lsoft lsoft closed this as completed Sep 23, 2022
@ghost ghost removed the untriaged New issue has not been triaged by the area owner label Sep 23, 2022
@stephentoub
Copy link
Member

note, net6 is faster a bit yet

This is a performance vs security tradeoff, and we're defaulting to a more secure implementation at the expense of a small perf impact (which we can also work to decrease further in time).

@Clockwork-Muse
Copy link
Contributor

Our app needs to compile more than 50000 expressions and the start.

.... are source generators an option for you? That is, do you actually need the runtime generation, or is it just attempting to generate something that would otherwise require lots of manual code writing.
That aside, are you caching the results of the expression generation?

@lsoft
Copy link
Author

lsoft commented Sep 26, 2022

@Clockwork-Muse yeah, SG is an option, but expensive option))

are you caching the results of the expression generation?

yep))

@ghost ghost locked as resolved and limited conversation to collaborators Oct 26, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

4 participants