0

First off, this is not a dupe of this question:

Are anonymous methods defined inline?

I think that asker is asking a different question.

I am inquiring about method inlining where, during the compile process, the call to a method is replaced by the actual of the method.

My understanding of anonymous methods is that they actually compile to concrete methods with a compiler-assigned name. This is done to reduce stack overhead. When anonymous methods are compiled, are they inlined just the same?

Community
  • 1
  • 1
oscilatingcretin
  • 10,457
  • 39
  • 119
  • 206

1 Answers1

0

Anonymous methods work exactly the same way as normal methods - if the JIT compiler decides to inline them, they can be inlined.

However, most likely, you're not calling an anonymous method directly - you are invoking a delegate that points to the anonymous method. In that case, as with any other delegate invokation, the compiler can't inline anything since it doesn't know at compile-time (or JIT-time) which method is actually going to be invoked.

I guess you're confused with how generics work in .NET, rather than anonymous methods explicitly, especially contrasted with C++'s templates - for example, if I do collection.Select(i => i.SomeProperty) in C#, there's still only one method Select[1]; how would you inline the i => i.SomeProperty method when there's other Select calls that take different functions as arguments? In contrast, using templates in C++ allows inlining "function" arguments, since the templates are only a compile-time code generation feature; every use of an analogous Select template in C++ would give you a separate piece of code, no method invocation is involved.

Needless to say, this is just an implementation detail. It would be valid for a future compiler to inline the method call, by pretending it isn't a delegate and inlining the Select method itself.

[1] - Technically, during JIT compilation, there may be multiple versions of the Select method for different type arguments - this has no bearing on this scenario, though; it still doesn't make a different method for every possible argument.

Luaan
  • 62,244
  • 7
  • 97
  • 116
  • During JIT compilation, there *are* multiple versions of the `Select` method, so (in a general case) I don't see why the JIT compiler would be able to inline a (concrete) generic method. However, LINQ is a rather special case, since there is lots of plumbing that happens with `IEnumerable`, and furthermore it's lazy evaluated, so I doubt any of the extension methods like `Select` will get inlined in practice. The only situation I can think of would be with an immediate `ToList`, i.e. `items.Select(i => i.x).ToList()` completely inlined into a loop. – vgru Oct 21 '16 at 12:56
  • (anyway, my point is just that `Select` and other LINQ methods are particularly hard to inline) – vgru Oct 21 '16 at 12:59
  • @Groo Agreed, inlining the actual LINQ extension methods would be difficult, if not impossible. However, my question is referring to the inlining of the anonymous delegate itself. If it's not inlined, calling .Select() would add two calls on the stack, whereas there would be only one if the anonymous delegate body were inlined. – oscilatingcretin Oct 21 '16 at 13:18
  • @oscilatingcretin No, you would need to inline the whole `Select` method in order to inline the anonymous method. There's nowhere to inline the method *to* - either you'd need to have a separate `Select` method for each of the possible arguments which would then be inlined in the `SelectForAnonymousMethod42` method, or you need to inline *both* the methods. – Luaan Oct 21 '16 at 13:45
  • @Groo That's why the footnote says "technically, there may be multiple versions for different type arguments". And there's no contract that says "enumerables are lazily evaluated" - the runtime would be perfectly within its rights if it changed every enumerable to an array, or batched the enumerations. There's a reason why you're not supposed to cause side-effects in the more functional parts of C# - you get hardly any guarantees as to how the enumerable is actually evaulated. – Luaan Oct 21 '16 at 13:48
  • ’Select’ on ’IEnumerable’ is LINQ to objects, and LINQ to objects is lazy evaluated, that's pretty much a guarantee. And "changing every enumerable to an array" is most certainly not a valid transformation. – vgru Oct 21 '16 at 21:02
  • @Groo No, that's your assumption. The only guarantee an enumerable gives you is that you can iterate it. Everything else is just an implementation detail. But if you feel that there's any relevant bit in the C#/.NET specification I missed, or at least something in MSDN that guarantees the behaviour you're assuming, feel free to reference it. For example, the documentation for `Select` only says that the evaluation is deferred until the first `GetEnumerator` call - a far cry from every individual element being lazily evaluated. – Luaan Oct 21 '16 at 21:28
  • So (if I am getting you right, perhaps I am misunderstanding), you are saying that 1) [this snippet](http://pastebin.com/321r0k11) is not guaranteed to print two zeros? Or that 2) [this snippet](http://pastebin.com/2jtXNtVH) won't necessarily print 1, 2, and 3? – vgru Oct 22 '16 at 09:39
  • @Groo Exactly. Mind you, on any MS.NET runtime I've seen, the implementation detail is that the evaluation is one element at a time, projecting from the source collection (which may or may not support modifying the elements - e.g. `List` will throw an exception when you iterate the list and then change it while iterating). But it's not part of the contract - just an implementation detail you shouldn't rely on - it can change at any time in the future, including even minor updates or security hot-fixes. Though given how abused it is and how seriously MS takes backwards compatibility... – Luaan Oct 22 '16 at 11:01
  • @Luaan: I am still not sure at which point you believe the runtime is free to evaluate eagerly? Eagerly evaluate the entire method with `yield` statements as soon as `GetEnumerator` is called? Or, can the BCL be written to make `Select` simply eagerly evaluate the entire list at first `MoveNext`? You are actually saying that invoking `.FirstOrDefault()` and assuming that only a single item will be yielded is "abusing the implementation details"? – vgru Oct 22 '16 at 13:06
  • @Groo You're still looking at it from the entirely wrong perspective. It's not that the runtime is free or not to evaluate eagerly - it's that eager (or lazy) evaluation is *not part of the contract*. There's no point in continuing this unless you can quote the specification/documentation where you are guaranteed to get the kind of evaluation you're expecting. If it's not part of the contract, it's not guaranteed - I don't understand what's so complicated about that concept. – Luaan Oct 22 '16 at 18:17
  • Your words: **"the runtime would be perfectly within its rights if it changed every enumerable to an array"**. You are not talking about `IEnumerable` contract or the implementation of `Select` here, you are talking about the CLR, so I don't know why you even quoted those docs. Having the runtime change every enumerable to an array would 1) change program behavior (I don't think I can quote any spec which says CLR is not within its rights to arbitrarily alter program behavior), 2) would crash on infinite lazy `yield`s (or you can quote a place where it is stated these are not allowed). – vgru Oct 23 '16 at 09:24
  • @Groo Have you read the CLR specification? The runtime is free to change program behaviour that isn't contractual - that's why we have the contract in the first place. If you don't break the contract (e.g. cause side-effects in an enumerable), your program behaviour isn't going to change. And infinite enumerables are allowed of course - that's why I mentioned batching, which is perfectly legal (and used in many other languages that use enumerables, e.g. Clojure). The runtime has contracts. The library has contracts. C# has contracts. Are you accepting the C# ones while ignoring CLR's? – Luaan Oct 23 '16 at 10:43
  • C# `foreach` statement combined with a method which uses `yield return` should guarantee sequential execution. Even the IL compiler must guarantee this behavior. So, CLR can decide to break this behavior, and *I* am ignoring CLR contracts? – vgru Oct 23 '16 at 19:45
  • @Groo This is growing quite tiring. You still haven't cited any part of the specification or documentation that would assert this guarantee. I'm not going to say any more until you do. – Luaan Oct 23 '16 at 20:05