about "hybrid barrier"

190 views
Skip to first unread message

metronome

unread,
Jul 28, 2023, 8:05:03 AM7/28/23
to golang-nuts

Hi,

I came across two questions regarding the hybrid barrier that I am hoping someone can help with.


1.

Chang https://go-review.googlesource.com/c/go/+/31765 says:

"It's unconditional for now because barriers on channel operations require checking both the source and destination stacks and we don't have a way to funnel this information into the write barrier at the moment."

Can anyone help in understanding the statement, say with a sample? Isn't "channel operations involving both stacks" already covered by runtime.sendDirect?

2. comments in mbarrier.go says

// The insertion part of the barrier

// is necessary while the calling goroutine's stack is grey. In

// pseudocode, the barrier is:

//

// writePointer(slot, ptr):

// shade(*slot)

// if current stack is grey:

// shade(ptr)

// *slot = ptr

What does "grey stack" mean? Is a stack considered 'grey' right after its goroutine gets suspended, scanned and resumed to execution?

Thanks a lot.

Michael Knyszek

unread,
Jul 31, 2023, 12:29:25 AM7/31/23
to golang-nuts
On Friday, July 28, 2023 at 8:05:03 AM UTC-4 metronome wrote:

Hi,

I came across two questions regarding the hybrid barrier that I am hoping someone can help with.


1.

Chang https://go-review.googlesource.com/c/go/+/31765 says:

"It's unconditional for now because barriers on channel operations require checking both the source and destination stacks and we don't have a way to funnel this information into the write barrier at the moment."

Can anyone help in understanding the statement, say with a sample? Isn't "channel operations involving both stacks" already covered by runtime.sendDirect?

Here's my best guess: at the time this CL was written, sendDirect called typeBitsBulkBarrier which in turn called writebarrier_prewrite and then gcmarkwb_m (the generic non-assembly write barrier function). gcmarkwb_m, however, didn't have any knowledge of the context around the pointers being written and deleted, or the stack it would've been running on. Information about both the source and destination stacks would have had to be passed down through those other functions. It could be done, but it's a lot of context to pass around for just one corner case.

Though, honestly, I believe the true reason the write barrier was made unconditional was because the performance was good enough and it's more complex to make it conditional.

In fact, I suspect this is why the hybrid write barrier is still unconditional to this day. It also looks very different! The write barrier no longer directly marks objects and instead it enqueues the relevant pointers in the write barrier buffer system, so there's no write barrier routine to plumb this information down into. There may be some ways to make it conditional (this TODO for instance) but I don't think anyone is planning to work on it. My gut feeling is that the complexity and cost of plumbing this information through (or acquiring and using it at the point of the write barrier) might not be worth whatever modest performance improvement we would get out of it. It would still be interesting to try and find out; I'm happy to be wrong. :)

2. comments in mbarrier.go says

// The insertion part of the barrier

// is necessary while the calling goroutine's stack is grey. In

// pseudocode, the barrier is:

//

// writePointer(slot, ptr):

// shade(*slot)

// if current stack is grey:

// shade(ptr)

// *slot = ptr

What does "grey stack" mean? Is a stack considered 'grey' right after its goroutine gets suspended, scanned and resumed to execution?

Thanks a lot.

A goroutine stack is a GC mark root, so I believe in this terminology it's implicitly grey at the start of the mark phase. It then transitions to black once it gets scanned.
Reply all
Reply to author
Forward
0 new messages