优化 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)对这个应用程序进行负载测试时,我得到的结果是
- 每个请求的平均响应时间 2.51 秒
- T运行操作速率 160.57 t运行每秒操作
有人可以回答我下面的问题吗:-
- 当我 运行 有 100、200、500 个并发用户时,我看到了编号。上述应用程序使用的 OS 个线程在应用程序刚刚启动时从 7 个卡在了 35 个。增加 no.of 并发连接不会改变这个数字。即使有 500 个并发请求到达服务器,OS 线程数仍然停留在 35 OS 个线程(应用程序以 runtime.GOMAXPROCS(runtime.NumCPU()) 启动) .当测试停止时,数字仍然是 35。
- 有人可以向我解释这种行为吗?
- 能不能。 OS 个线程以某种方式增加(来自 OS 或来自 GOlang)?
- 如果没有,这会提高性能吗? OS 个线程增加了?
- 有人可以建议一些其他优化此应用程序的方法吗?
环境:-
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 到组合中。买一台更好的电脑,或者更多。
对于多台计算机,您可以在所有计算机前面放置一个负载平衡器,这应该很容易扩展。
您还可以通过将这项工作从网络服务器中分离出来并将其移动到某个后端系统而受益。考虑使用工作队列。
我有一个玩具网络应用程序,它非常 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)对这个应用程序进行负载测试时,我得到的结果是
- 每个请求的平均响应时间 2.51 秒
- T运行操作速率 160.57 t运行每秒操作
有人可以回答我下面的问题吗:-
- 当我 运行 有 100、200、500 个并发用户时,我看到了编号。上述应用程序使用的 OS 个线程在应用程序刚刚启动时从 7 个卡在了 35 个。增加 no.of 并发连接不会改变这个数字。即使有 500 个并发请求到达服务器,OS 线程数仍然停留在 35 OS 个线程(应用程序以 runtime.GOMAXPROCS(runtime.NumCPU()) 启动) .当测试停止时,数字仍然是 35。
- 有人可以向我解释这种行为吗?
- 能不能。 OS 个线程以某种方式增加(来自 OS 或来自 GOlang)?
- 如果没有,这会提高性能吗? OS 个线程增加了?
- 有人可以建议一些其他优化此应用程序的方法吗?
环境:-
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 到组合中。买一台更好的电脑,或者更多。
对于多台计算机,您可以在所有计算机前面放置一个负载平衡器,这应该很容易扩展。
您还可以通过将这项工作从网络服务器中分离出来并将其移动到某个后端系统而受益。考虑使用工作队列。