Added design notes
This commit is contained in:
parent
3e2891a18b
commit
70d3c116ce
165
meetings/LDM-2016-11-01.md
Normal file
165
meetings/LDM-2016-11-01.md
Normal file
|
@ -0,0 +1,165 @@
|
|||
# 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!
|
||||
|
Loading…
Reference in a new issue