Pardon the interruption: Loop preemption in Go 1.14

迭代点:

  • 1.5

    • concurrent garbage collector

  • 1.6

    • concurrent finalizer scans

    • dirty stack tracking

    • global write barriers

  • 1.7

    • concurrent stack shrinking

  • 1.8

    • hybrid write barrier

抢占:

  • 1.0

    • voluntary preemption only

  • 1.2

    • function call preemption

      • function entry -> grow stack/preempt -> Y -> call runtime

  • 1.4

    • asynchronous preemption

      • +0.3% binary size

      • 0 performance overhead

      • ~25us typical preemption bound

一些技术尝试:

  • preempt back-edges

    • adding instructions to loops isn’t viable

      • 每次loop判断preempt

        • 8% slowdown

  • “fowward simulation”

  • use signals, like an OS kernal

    • zero overhead, but doesn’t work with GC

      • 一次切换寄存器地址变了

  • signals + gc stack maps at every instruction

    • +10% binary size

    • stack maps everywhere bloats binaries and doesn’t “keep it simple”

  • signal-based preemption with conservative innermost frame scanning

    • 1.14

    • 实现

      • send a signal to the goroutine

        • 23 SIGURG

      • examine where it stopped/conservative scanning/precisely scanning

        • runtime.asyncPreempt

        • requires non-moving GC

          • https://en.wikipedia.org/wiki/Tracing_garbage_collection#Moving_vs._non-moving