0

我们正在运行一个大部分时间都在做 GC 的 Go 程序。我们做了一个内存配置文件,我做了一个“go tool pprof -alloc_objects”。然后我在 pprof 控制台中做了一个“top5”,它显示了以下内容:

我的问题是,runtime.adjustdefers 是什么意思?

(pprof) top5
4576708929 of 7330217181 total (62.44%)
Dropped 765 nodes (cum <= 36651085)
Showing top 5 nodes out of 88 (cum >= 970919101)
      flat  flat%   sum%        cum   cum%
2035058528 27.76% 27.76% 2035058528 27.76%  runtime.adjustdefers
 996366409 13.59% 41.36% 1284278077 17.52%  github.com/pelletier/go-buffruneio.init
 627682563  8.56% 49.92%  916069310 12.50%  github.com/prometheus/common/expfmt.MetricFamilyToText
 509166106  6.95% 56.86%  509166106  6.95%  encoding/csv.(*Reader).ReadAll
 408435323  5.57% 62.44%  970919101 13.25%  golang.org/x/net/html.init
4

1 回答 1

0

Go 编程语言规范

延迟语句

“defer”语句调用一个函数,该函数的执行被推迟到周围函数返回的那一刻,要么是因为周围函数执行了一个 return 语句,到达了它的函数体的末尾,要么是因为相应的 goroutine 正在恐慌。


go/src/runtime/stack.go

func adjustdefers(gp *g, adjinfo *adjustinfo) {
    // Adjust defer argument blocks the same way we adjust active stack frames.
    tracebackdefers(gp, adjustframe, noescape(unsafe.Pointer(adjinfo)))

    // Adjust pointers in the Defer structs.
    // Defer structs themselves are never on the stack.
    for d := gp._defer; d != nil; d = d.link {
        adjustpointer(adjinfo, unsafe.Pointer(&d.fn))
        adjustpointer(adjinfo, unsafe.Pointer(&d.sp))
        adjustpointer(adjinfo, unsafe.Pointer(&d._panic))
    }
}

go/src/runtime/stack.go

// Copies gp's stack to a new stack of a different size.
// Caller must have changed gp status to Gcopystack.
//
// If sync is true, this is a self-triggered stack growth and, in
// particular, no other G may be writing to gp's stack (e.g., via a
// channel operation). If sync is false, copystack protects against
// concurrent channel operations.
func copystack(gp *g, newsize uintptr, sync bool) {
    // . . .
    // allocate new stack
    new := stackalloc(uint32(newsize))
    if stackPoisonCopy != 0 {
        fillstack(new, 0xfd)
    }
    // . . .
    // Compute adjustment.
    var adjinfo adjustinfo
    adjinfo.old = old
    adjinfo.delta = new.hi - old.hi
    // . . .
    // Adjust remaining structures that have pointers into stacks.
    // We have to do most of these before we traceback the new
    // stack because gentraceback uses them.
    adjustctxt(gp, &adjinfo)
    adjustdefers(gp, &adjinfo)
    adjustpanics(gp, &adjinfo)
    if adjinfo.sghi != 0 {
        adjinfo.sghi += adjinfo.delta
    }
    // . . .
}

根据我对代码的阅读,当调整 goroutine 堆栈的大小时,会对adjustdefers延迟函数进行指针调整。


你说你正在“运行一个大部分时间都在做 GC 的 Go 程序”。第二高的包是github.com/pelletier/go-buffruneio。代码看起来效率低下。这是读取符文的简单基准。

package main

import (
    "bufio"
    "bytes"
    "io"
    "testing"

    "github.com/pelletier/go-buffruneio"
)

var buf = make([]byte, 64*1024)

func BenchmarkBuffruneio(b *testing.B) {
    b.ReportAllocs()
    for i := 0; i < b.N; i++ {
        r := buffruneio.NewReader(bytes.NewBuffer(buf[:cap(buf)]))
        for {
            rune, _, err := r.ReadRune()
            if err == io.EOF || rune == buffruneio.EOF {
                break
            }
        }
    }
}

func BenchmarkBufio(b *testing.B) {
    b.ReportAllocs()
    for i := 0; i < b.N; i++ {
        r := bufio.NewReader(bytes.NewBuffer(buf[:cap(buf)]))
        for {
            _, _, err := r.ReadRune()
            if err == io.EOF {
                break
            }
        }
    }
}

输出:

$ go test -v -bench=.
goos: linux
goarch: amd64
pkg: so/runes
BenchmarkBuffruneio-2        200    9395482 ns/op    4198721 B/op    131078 allocs/op
BenchmarkBufio-2            3000     333731 ns/op       4208 B/op         2 allocs/op
PASS
ok      so/runes    3.878s
$
于 2017-04-20T12:29:02.040 回答