csharplang/meetings/2016/LDM-2016-11-01.md
Nick Schonning 6cd82c21ed fix: MD033/no-inline-html
Inline HTML get swallowed in MD and HTML rendering
2019-05-25 01:31:46 -04:00

4.5 KiB

C# Language Design Notes for Nov 1, 2016

Raw notes - need cleaning up

Agenda

  • Abstracting over memory with Span<T>

Abstracting over memory spans

It's common for teams building IO/data pipelines to invent a type similar to Span<T>, which is intended to abstract over native and managed memory, so that you write code over them only ones.

  • Native memory
  • Managed arrays
  • stackalloc
  • stack-allocated

We are now in a situation (unlike when C# started out) where every bit of performance counts. You can't afford overhead in terms of:

  • indirection
  • bounds checking
  • virtual calls
  • GC
  • copying

Span needs to be as efficient to access/index as an array. It should essentially be a "safe pointer".

logically:

struct Span<T>
{
	ref T data;
	int length;
}

Can't be declared like this, because the CLR doesn't support ref fields. But there are IntPtr tricks that can be used.

These need to be treated as "ref-like":

  • Can't go on the heap
  • Can't be captured by lambdas, iterators or async
  • Can't be a type argument
  • Etc...

Also need a ReadOnlySpan<T> or similar.

So need a way to specify ref-like types, so that the compiler wants to enforce it.

The stack-only-ness helps not having to spend anything on lifetime issues. It lets you pass it to something, and when they return you know they didn't keep any references to it.

Should we make existing types "span-aware"? Arrays? Strings?

We need to figure out what the right trade-off is.

Generally we would keep Span<T> at the 1% of users. It's analogous to array in the sense that it's rarely used directly in higher-level code.

For the rest there's wrapper types that are not limited to the stack, and can go into Span-land on demand.

Ref-like types

Let's call the ref-like types "ref structs".

(Unlike refs these can be fields. ref fields require new runtime semantics, whereas we can otherwise make do with runtime optimizations, so we'll skip on ref fields for now)

Ref-like-ness should be declarative; it shouldn't be something you infer. Because something could change somewhere else that changes whether you are or not.

(The structs capturing ref-like locals when you have local functions would be inferred. But that's locally in code).

A problem something like Span may have at the language level is that it doesn't look like something that restrictions apply to, unlike ref int or int*. We could at least have a naming convention.

M(await X, GetSpan());

Lifting out GetSpan() to a local wouldn't work. So that speaks to there being a notation on usage.

  • Nothing?
  • Naming conventions?
  • syntax? ref? *?

Is this really only going to be done by 1% of users (which is still many).

Stackalloc

Span<byte> s = stackalloc byte[4];

Ref return rules

Need to be adjusted a bit. YOu should only be able to return spans to things that are safe to return.

Downlevel use

Do we need to poison these type for downlevel import, and how do we do that?

optreq`s? Mangled names? Obsolete it and recognize something about that in the new compiler that will make it not obsolete it.

Readonly ref

If we want to enable spans representing strings, for instance, we need readonly spans. And their indexer needs to return a readonly ref.

Not so central to this proposal, but generally interesting: Can we make struct methods that don't copy (their this would be readonly ref)? In practice you'd probably want to call the whole struct readonly.

Slicing

Essentially an API feature, but would want nice overloadable syntax.

Structs that can't be passed by value

TO avoid copying costs, semantic errors etc. Icing.

FOr Memory<T>

Not ready to discuss yet, but is there a way that you can prevent a Memory<T> from getting freed while a Span is "out".

Maybe some version of "fixed" can help. A challenge to generalize this from Span to any ref-like type.

Span<byte> b = ...;
fixed(byte* p = &b.GetRawPointer())

Maybe this can be expressed more easily?

Limitations around ref locals today

There are many limitations to keep the rules simple, including the fact that ref locals are readonly at the ref level.

What should the restrictions be around ref-like types? One difference is that e.g. Span has a default value; 0-length span.

Next steps

At the language level: Add an attribute to express ref-like-ness, and build a compiler to enforce it!