-
Notifications
You must be signed in to change notification settings - Fork 1k
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
Proposal: Use value types to store query comprehensions' intermediate variables #372
Comments
On the Roslyn repo it was mentioned that the compiler may switch to using tuples instead of anonymous types for range projections: public void MyMethod()
{
var q =
from x in xs
from y in ys
from z in zs
select x + y + zs;
}
public void MyMethod()
{
var q =
.SelectMany(x => ys, (x, y) => (x, y))
.SelectMany(tuple => zs, (tuple, z) => tuple.Item1 + tuple.Item2 + z);
} That would get you the performance benefit you seek effectively for free and without requiring an opt-in. |
Here's the Roslyn conversation: /cc @gafter |
@HaloFour That'd be awesome! It's more or less exactly what I'm looking for. (The main downside is that it requires you to depend on ValueTuple, but that's not so bad.) Do you have a link to that comment? Edit: Thanks for the link! 😄 |
I think that doing this is a good idea, but it should not effect Because of that, I don't see much point in making this opt-in. It might be worth considering if a value type should be used only when the copying overhead is not significant, based on some heuristic. But I think the compiler is likely going to have a better chance of making the right decision in the common case. (And if performance matters to you so much that you actually care about this in a specific piece of code, you're probably still better of not using LINQ there in the first place.) |
I think if the compiler can detect use of LINQ statements on As it stands today, codebases are actively avoiding usage of LINQ when under this condition (or occasionally banning it altogether). |
That is several orders of magnitude more involved. Currently the compiler has zero knowledge of The only thing that the compiler cares about when it comes to determining whether to emit lambdas as functions and delegates is whether the argument accepts a delegate directly, or if it accepts an |
@HaloFour the compiler can very well lower the calls on |
@orthoxerox I think @HaloFour's point wasn't that inlining iterators and lambdas is impossible or a bad idea, just that it's much more complex to implement in Roslyn than this (modest) proposal. In any case I think it's an altogether different feature than stack-allocating intermediate results, and it deserves its own issue. |
Sure, the compiler could do lots of things. But every time this has come up it seems that the compiler team has been very reluctant to consider this. It would effectively mean baking all of the implementation details of LINQ directly into the language and the spec, which would be a large undertaking. The opinion seems to be that such optimization belongs in the JIT or some ahead-of-time compilation/optimization. That said, I wouldn't at all be opposed to the compiler team taking up such an endeavor. I am a fan of improved performance. 😄 |
Is there any language issue here? It sounds more like a compiler feature being requested. |
As this request doesn't affect the syntax of the language at all, nor does it involve any modifications to the language spec, I assume that it belongs on the Roslyn repo? In that case a ping on dotnet/roslyn#8192 seems sufficient. |
Yes, I believe that dotnet/roslyn#8192 is the right place to continue this discussion. |
Fyi, work has started on stack-allocated objects. dotnet/coreclr#20251 |
Problem statement
In high-throughput situations it's often desirable to minimise garbage. You often see advice like "don't use LINQ in hot code because it allocates a lot". One reason for this is that query comprehensions are translated to the query pattern using anonymous objects which live on the heap.
(Of course in practice
dummy
will be a transparent identifier.) Whiledummy
will often be short-lived and won't survive the nursery, if your goal is to minimise garbage it's still preferable to avoid allocating it altogether.You can achieve this by writing your query manually and storing intermediate variables in a custom value type. The example below will run with O(1) allocations:
When your query is long or complicated this translation gets rather tedious rather quickly (although C#7's new
ValueTuple
certainly eases some of the pain). I'd like to be able to use the nice original query syntax but be confident that it won't allocate a lot at run time.Proposed solution
My proposal is to (optionally) translate the original query into one which looks like the manually-written version, by generating
MyStruct
at compile time, much like how anonymous objects already work.It's not always desirable to use value types - it can be expensive to copy large value types around, and existing query providers may not understand expressions that don't use anonymous objects. So I propose having this behaviour disabled by default. Users can enable the value-type translation on a per-method level using an attribute:
The text was updated successfully, but these errors were encountered: