-
Notifications
You must be signed in to change notification settings - Fork 4.1k
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
Allow stackalloc for reference types when they are used as locals when not escaping #16154
Comments
This has come up before and IIRC it's not possible in C# without support for it from the CLR: |
This is the core problem with this proposal. In order to be safe the compiler must be able to distinguish between operations that allow escaping and those which do not. This is a significant problem for C# as it exist today. Even the simple code that you put into the sample is problematic: Kclass c = stackalloc Kclass() This itself could be dangerous as constructors can be a source of escaping class Kclass {
static Kclass _dangerous;
internal KClass() {
// Evil but legal
_dangerous = this;
}
} Hence for the compiler to know that even the simple act of allocating is safe it must understand the escaping semantics of the constructor of Solving this problem can really only be done with a new annotation, for example One of the projects we did in Midori took the annotation approach (it was called |
Thank you for the explanation. This seems to be a hard constraint for this and similar proposals I've seen around. Too bad. I agree that annotating classes would not be a great solution, as that would require almost everyone to make the annotations in order to be compatible with this feature. Would it be possible to throw an exception though? As a rough outline, could the CLR enter a special "assignment checking mode" at the time of construction or assignment from anything that is stackalloc-ed, and while within that mode every assignment is examined until exited from that mode. If within the assignment the right side is the location of class Kclass {
static Kclass _dangerous;
internal Kclass() {
_dangerous = this; // Stopped by exception, we're still in monitoring mode for that k
}
}
class Fast {
void Update() {
// Enter "assignment monitoring mode"
Kclass k = stackalloc Kclass ();
// Exit "assignment monitoring mode"
// Enter "assignment monitoring mode"
_myVar = k; // compiler can catch this one
// Exit "assignment monitoring mode"
// Enter "assignment monitoring mode"
k.Register(); // Actually, not discovered at compile time, but exception.
// Exit "assignment monitoring mode"
}
} |
Re: throwing exception, wouldn't runtime tracking nullify any perf gains from doing this in the first place? |
Probably, but I had to try :) It could prevent the allocations while slowing down the assignments. |
Actually, I have concluded this problem is not solvable in the way I wanted it without being more trouble than it's worth. Sometimes the compiler can make a proper deduction, and in that case the other proposal can come in handy as a benefit (but not expectation). If these allocations are an issue, I guess they must be solved manually, like before: private Klass _tempK = new Klass();
void Update() {
_tempK.Init();
// Use of _tempK ...
} |
@jnm2 @sirgru @jaredpar |
@ygc369 this seems to be an eternal topic, I see your issues about the similar thing are still having traction. I currently don't believe the solution above would the be a good fit, but I thought about the subject later and posted a proposal here https://github.com/dotnet/coreclr/issues/8756. The gist is: the objects could potentially live on the heap "inside" a custom allocator, where they are reference counted and un-examined by regular GC. Once they get out of scope, if the reference count is not 1 (the last remaining one), this means the variable has "escaped" and it should either be promoted to a full "heap" variable, now examined by GC or an exception could be thrown. This would effectively lower the GC pressure with minimal performance impact, as in most cases these short-lived objects are not referenced in many places. Also, it would not bring the stack back into the picture, which can be good because I don't think stack objects can be treated polymorphically. It would also have a benefit of improving memory locality in places where it matters, provided the programmer does it right. |
@sirgru class Allocator<T> where T: new()
{
private T[] arr;
private int alloc_index
public Allocator(int max_num)
{
this.arr=new T[max_num];
int i;
for(i=0;i<max_num;i++)this.arr[i]=new T();
this.alloc_index=0;
}
public T alloc()
{
return this.arr[this.alloc_index++];
}
} |
Fyi, work has started on stack-allocated objects. dotnet/coreclr#20251 |
Closing this out. We're doing all language design now at dotnet/csharplang. If you're still interested in this idea let us know and we can migrate this over to a discussion in that repo. Thanks! |
Problem:
In some domains, game development for example, reducing allocations on the heap is a big concern. One obvious way to reduce allocations was proposed here: https://github.com/dotnet/coreclr/issues/1784 . And, while this solution has good merit, there are a couple of drawbacks:
Solution:
Introduce use of
stackalloc
instead ofnew
for reference types to allow creation on the stack, in cases where they are locals and are used in no features that would risk escaping, such as being used as method parameters of being captured. After something has been stackalloc-ed, any attempt to use it in a way that could possibly lead to escaping would prevent compilation. In that case the user could simply use regularnew
.Example:
However, there are still cases where proving this may be more complicated:
I can imagine there are cases where the compiler can not deduce if the escaping happened. In that case, an exception can be thrown and no assignement would happen.
So, this proposal can be seen as a more strict, enforced version of the previous one, in order to show intent and attempt to make guarantees. This proposal has no connections with unsafe context.
The text was updated successfully, but these errors were encountered: