12

Replace arguments with Span in methods?

Should I replace all my array (such as byte[], char[], and string[]) parameters in my synchronous methods with Span (such as Span<byte>, Span<char>, and Span<string>)?

Example:

public void Foo(byte[] bytes)

With:

public void Foo(Span<byte> bytes)

Replace arguments with Memory in async methods?

Should I replace all my array (such as byte[], char[], and string[]) parameters in my asynchronous methods with Memory (such as Memory<byte>, Memory<char>, and Memory<string>)?

Example:

public async Task FooAsync(byte[] bytes)

With:

public async Task FooAsync(Memory<byte> bytes)

Replace return type with Span in methods?

Should I replace all my array (such as byte[], char[], and string[]) return types in my methods with Span (such as Span<byte>, Span<char>, and Span<string>)?

Example:

public byte[] Foo()

With:

public Span<byte> Foo()

Hmm...

  • Are arrays as method arguments undesired? Is Span favored?
  • Are arrays as return types undesired? Is Span favored?

I've read a couple of articles about Span, ReadOnlySpan and Memory but find it all a bit difficult to grasp for my puny little mind. Is there any rule of thumb or imbeciles guide to Span?

Fred
  • 12,086
  • 7
  • 60
  • 83
  • 2
    If it ain't slow, don't `Span` it. Or to put it differently: no, do not go around proactively replacing all use of arrays with `Span` or `Memory` unless you've established that you could get some really nice and necessary perf benefits from it. Typically, if you're writing the kind of lower level code where you can really benefit from it, this will be obvious to you, because all the copying and index/offset passing without it will stick out like a sore thumb. – Jeroen Mostert Oct 25 '18 at 13:17
  • No, don't do that. `Span` isn't compatible with `IEnumerable` let alone `IList` etc (in terms of passing arguments), so you'll end up inflicting a special-purpose interface on all the users of your class. – Matthew Watson Oct 25 '18 at 13:18
  • @MatthewWatson But is `T[]` compatible with `IEnumerable` or `IList`? – Fred Oct 25 '18 at 13:42
  • Yes, it is. This compiles: `int[] x = new int[10]; IEnumerable y = x; IList z = x;` But I assume you mean the other way around - your methods are accepting `byte[]` - in which case, they are not, so I take yout point: it's less of an issue. – Matthew Watson Oct 25 '18 at 13:50
  • Span gets interesting when you operate on a sub-section of an array. It is a hack around the missing support for array slices in the CLR. That gets painful when you need to create a new array just to keep the method happy. You have to look at the caller of these methods to see if you'd be ahead. – Hans Passant Oct 25 '18 at 14:36

1 Answers1

2

Replace arguments with Span in methods?

Replace arguments with Memory in async methods?

Replace return type with Span in methods?

No, there's no benefit in just changing signatures. It might even have a negative effect as structs have to be copied for every call. Span<T> is not heavy, but why waste cycles?

If you're working with the array inside the method and need a slice to work on or whatever, create a span where it is needed. If you actually have callers that would like to pass in a Span<T>, then consider a signature change (or an overload).

As Span<T> only can live on the stack it's harder to pass them around to async methods, iterators or other threads.

Generally, consider Span<T> as an alternative to pointers that point to arrays. So use them when you...

  • have unsafe buffers (AllocHGlobal, data provided from/to external P/Invoke APIs, stackalloced data) where ownership is clearly defined
  • have hot loops with a lot of array accesses (here you would have used pointers before)
  • needed to do pointer arithmetic before
  • need to reinterpret some memory (MemoryMarshal.Cast)
  • need the data to stay on the same thread
  • need to limit the lifetime of the data

I haven't yet encountered any use case where I would want to use Memory<T>.

sneusse
  • 1,422
  • 1
  • 10
  • 18