Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/multithreading/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Multithreading 使用GO时如何测量系统过载_Multithreading_Performance_Go_Scalability - Fatal编程技术网

Multithreading 使用GO时如何测量系统过载

Multithreading 使用GO时如何测量系统过载,multithreading,performance,go,scalability,Multithreading,Performance,Go,Scalability,我在GO中重写一个旧系统,在旧系统中我测量系统平均负载,以了解是否应该增加线程池中的线程数 在go中,人们不使用threadpool或goroutine池,因为启动goroutine非常便宜。 但是仍然运行太多的goroutine效率较低,仅足以保持cpu使用率接近100% 因此,有一种方法可以知道有多少goroutine已准备好运行(未被阻止),但当前尚未运行。或者是否有办法获取计划的可运行goroutine“Run queue”的数量。请查看 要打印“所有当前goroutine的堆栈跟踪”,

我在GO中重写一个旧系统,在旧系统中我测量系统平均负载,以了解是否应该增加线程池中的线程数

在go中,人们不使用threadpool或goroutine池,因为启动goroutine非常便宜。 但是仍然运行太多的goroutine效率较低,仅足以保持cpu使用率接近100%

因此,有一种方法可以知道有多少goroutine已准备好运行(未被阻止),但当前尚未运行。或者是否有办法获取计划的可运行goroutine“Run queue”的数量。

请查看

要打印“所有当前goroutine的堆栈跟踪”,请使用:

要打印“导致同步原语阻塞的堆栈跟踪”,请使用:

您可以将这些功能与中的功能结合起来,以获得一些基本的报告

此示例故意创建许多阻塞的goroutine并等待它们完成。每隔5秒,它打印
块的输出
pprof配置文件,以及仍然存在的GOROUTION数:

package main

import (
    "fmt"
    "math/rand"
    "os"
    "runtime"
    "runtime/pprof"
    "strconv"
    "sync"
    "time"
)

var (
    wg sync.WaitGroup
    m  sync.Mutex
)

func randWait() {
    defer wg.Done()
    m.Lock()
    defer m.Unlock()
    interval, err := time.ParseDuration(strconv.Itoa(rand.Intn(499)+1) + "ms")
    if err != nil {
        fmt.Errorf("%s\n", err)
    }
    time.Sleep(interval)
    return
}

func blockStats() {
    for {
        pprof.Lookup("block").WriteTo(os.Stdout, 1)
        fmt.Println("# Goroutines:", runtime.NumGoroutine())
        time.Sleep(5 * time.Second)
    }
}

