优化 CPU 密集型 Golang WebApp 的方法

Ways of optimizing a CPU Intensive Golang WebApp

我有一个玩具网络应用程序,它非常 cpu 密集

func PerfServiceHandler(w http.ResponseWriter, req *http.Request) 
{
   start := time.Now()
   w.Header().Set("Content-Type", "application/json")

   x := 0
   for i := 0; i < 200000000; i++ {
       x = x + 1
       x = x - 1
    }
    elapsed := time.Since(start)    
    w.Write([]byte(fmt.Sprintf("Time Elapsed %s", elapsed)))
}

func main() 
{
    http.HandleFunc("/perf", PerfServiceHandler)
    http.ListenAndServe(":3000", nil)
}

上述函数执行大约需要120毫秒。但是当我用 500 个并发用户(siege -t30s -i -v -c500 http://localhost:3000/perf)对这个应用程序进行负载测试时,我得到的结果是

有人可以回答我下面的问题吗:-

环境:-

Go - go1.4.1 linux/amd64
OS - Linux 3.2.0-4-amd64 #1 SMP Debian 3.2.65-1+deb7u2 x86_64 GNU/Linux
Processor - 2.6Ghz (Intel(R) Xeon(R) CPU E5-2640 v3 @ 2.60GHz)
RAM - 64 GB

OS 参数 -

nproc - 32
cat /proc/sys/kernel/threads-max - 1031126
ulimit -u - 515563
ulimit -a
    core file size          (blocks, -c) 0
    data seg size           (kbytes, -d) unlimited
    scheduling priority             (-e) 0
    file size               (blocks, -f) unlimited
    pending signals                 (-i) 515563
    max locked memory       (kbytes, -l) 64
    max memory size         (kbytes, -m) unlimited
    open files                      (-n) 65536
    pipe size            (512 bytes, -p) 8
    POSIX message queues     (bytes, -q) 819200
    real-time priority              (-r) 0
    stack size              (kbytes, -s) 8192
    cpu time               (seconds, -t) unlimited
    max user processes              (-u) 515563
    virtual memory          (kbytes, -v) unlimited
    file locks                      (-x) unlimited

多个goroutine可以对应一个os线程。此处描述了设计:https://docs.google.com/document/d/1TTj4T2JO42uD5ID9e89oa0sLKhJYD0Y_kqxDv3I3XMw/edit, which references this paper: http://supertech.csail.mit.edu/papers/steal.pdf.

关于问题:

Even when 500 concurrent requests arrive at the server the number of OS threads were still stuck at 35 OS threads [...] Can someone explain me this behaviour?

由于您将 GOMAXPROCS 设置为 CPU 的数量,go 一次只会 运行 那么多 goroutines。

可能有点令人困惑的一件事是 goroutines 并不总是 运行ning(有时它们是 "busy")。例如,如果您读取一个文件,而 OS 正在执行该工作,则 goroutine 很忙,调度程序将选择另一个 goroutine 到 运行(假设有一个)。文件读取完成后,goroutine 将返回 "runnable" goroutines 列表。

OS 级线程的创建由调度程序处理,并且围绕系统级调用存在额外的复杂性。 (有时您需要一个真正的专用线程。请参阅:LockOSThread)但您不应期望有大量线程。

Can the no. of OS threads be increased somehow (from OS or from GOlang)?

我认为使用 LockOSThread 可能会导致创建新线程,但这无关紧要:

Will this improve the performance if no. of OS threads are increased?

没有。您的 CPU 从根本上限制了它一次可以做多少事情。 Goroutines 工作是因为事实证明 most 操作在某种程度上是 IO 绑定的,但是如果你真的在做一些 CPU 绑定的事情,那么在问题上投入更多的线程将无济于事。事实上,这可能会使情况变得更糟,因为在线程之间切换会产生开销。

换句话说,Go 在这里做出了正确的决定。

Can someone suggest some other ways of optimizing this app?

for i := 0; i < 200000000; i++ {
   x = x + 1
   x = x - 1
}

我认为你写这段代码只是为了让 CPU 做很多工作?实际代码是什么样的?

您最好的选择是找到一种优化该代码的方法,以便它需要更少的 CPU 时间。如果那不是 possible(它已经高度优化),那么您将需要添加更多计算机/CPUs 到组合中。买一台更好的电脑,或者更多。

对于多台计算机,您可以在所有计算机前面放置一个负载平衡器,这应该很容易扩展。

您还可以通过将这项工作从网络服务器中分离出来并将其移动到某个后端系统而受益。考虑使用工作队列。