-
Notifications
You must be signed in to change notification settings - Fork 4.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[hackathon] ValueArray
- a compliment type to the Span
, which owns its data without indirections.
#60519
Conversation
Note regarding the This serves as a reminder for when your PR is modifying a ref *.cs file and adding/modifying public APIs, to please make sure the API implementation in the src *.cs file is documented with triple slash comments, so the PR reviewers can sign off that change. |
I couldn't figure out the best area label to add to this PR. If you have write-permissions please help me learn by adding exactly one area label. |
@davidwrighton @stephentoub - please take a look. Thanks! |
The maximum array rank is constrained by runtime implementation limitations ( |
ValueArray
- a compliment type to the Span
, which owns its data without indirections.ValueArray
- a compliment type to the Span
, which owns its data without indirections.
@jkotas - I have relaxed the dimension limit in this change. |
This limit exists to avoid running in stackoverflows. Given how many of the type loader algorithms are implemented, it is not hard to hit stackoverflow for arrays with high rank. |
There are number of implementation limits like this. For example, there is an implementation limit on size of arguments or size of local variables. These implementation limits are not mentioned in the ECMA spec. |
I am wondering whether there are other hacky ways to encode the array that have
|
I just meant that we are not required by the spec to have a limit. It is an implementation detail that we can change, carefully of course. I will check if we have any kind of recursion when processing the rank. |
Just wanted to note that there are a few examples in the Windows SDK that have "value arrays" going up to 1025 elements. The largest of these, that I'm aware of, consists of 12-byte structs (so you end up with a 12kb value type). I think the biggest stack-allocation in the BCL (for |
There are certainly other ways to encode the length with different tradeofs. I just picked array rank as the most straightforward. It seems to be fairly cheap -in terms of instantiations/types required, and in terms of implementation difficulty and changes to runtime. Also it is easily expressible in C# - very little typing, if you look through tests. That is nice at prototyping/experimenting stage. |
@tannergooding - there are some limits on how large variables on the stack can be. The limit will apply regardless how you declare the variable. I am not sure if 12K comes close to such limit. I suspect somebody somewhere is already using such type by manually specifying a struct with 1025 fields. |
On the Libraries we use hacks such as Line 97 in 57bfe47
And use them unsafely : Line 42 in 57bfe47
As you notice there are a few short |
Why wouldn't we just use |
I agree that the current hack is ok for experimentation. I have been thinking what this may look like if we were to productize this idea, and the hacks mentioned here look pretty ugly. We should be thinking about the clean way to represent it: either add support for parametrizing generics using constants or add a new ELEMENT_TYPE for the fixed array. There was actually ET value for fixed arrays in the early days of .NET https://github.com/search?q=ELEMENT_TYPE_VALUEARRAY_INTERNAL&type=code , but it did not make it. |
I would ask the question the other way - Why would you use stackalloc if a ValueArray is sufficient? - because ValueArray is conceptually simpler.
There are downsides also:
If a ValueArray is available I'd ask myself every time I use |
yes, I saw it and figured that we probably had value arrays pre-1.0 in some form, but it did not ship. Possibly because before Current assumption is that we are not changing metadata version for this feature, so we had to find ways that fit it into v2.0 spec. |
To play devil's advocate, because it's one tool that suffices whether or not the length is variable and it's an existing feature that doesn't increase concept count. |
I think these are orthogonal things and both have their uses. class C1<T, int L>
{
T[L] Data; // ELEMENT_TYPE_VALUEARRAY <T> <L> Data
} The features do not require one another though, so value array does not need to wait for generic constants. If there is a way to make |
The range of usefulness of |
It's not, it's a Venn diagram. That's kind of my point. I think the argument for |
@stephentoub, I think one thing to note is that there is potential optimization opportunities and other static analysis that can be done for When the size is well known, This has downsides as well (its a breaking change to go from
|
So, for example, things like Likewise, the floating-point formatting/parsing logic always allocates a |
The generic constants can be be used to represent the value array size like |
The cited example was: FourStackStrings stackStrings = default;
var strings = new ValueListBuilder<string>(MemoryMarshal.CreateSpan(ref stackStrings.Item1!, 4)); Once var strings = new ValueListBuilder<string>(stackalloc string[4]); Can you help me understand why this specific case would a) be better with |
…ixed` statements.
So, should we merge this then and start using it? |
I'm not sure if this particular case is more beneficial as In either case, I'd hope the syntax for using value array ends up being decently trivial itself. |
The syntax is a relatively easy part ( relatively ;-) - compared to the runtime part). It is work in progress (dotnet/roslyn#57286) The tooltips reveal the underlying type, but will show |
I'll defer to @davidwrighton. From my perspective, this is an experimental branch and merging this into it is fine. |
BTW, I noticed that JIT is very good at eliding and hoisting range checks when working with value array. I.E. Since the length is statically known, I was thinking that these could be good optimizations, but it looks like JIT does that without any additional work. |
Talked to @davidwrighton - and we think it is ok to merge this. |
ValueArray<T, R>
is a data type that represents an indexable fixed-sized chunk of data containing N elements of type T.The
R
type argument must be an object array type, that is not used for anything other than to convey the length of the value array declaratively - via theR
's rank.The only "magical" part of the
ValueArray
type is that the VM logically replicates the storage N times and adjusts the instance layout and GC tracking accordingly.In all other ways this is a regular struct - it can be copied by value, embedded in another struct/class, passed in/out of methods, stored on the heap, boxed...