165 lines
4.5 KiB
Markdown
165 lines
4.5 KiB
Markdown
# 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:
|
|
|
|
``` c#
|
|
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 arerely 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.
|
|
|
|
``` c#
|
|
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
|
|
----------
|
|
|
|
``` c#
|
|
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 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.
|
|
|
|
``` c#
|
|
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 thate.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!
|
|
|