func main() {
    rand.Seed(time.Now().Unix())
    runtime.SetBlockProfileRate(1)
    fmt.Println("Running...")
    for i := 0; i < 100; i++ {
        wg.Add(1)
        go randWait()
    }
    go blockStats()
    wg.Wait()
    fmt.Println("Finished.")
}
主程序包
进口(
“fmt”
“数学/兰德”
“操作系统”
“运行时”
“运行时/pprof”
“strconv”
“同步”
“时间”
)
变量(
wg sync.WaitGroup
互斥
)
func randWait(){
推迟工作组完成()
m、 锁()
延迟m.Unlock()
interval,err:=time.ParseDuration(strconv.Itoa(rand.Intn(499)+1)+“ms”)
如果错误!=零{
fmt.Errorf(“%s\n”,err)
}
睡眠时间(间隔)
返回
}
func blockStats(){
为了{
pprof.Lookup(“块”).WriteTo(os.Stdout,1)
fmt.Println(“#Goroutines:,runtime.NumGoroutine())
时间。睡眠(5*时间。秒)
}
}
func main(){
rand.Seed(time.Now().Unix())
运行时.SetBlockProfileRate(1)
fmt.Println(“运行…”)
对于i:=0;i<100;i++{
工作组.添加(1)
走吧,等等
}
goblockstats()
wg.Wait()
fmt.Println(“完成”)
}
我不确定这是否是你想要的,但你可以修改它以满足你的需要

有没有办法知道有多少goroutine已准备好运行(未被阻止),但当前尚未运行

您将能够(2014年第4季度/2015年第1季度)尝试并可视化这些goroutines,并开发一个新的跟踪工具(2014年第4季度):

跟踪包含:

  • 与goroutine调度相关的事件
    • goroutine开始在处理器上执行
    • 同步原语上的goroutine块
    • 一个goroutine创建或取消阻止另一个goroutine
  • 网络相关事件
    • 一个goroutine在网络IO上阻塞
    • 网络IO上的goroutine未被阻止
  • 系统调用相关事件
    • goroutine进入syscall
    • goroutine从syscall返回
  • 垃圾收集器相关事件
    • GC启动/停止
    • 同步扫描启动/停止;及
  • 用户事件
我所说的“处理器”是指逻辑处理器,
GOMAXPROCS

每个事件都包含事件id、精确的时间戳、操作系统线程id、处理器id、goroutine id、堆栈跟踪和其他相关信息(例如未阻止的goroutine id)


只有在“运行太多goroutine的效率低于将cpu使用率保持在100%左右”的情况下才会出现这种情况。Go很少从正在运行的goroutine切换,除非它在等待I/O或通道操作或同步原语时被阻止,因此启动大量goroutine,操作系统线程数通过
runtime.GOMAXPROCS(runtime.NumCPU())
匹配CPU数,不需要创建太多额外的上下文切换开销。我们也许可以在工作负载的附加信息方面提供更多帮助--您的goroutines主要是在旋转CPU,还是在等待DB,或是通道操作,或者…?谢谢用户2714852,你是说如果GOMAXPROCS设置为2,我启动4个永不停止且从不阻塞的goroutine,那么运行时将只运行前2个,从不切换到另一个?在Go 1.1中,这是完全正确的:goroutine调度完全是协作的,如果存在无I/O的无休止循环,等等。,它永远占据着线。这是我在书中谈到的。(您始终可以调用runtime.Gosched()以显式屈服);那句话中的“偶尔”让我说“很少”强制转换。我只知道这些;我刚才从Go源代码中无法获得更多信息。您可以添加一些级别的跟踪,以跟踪goroutine在运行时处理了多少个作业,作为每个goroutine的活跃度的度量。要回答两个问题,请假设goroutine主要做CPU密集型工作。+1这似乎回答了这个问题。我发现这个SOA示例程序非常棒。还可能值得测试问题的前提,即动态调优goroutine count是否能够提高性能,使其值得(与仅仅启动大量goroutine或其他简单策略相比)。答案可能取决于应用程序。但是,不管怎样,这似乎回答了人们提出的问题。@user2714852我同意,我还没有遇到过这样的情况:手动调整goroutine的数量比只启动所需数量并让运行时处理调度提供更好的性能。我相信有一些例子,只是不是很常见,从我所能告诉你的情况应该在Go 1.2及以后的版本中变得更好。“我还没有遇到过这样的情况,手动调整Goroutine的数量比只启动所需数量的Goroutine提供更好的性能”如果您的任何goroutine可能正在执行阻塞文件系统或系统调用工作,这是一个非常糟糕的主意。假设你有一个6阶段的任务。更好
pprof.Lookup("block").WriteTo(os.Stdout, 1)
package main

import (
    "fmt"
    "math/rand"
    "os"
    "runtime"
    "runtime/pprof"
    "strconv"
    "sync"
    "time"
)

var (
    wg sync.WaitGroup
    m  sync.Mutex
)

func randWait() {
    defer wg.Done()
    m.Lock()
    defer m.Unlock()
    interval, err := time.ParseDuration(strconv.Itoa(rand.Intn(499)+1) + "ms")
    if err != nil {
        fmt.Errorf("%s\n", err)
    }
    time.Sleep(interval)
    return
}

func blockStats() {
    for {
        pprof.Lookup("block").WriteTo(os.Stdout, 1)
        fmt.Println("# Goroutines:", runtime.NumGoroutine())
        time.Sleep(5 * time.Second)
    }
}

func main() {
    rand.Seed(time.Now().Unix())
    runtime.SetBlockProfileRate(1)
    fmt.Println("Running...")
    for i := 0; i < 100; i++ {
        wg.Add(1)
        go randWait()
    }
    go blockStats()
    wg.Wait()
    fmt.Println("Finished.")
}