As discussed briefly in https://github.com/dotnet/corefx/issues/13427 I propose to add a Fill(T value) method to Span<T> to make span the goto place for safe and fast memory operations.
This is analoguous to the same method approved for arrays in https://github.com/dotnet/corefx/issues/6695 and added in https://github.com/dotnet/coreclr/pull/8137, https://github.com/dotnet/corert/pull/2259 but still lacking optimization https://github.com/dotnet/coreclr/issues/8290, perhaps Array.Fill<T> could forward to Span<T>.Fill in the future...
Provide a safe yet fast way of filling of any type of contiguous memory; managed or unmanaged.
var span = new Span<int>(array, 3, 10);
span.Fill(42);
The argumentation for adding this is similar to Array.Fill<T>, but here with a wider use case, since this can be used with both managed and unmanaged memory:
Add a single method Fill(T value) to the existing Span<T> API:
public class Span<T>
{
public void Fill(T value);
}
Open question is whether this should be added as a member method or an extension method. I would argue for a member method, since this will depend on internal representation, and would perhaps allow for the JIT intrinsic version to do optimizations that a generic extension method can't. If that is a valid argument?
@karelz @jkotas @omariom @benaadams @jamesqo
Can we call it memset :trollface:
Also an offset+count method?
public class Span<T>
{
public void Fill(T value);
public void Fill(T value, int offset, int count);
}
Unless this pattern seems ok?
span.Slice(1, 12).Fill(42);
@benaadams I think having an offset+count one is redundant. Span is not like an array where you fundamentally cannot pass part of it around, so having that overload is just syntax sugar.
so having that overload is just syntax sugar
It is, if there is no slice cost, therefore no perf difference between
span.Slice(1, 12).Fill(42);
and
span.Fill(42, 1, 12);
They may end up being the same; e.g. Slice will bounds check; but Fill with extra params will also bounds check; Fill without extra params wouldn't have to bounds check.
If they both inlined, it would likely end up the same?
@benaadams 'syntax sugar' was not the correct term; I meant to say that it was redundant. The whole point of introducing Span was to avoid the offset/count pattern, wasn't it?
The whole point of introducing
Spanwas to avoid the offset/count pattern, wasn't it?
Correct. You can tell from the existing Span surface, e.g. none of the Copy methods take offset/counts.
Just pointing out without offset/count its pushing a lot into inline Slicing.
Its a question of granularity and performance of Slice + Action.
So if I had two spans that represented a fixed size message packet and I wanted to copy a section from one to another the usage pattern would be:
spanSrc.Slice(4, 16).CopyTo(spanDst.Slice(4, 16));
Rather than
spanSrc.CopyTo(4, spanDst, 4, 16);
Which may be fine and end up with the same instructions being executed; or it may end up with extra items on the stack and extra checks being done.
@benaadams If it is less performant then that would probably be considered a problem with the JIT & should be fixed instead of being worked around, no?
should be fixed instead of being worked around, no?
Ideally, then it would hopefully also pick up similar patterns in other code.
span.Slice(1, 12).Fill(42);
spanSrc.Slice(4, 16).CopyTo(spanDst.Slice(4, 16));
Yes, I think that is the beauty of the Span/Slice API, we avoid combinatorial explosion in overloads so one only needs one version of each method for a given functionality.
E.g. CopyTo, Clear, Fill and any other method currently available for Array that I think should be made available for Span e.g. Sort, FindIndex... I think of Span<T> as a low level cousin of array that has the benefit of being useable on unmanaged memory. For example, how can I sort an unmanaged array in C# currently? There is no support for this. Span<T> unifies this. Not sure whether other people feel like that is the case or even ideal...
Just pointing out without offset/count its pushing a lot into inline Slicing.
I have the same concern here, but hope that this would be given attention if that was the case, and until this is proven a problem we should not design around it.
API review: Approved.
Note the issue is still up for grabs if anyone wants to take it.
It looks like it will be a little bit involved implementation (see attempt in dotnet/coreclr#8498)
@karelz I hope to give it a shot, but again am waiting for the Unsafe additions. Otherwise, it seems @jkotas is ok with a functional commit, before an optimized commit.
Thanks! Assigned to you.
Most helpful comment
Can we call it memset :trollface: