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

Inline literal delegates passed to functions #6498

Open
DemiMarie opened this issue Aug 13, 2016 · 4 comments
Open

Inline literal delegates passed to functions #6498

DemiMarie opened this issue Aug 13, 2016 · 4 comments
Labels
area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI enhancement Product code improvement that does NOT require public API changes/additions optimization tenet-performance Performance related issue
Milestone

Comments

@DemiMarie
Copy link

DemiMarie commented Aug 13, 2016

This is a feature request to inline literal delegates passed to functions, so that the IL generated by

Array.map (fun x -> x) some_array

or its C# equivalent has no indirect dispatch.

category:cq
theme:inlining
skill-level:expert
cost:large

@pgavlin
Copy link
Contributor

pgavlin commented Aug 15, 2016

cc @AndyAyersMS @dotnet/jit-contrib

@russellhadley
Copy link
Contributor

@pgavlin, Andy is out for a while. @kyulee1 is inliner backup.

@AndyAyersMS
Copy link
Member

See my long and rambling series of notes over in #4584 for some context. There is quite a bit of up front work needed to support this, even in very simple / obvious cases.

@msftgits msftgits transferred this issue from dotnet/coreclr Jan 31, 2020
@msftgits msftgits added this to the Future milestone Jan 31, 2020
@ldematte
Copy link

ldematte commented Jul 1, 2020

Hi there, I arrived here from #34634 through #4584. Someone rightly pointed out that discussion/information about inlining delegates is more at home here, even if the two issues are deeply related, so here I am. I though it might be useful to add my scenario both for you guys (to have more use cases if you are going to implement the feature) and for me, to understand if my scenario could benefit from this feature, if it is something even more complicated, and/or if I could/should do something to make the code "easier" on the JITer.

I code mainly in C#, but (like Mike_E in the other thread) my team and I found that we are using a kind of "functional" programming style, rich in delegates.
In most places we do not care too much about inlining, we do not need that level of optimization; in some cases however this optimization is something we would really like to have.
I am working on the streaming core of our applications. You can think of it as a pipeline, were raw data received from a stream need to be transformed and forwarded for further processing (accumulation, further streaming, computation).
Data from the input are of many different types (bitflag, int, short, float, etc.) and each can be transformed through different operations.

We have experimented various approaches on how to build the pipeline in the most efficient way; in some parts we use dynamically compiled expressions (Expression.Compile), but for the most it is a mix of 2 delegate-heavy techniques.

The first is, we use a lot of continuations, were we pass to a operation what we want to do next:

void Operation(T input, Action<T> optionA, Action<T> optionB, Action<Exception> failure) { }

We even have some cases where this is nested, like:

void OptionalRead(Action<int, Action<byte[], int, int>> callback);

OptionalRead((flag, reader) => { 
    if (some condition) { 
        reader(buffer, startPos, len);
    }
}

The other case is in building the operations themselves. Since the choices depend on type and input, are multiple, and they are expanding over the releases but are fixed for a well defined period, we want to avoid to end up with code that is a nightmare of "if", based mostly on conditions that we know are not going to change.

For example
void process() {
if (type == TypeCode.Int32)
if (bitOperation != 0) // <-- bitOperation never changes for this execution
if (bitMask != 0) // same...

We could have composed operations using interfaces and classes with an Invoke method, but this looks a lot like a delegate, so we build a delegate

Func<T, T> BuildBitOperation() {
     if (type == TypeCode.Int32)              
         if (bitOperation == 0) 
             return x => x;
    // all the other cases         
}

which is stored and later used with the other ones.
I know, the examples could seem silly and maybe you may say that building delegates this way is over-complicated, but for our purposes it is working well... the only drawback being that we have lots of callvirt Invoke in IL. And as you can see in the example, there are often paths where the operation ends up being identity. At least for those "fast paths" inlining would be very welcome.

I have read @AndyAyersMS comments on the other issue, and it seems that full inlining is a long way. Also, if I got it correctly, this case (returning a simple delegate with no local captures) would not even benefit from it as it is not invoked immediately, but is it returned and invoked elsewhere.

@BruceForstall BruceForstall added the JitUntriaged CLR JIT issues needing additional triage label Oct 28, 2020
@BruceForstall BruceForstall removed the JitUntriaged CLR JIT issues needing additional triage label Jan 24, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI enhancement Product code improvement that does NOT require public API changes/additions optimization tenet-performance Performance related issue
Projects
None yet
Development

No branches or pull requests

7 participants