Skip to content
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

runtime: gcResetMarkState contributes significantly to sweep termination time #14420

Closed
rhysh opened this issue Feb 20, 2016 · 4 comments
Closed

Comments

@rhysh
Copy link
Contributor

rhysh commented Feb 20, 2016

When profiling the reproducer for #14406 with "go version go1.6 linux/amd64", stack shrinking off, and numactl disabling memory migration, I see calls to runtime.gcResetMarkState as called by runtime.gcStart in nearly all sampled stacks that include runtime.gcStart.

The lines reported by GODEBUG=gctrace=1 show sweep termination durations of around 30ms when running with 1e6 goroutines. It looks like the sequential processing of runtime.allgs is responsible for these long pauses.

Full gctrace output is in #14406

gc 22 @96.880s 3%: 38+203+129 ms clock, 1220+0/1681/4936+4139 ms cpu, 1266->1276->656 MB, 1293 MB goal, 36 P

@aclements

@ianlancetaylor ianlancetaylor added this to the Go1.7 milestone Feb 20, 2016
@aclements
Copy link
Member

Sure enough. The goroutines don't even have to be doing anything. The cost is something like 25ns/goroutine on my laptop. Amusingly, I can cut that roughly in half just by putting gcAssistBytes on the same cache line as gcscandone and gcscanvalid. :)

I think we can just do this concurrently in gcStart before stopping the world. The downside of that is that it will still block allocation on Gs that go outside of their span cache, but that's better than blocking everything.

We could also make it just plain faster by pulling those fields out into their own contiguous arrays we can bulk zero.

We're paying the same per-goroutine cost in markroot during STW mark termination when we look for stacks to scan. That's trickier to avoid. We might be able to fix that by lifting the gcscanvalid check up in to markroot if we also move gcscanvalid in to a contiguous array.

@RLH, any thoughts?

Here's a simplified reproducer (I love the term "ballast", BTW):

package main

import (
    "os"
    "time"
)

const (
    ballastSize   = 100 << 20
    garbageSize   = 10 << 20
    garbagePeriod = 100 * time.Millisecond

    // This uses about 2GB of memory for stacks.
    numGs = 4e5
)

var (
    ballast []byte
    garbage []byte
)

func churn() {
    ballast = make([]byte, ballastSize)

    for {
        time.Sleep(garbagePeriod)
        garbage = make([]byte, garbageSize)
    }
}

func main() {
    for i := 0; i < numGs; i++ {
        go func() { select {} }()
    }

    go churn()

    time.AfterFunc(10*time.Second, func() { os.Exit(0) })
    select {}
}

@aclements aclements self-assigned this Mar 1, 2016
@aclements
Copy link
Member

Looks like I caused this performance degradation in b0d5e5c.

@gopherbot
Copy link

CL https://golang.org/cl/20147 mentions this issue.

@adg adg added Release-OK and removed Release-OK labels Apr 7, 2016
@adg adg modified the milestones: Go1.6.1, Go1.6.2 Apr 7, 2016
@gopherbot
Copy link

CL https://golang.org/cl/22043 mentions this issue.

gopherbot pushed a commit that referenced this issue Apr 14, 2016
Currently we reset the mark state during STW sweep termination. This
involves looping over all of the goroutines. Each iteration of this
loop takes ~25ns, so at around 400k goroutines, we'll exceed our 10ms
pause goal.

However, it's safe to do this before we stop the world for sweep
termination because nothing is consuming this state yet. Hence, move
the reset to just before STW.

This isn't perfect: a long reset can still delay allocating goroutines
that block on GC starting. But it's certainly better to block some
things eventually than to block everything immediately.

For 1.6.x.

Fixes #14420.

name \ 95%ile-time/sweepTerm           old          new  delta
500kIdleGs-12                 11312µs ± 6%  18.9µs ± 6%  -99.83%  (p=0.000 n=16+20)

Change-Id: I9815c4d8d9b0d3c3e94dfdab78049cefe0dcc93c
Reviewed-on: https://go-review.googlesource.com/20147
Reviewed-by: Rick Hudson <rlh@golang.org>
Run-TryBot: Austin Clements <austin@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-on: https://go-review.googlesource.com/22043
Reviewed-by: Austin Clements <austin@google.com>
@golang golang locked and limited conversation to collaborators Apr 19, 2017
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

5 participants