いろいろ備忘録日記

主に .NET とか Go とか Flutter とか Python絡みのメモを公開しています。

Goメモ-417 (スケジューラの統計情報を取得, runtime/metrics)

関連記事

GitHub - devlights/blog-summary: ブログ「いろいろ備忘録日記」のまとめ

Goメモ-415 (ヒープメモリの統計情報を取得, runtime/metrics)

Goメモ-416 (CPUの統計情報を取得, runtime/metrics)

概要

以下、自分用のメモです。忘れないようにここにメモメモ。。。

Go 1.16 から、runtime/metrics というパッケージが追加されていたのを最近知りました。

Go 1.16 リリースノート には、以下のように記載されています。

The new runtime/metrics package introduces a stable interface for reading implementation-defined metrics from the Go runtime. It supersedes existing functions like runtime.ReadMemStats and debug.GCStats and is significantly more general and efficient. See the package documentation for more details.

(新しいruntime/metricsパッケージは、Goランタイムから実装定義のメトリクスを読み取るための安定したインターフェイスを導入します。これはruntime.ReadMemStatsやdebug.GCStatsのような既存の関数に取って代わり、より一般的で効率的です。)

なるほど。。とりあえず、呼び出してみたサンプルを未来の自分のために貼り付けておきます。

今回は、スケジューラ関連の値を取得するサンプルです。ヒープメモリ関連の値については Goメモ-415 (ヒープメモリの統計情報を取得, runtime/metrics) を参照ください。CPU関連の値については Goメモ-416 (CPUの統計情報を取得, runtime/metrics) を参照ください。

サンプル

package metricsop

import (
    "context"
    "runtime"
    "runtime/metrics"
    "sync"
    "time"

    "github.com/devlights/gomy/output"
)

// Sched は、runtime/metrics を利用してスケジューラ関連の情報を取得するサンプルです.
//
// # REFERENCES
//   - https://pkg.go.dev/runtime/metrics@latest
func Sched() error {
    var (
        items = []string{
            "/sched/gomaxprocs:threads",      // ユーザーレベルのGoコードを同時に実行できるオペレーティング・システムのスレッド数
            "/sched/goroutines:goroutines",   // 生きているゴルーチンの数
            "/sched/latencies:seconds",       // ゴルーチンが実際に実行される前に、スケジューラ内で実行可能な状態で過ごした時間の分布
            "/sync/mutex/wait/total:seconds", // ゴルーチンがsync.Mutex、sync.RWMutex、またはランタイム内部ロックでブロックされた時間の累計
        }
        samples = make([]metrics.Sample, len(items))
    )

    for i, name := range items {
        samples[i].Name = name
    }

    var (
        ctx, cxl = context.WithTimeout(context.Background(), 2*time.Second)
        ready    = make(chan bool)
        lock     sync.Mutex
        busyfn   = func(ctx context.Context, ready <-chan bool) {
            <-ready

            lock.Lock()
            time.Sleep(100 * time.Millisecond)
            lock.Unlock()

            <-ctx.Done()
        }
    )
    defer cxl()

    for range runtime.GOMAXPROCS(0) - 1 {
        go busyfn(ctx, ready)
    }
    close(ready)

    <-time.After(1 * time.Second)

    metrics.Read(samples)
    for _, s := range samples {
        output.Stdoutl("[Name ]", s.Name)

        switch s.Value.Kind() {
        case metrics.KindUint64:
            output.Stdoutf("[Value]", "%v\n", s.Value.Uint64())
        case metrics.KindFloat64:
            output.Stdoutf("[Value]", "%v\n", s.Value.Float64())
        case metrics.KindFloat64Histogram:
            output.Stdoutf("[Value]", "Bucket Count: %d\n", len(s.Value.Float64Histogram().Buckets)-2)
        default:
            output.Stdoutl("[Value]", "INVALID")
        }

        output.StdoutHr()
    }

    <-ctx.Done()

    return nil
}

実行すると以下の様になります。

      $ task
       task: [build] go build .
       task: [run] ./try-golang -onetime

       ENTER EXAMPLE NAME: metrics_sched

       [Name] "metrics_sched"
       [Name ]              /sched/gomaxprocs:threads
       [Value]              16
       --------------------------------------------------
       [Name ]              /sched/goroutines:goroutines
       [Value]              16
       --------------------------------------------------
       [Name ]              /sched/latencies:seconds
       [Value]              Bucket Count: 161
       --------------------------------------------------
       [Name ]              /sync/mutex/wait/total:seconds
       [Value]              4.010605616
       --------------------------------------------------


       [Elapsed] 2.000425097s

確かにいろいろ取得出来ていますね。

取得するためのキー名を間違えて指定した場合はエラーとかにはならずに値が返ってきて、Kindの値が0となっています。

参考情報

metrics package - runtime/metrics - Go Packages

Goのおすすめ書籍


過去の記事については、以下のページからご参照下さい。

サンプルコードは、以下の場所で公開しています。