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

LambdaCompiler could reduce Closure.Constants size #18329

Closed
bartdesmet opened this issue Aug 26, 2016 · 0 comments
Closed

LambdaCompiler could reduce Closure.Constants size #18329

bartdesmet opened this issue Aug 26, 2016 · 0 comments
Milestone

Comments

@bartdesmet
Copy link
Contributor

Found when performing closure elimination steps during expression rewriting a la:

int? c = 42;
IQueryable<int?> xs = ...;
var res = xs.Where(x => x > c);

Upon partial evaluation of c by capturing the value of c at enumeration time of res, we end up with a ConstantExpression of type int?. Unlike constants of non-nullable primitive types, the compiled expression tree does not emit IL instructions to construct the constant but stores the value in the Constants array of the Closure instance used by the compiled delegate. On desktop, this also prevents the overload of Compile taking in a MethodBuilder from successfully persisting such an expression tree because it refers to a "live constant" that cannot be represented in IL.

Note that this introduces boxing as well as allocation of an entry in the Constants array, which could be left as null in case it's empty. In case a lot of compiled expressions are kept resident in memory (cf. services for continuous computations I'm working on in Bing), this can have some impact.

Repro:

using System;
using System.Linq.Expressions;
using System.Runtime.CompilerServices;

class Constants
{
    static void Main()
    {
        Print(Expression.Constant(42)); // OK
        Print(Expression.Constant(42, typeof(int?))); // NOK
        Print(Expression.Constant(default(TimeSpan))); // NOK
    }

    static void Print(Expression e)
    {
        Console.WriteLine("Constants in: " + e);

        var f = Expression.Lambda(e).Compile();
        var c = (Closure)f.Target;
        foreach (var v in c.Constants ?? Array.Empty<object>())
            Console.WriteLine(v);

        Console.WriteLine();
    }
}

Note that a case could be made for default values of value types as well (see the TimeSpan case), though checking whether a struct value matches the default value of the struct's type (in order to emit initobj) may be tricky (one way I could think of would be a non-virtual call to ValueType.Equals).

An improvement for nullable types of primitives would be to allow them in CanEmitILConstant and add support in TryEmitILConstant to emit the non-null value, followed by a newobj to construct the Nullable<T> atop of it.

@msftgits msftgits transferred this issue from dotnet/corefx Jan 31, 2020
@msftgits msftgits added this to the 1.1.0 milestone Jan 31, 2020
@ghost ghost locked as resolved and limited conversation to collaborators Dec 29, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants