You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Optimize out write barriers for fields in ref-like structs #9512
Hmm, I wonder if there are sufficient guarantees that by ref like types only live on the stack for the JIT to be able to eliminate GC write barriers when writing to fields of such types.
Yes, there are. It would be nice optimization to have.
And does a byref to a ref-like type serve any useful purpose?
It seems that it could be converted to an unmanaged pointer because it doesn't need to be reported to the GC. Though it looks like attempting to write an object reference to an unmanaged pointer makes the JIT emit a checked write barrier rather than no barrier, hmm...
An example containing 2 cases where the GC write barrier could be eliminated if the write location is known to be on the stack:
refstructRefLike{publicobjecto;[MethodImpl(MethodImplOptions.NoInlining)]publicvoidNotInlined(objecto){this.o=o;// GC WB, the JIT currently assumes that `this` can be on the GC heap}[MethodImpl(MethodImplOptions.AggressiveInlining)]publicvoidInlined(objecto){this.o=o;// No GC WB if the method is inlined and `this` is on the stack.}}publicstaticvoidMain(){RefLiker=newRefLike();Test(newobject(),refr);}[MethodImpl(MethodImplOptions.NoInlining)]staticvoidTest(objecto,refRefLikear){RefLikelr=newRefLike();lr.o=o;// No GC WBlr.Inlined(o);// No GC WBlr.NotInlined(o);// GC WB inside NotInlinedar.o=o;// GC WB, the JIT currently assumes that `ar` can point into the GC heap }
I tried a quick and dirty implementation to see what happens. I wasn't expecting to see anything in the jit-diffs but it turns out that there's already a case like this in corelib. A bunch of GC write barriers disappeared from DateTimeParse.ParseByFormat due to DateTimeResult being a ref struct.
Reacted by Jan Kotas, Matt Warren, Maoni Stephens and Joe Amenta
Another example - a non ref struct inside a ref struct:
struct NotRefLike
{
public object o;
}
ref struct RefLike
{
public object o;
public NotRefLike n;
...
}
...
lr.n.o = o; // No GC WB already, it's known to be a local variable
ar.n.o = o; // No GC WB with my hack. Fortunately the JIT transforms ar.n.o into a single indir.
lr.n.FooBar(); // Of course, any not inlined method of NotRefLike will have GC WBs.
ref object ro = ref lr.n.o;
Console.WriteLine();
ro = o; // GC WB unfortunately. Not directly related to ref-like stuff,
// the JIT just doesn't figure out that ro points into the stack.
I'm not sure if the use of 2 movsq to copy 16 bytes is a good idea, these instructions do not have a reputation for performance. But they surely keep the code small and avoid issues with GC. The ideal solution to copy 16 bytes would probably use SSE load/store but GC won't be happy with it since it cannot "see" the object reference stored in the first 8 bytes of the temporary XMM register.
ValueSegmentListsegmentsToReturn=default;refvarstartSegment=refsegmentsToReturn.Segment00;intcount=0;do{BufferSegmentnext=from.NextSegment;Debug.Assert(next!=null||toExclusive==null);from.ResetMemory();if((uint)count<(uint)SegmentPoolSize){// Store in temporary list while preforming expensive resetsUnsafe.Add(refstartSegment,count)=from;count++;}from=next;}while(from!=toExclusive);
Activity
mikedn commentedon Jan 6, 2018
And does a byref to a ref-like type serve any useful purpose?
It seems that it could be converted to an unmanaged pointer because it doesn't need to be reported to the GC. Though it looks like attempting to write an object reference to an unmanaged pointer makes the JIT emit a checked write barrier rather than no barrier, hmm...
mikedn commentedon Jan 6, 2018
An example containing 2 cases where the GC write barrier could be eliminated if the write location is known to be on the stack:
mikedn commentedon Jan 6, 2018
I tried a quick and dirty implementation to see what happens. I wasn't expecting to see anything in the jit-diffs but it turns out that there's already a case like this in corelib. A bunch of GC write barriers disappeared from
DateTimeParse.ParseByFormatdue toDateTimeResultbeing aref struct.mikedn commentedon Jan 9, 2018
Another example - a non ref struct inside a ref struct:
benaadams commentedon Jan 12, 2018
ref struct BufferReader<TSequence>in Pipelines/Buffers and its 2x non ref structPositionis an example where this would help dotnet/corefxlab#2046 (comment)mikedn commentedon Jan 12, 2018
Thanks for the example, copying structs like
Positionis not something that I tried yet.mikedn commentedon Jan 14, 2018
Yep, copying structs require a bit more hacking. Currently copying a struct like
Positiongenerates something likeI can change it to
I'm not sure if the use of 2
movsqto copy 16 bytes is a good idea, these instructions do not have a reputation for performance. But they surely keep the code small and avoid issues with GC. The ideal solution to copy 16 bytes would probably use SSE load/store but GC won't be happy with it since it cannot "see" the object reference stored in the first 8 bytes of the temporary XMM register.benaadams commentedon Feb 18, 2019
Example dotnet/aspnetcore#7674 resolved via inlining
benaadams commentedon Feb 23, 2019
Even uses
CORINFO_HELP_CHECKED_ASSIGN_REFwhen the ref struct is fully inlined (dotnet/corefx@a3cb00f) e.g.with
markples commentedon Aug 31, 2023
#86820, #89064, and #89116 are related (for struct copies)