-
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
Champion "params Span<T>" #1757
Comments
Previous discussion: #535 |
Similar issue which could be embraced by this proposal #1758 |
This would not work with the current CLR spec, as stackalloc'ed memory cannot contain references. |
We could fallback to |
This ones mentioned in the prior thread,
As discussed before, we don't need to define any resolution precedence here, you can just move That being said, I propose to disallow such overloads because almost all invocations would be ambiguous and the overload resolution as-is wouldn't make it an error for "free" (in the expanded form). |
What kind of rule did you have in mind for that? |
I would prefer to handle this in the This makes more sense to me because from a caller's standpoint just calling |
@scalablecory The params expansion rule in https://github.com/dotnet/csharplang/blob/master/spec/expressions.md#applicable-function-member makes the underlying type of the params parameter (array or span) vanish for the purposes of overload resolution, so I think that doesn't work.
|
params parameters with identical underlying types would give you a conflicting overload error, regardless of it being array or span (e.g. |
As I said, there's no need to have a preference (if at all possible), for instance, if you have void Foo(params int[] values); You can add an overload and move the params, void Foo(params Span<int> values);
void Foo(int[] values); This is still binary-compatible with existing code, and a recompilation would resolve all those invocations to the new overload. Downside of this approach is that you must upgrade your compiler to be able to use such APIs. That doesn't mean that we need to bump up the lang version, we can continue to expand to span at the call-site even with prior lang versions. |
This would be another great case for #1447 Proposal: Customisable overload resolution |
Would the upcoming work on object stack allocation in CoreCLR (see dotnet/coreclr#20251) make this feature less important? |
@svick wow didn't know that's a thing. I think that would permit using stackalloced Span storage also with references? |
I should note that due to required escape analysis on stackalloced memory, this can't be applied to regular params arrays, if that's what you're implying. |
@alrz Why not? The params array usually does not escape the called function, so escape analysis should figure out that the array can be stack allocated. |
Stack alloc would be great for params array. Expecially the following BCL methods would likely benefit significantly: String.Concat (2 overloads)
String.Format (2 overloads)
String.Join (2 overloads)
String.Split (1 overloads)
String.Trim (1 overloads)
String.TrimEnd (1 overloads)
String.TrimStart (1 overloads)
StringBuilder.AppendFormat (2 overloads)
Task.WaitAll (1 overloads)
Task.WaitAny (1 overloads)
Task.WhenAll (2 overloads)
Task.WhenAny (2 overloads)
TextWriter.Write (4 overloads)
TextWriter.WriteLine (4 overloads) Maybe the Jit can smart and only do stackalloc for arrays bellow a certain size, but it would definatelly be usefull. |
@svick there's no restriction on the callee, it could freely leak out the array instance. That's not true of Span. |
@alrz I don't follow - whether or not the function allows the array to escape and the object accordingly allowed to allocate on the stack, or not, would be deduced on a case-by-case basis. Although, personally, I agree; that decision should be reinforced by explicitly using a Span parameter. |
See: #179 |
it feels like |
100% agree. I've had a few chats since last time I posted on this issue. The conclusion we came to was exactly as you suggested that we need |
I think we should not rush on this and try to come up with a solution which includes passing in |
It would be interesting if the compiler could determine whether to stackalloc or rent from the shared array pool based on the number of arguments, and then take care of proper disposal of the array if we do rent one. |
Because ArrayPool<T>.Rent(int minimumLength) can return an array larger than requested, this would have to do |
My understanding is the team no longer has an appetite for a And I actually agree with this. Collection literals handles this nicely. But then why Or to ask a more pointed question, what's the difference between these two call sites (and when should class designers choose to add
|
Because then you get the perf benefit just by recompiling. You don't need to update any of your own code. It will be an automatic speedup for literally billions of lines of code just from the upgraded .net and compiler alone. Params span gets you that. :-) |
So with regards to my second question(s), the And class designers should always try to define |
If it is a scoped parameter then they will be equal. Params Span is implied 'scoped'.
That would make sense to me as long as they can do something special with the Span. For example, if they actually capture the array, then there would be no point. |
My understanding is that |
Note: whatever tiebreak rule we will have for params we will get likely carry to collection expressions. A core design goal is that they feel and behave the same. |
Oh, I see the proposal now: #7276 |
This can be easily implemented once we have const generics support in IL and a managed type void Test<T>(T x, T y, T z, T w)
{
Use(x, y, z, w);
}
void Use<T>(params Span<T> x) { }
struct Bar { } where var arr = new ValueArray<T, 4>();
arr[0] = x;
arr[1] = y;
arr[2] = z;
arr[3] = w;
Use(arr.AsSpan()); See dotnet/runtime#89730 for details. We already have a fully working MVP implementation of const generics and may introduce const generics formally in .NET 9. So please hold on :) |
If that's going to be a thing I wonder if the LDG should consider how inline arrays might be able to eventually migrate to this feature. |
You don't need to wait for const generics. As long as it's an implementation detail (so it can be changed later), it could be: var arr = new UnspeakableBy4<T>();
arr[0] = x;
arr[1] = y;
arr[2] = z;
arr[3] = w;
Use(arr.AsSpan());
[InlineArray(4)]
file struct UnspeakableBy4<T>
{
public T field0;
public Span<T> AsSpan() => /* ... */;
} It may look weird to create a type just for that... but the compiler already creates types for anonymous delegates and fixed buffers, so one more case is not the end of the world. Not sure how flexible Roslyn policy is about changing under-the-hood implementations of features but just saying this could be a possibility. Thought... on the other hand this could also be done without |
We don't need to wait here at all. Compiler can generate whatever code is suitable depending on what the runtime and bcl make available. Initially, we'll use InlineArray (and we will synthesize the types if necessary). If a better option comes along later, the compiler can switch to it then. |
Just curious: why does the compiler need to use a struct at all? Why not either use stackalloc or just have a bunch of variables that are next to each other and create a span over them? |
Stackalloc only works for unmanaged types. So you could not use it for most of the types you want to pass as params Span. Also, stackalloc, well, allocs. Each time you hit it out will allocate more stack space. So, for example, if you have a foreach loop, and inside the loop you call a params span, then that would allocate each time, growing the stack untenably. |
I missed this.
You need something the runtime actually understands as:
|
Makes sense, was thinking that there may be observable differences. That's probably more true with inline arrays, but as long as the language supports both |
Yup yup! |
I don't think Inline arrays are available today and fill the need for |
This proposal is now subsumed by the championed proposal #7700 for Params Collections. |
As suggested by @alrz in #1412 (comment), we could permit
params Span<T>
to implementparams
parameter-passing without any heap allocation. This could make the use ofparams
methods much more efficient.Proposal: https://github.com/dotnet/csharplang/blob/main/proposals/params-span.md
LDM history:
The text was updated successfully, but these errors were encountered: