-
Notifications
You must be signed in to change notification settings - Fork 17.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
runtime: timer doesn't scale on multi-CPU systems with a lot of timers #15133
Comments
What did you see instead? What is performance depending on number of cores?
I don't understand relation between iowait and memory ping-pong? iowait if waiting for IO, like hard drive, memory accesses are not IO. How exactly does profile look?
Would it be possible to change it to SetDeadline? It would setup 1 timer instead of 2. |
We probably can merge this into #6239 |
FYI, this is not an OS scalability problem, since the CPU usage graph above until 15:33 corresponds to 'app process per CPU core' mode of the app with enabled connection deadlines. We had to switch to this mode due to the issue with timer scalability in Go runtime. |
Thanks for the detained info! This confirms that the main issue is the global timers mutex. siftdown/up consume ~3% of time. If we distribute timers, then siftdown/up should become faster as well (smaller per-P heap + better locality).
Sounds pretty bad. @rsc @aclements |
…dline calls for keep-alive connections, since they don't scale properly. See golang/go#15133 for details
…dline calls for keep-alive connections, since they don't scale properly. See golang/go#15133 for details
…dline calls for keep-alive connections, since they don't scale properly. See golang/go#15133 for details.
CL https://golang.org/cl/34784 mentions this issue. |
The CL fixes timers scalability issue for us. Simple repro: Hammer the following http server with more than 100K concurrent http keep-alive connections and send serveral requests per second on each such a connection. The server must run on multi-core machine with GOMAXPROCS=NumCPU. package main
import (
"flag"
"fmt"
"log"
"net/http"
"time"
)
var addr = flag.String("addr", ":80", "TCP address to listen to")
func main() {
flag.Parse()
s := &http.Server{
Addr: *addr,
// These timeouts trigger high iowait without the CL 34784
// if many requests are sent over more than 100K
// keep-alive http connections.
ReadTimeout: 90*time.Second,
WriteTimeout: 5*time.Second,
Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "OK")
}),
}
if err := s.ListenAndServe(); err != nil {
log.Fatalf("error in ListenAndServe: %s", err)
}
}
|
Update deadline only if more than 25% of the last deadline pasted. See golang/go#15133 for details.
Please answer these questions before submitting your issue. Thanks!
What version of Go are you using (
go version
)?What operating system and processor architecture are you using (
go env
)?What did you do?
I run an http server serving more than a million concurrent TCP keep-alive connections on a system with 32 CPU cores. Each connection has read and write deadlines set via
net.Conn.SetReadDeadline
andnet.Conn.SetWriteDeadline
.What did you expect to see?
Server performance should scale with
GOMAXPROCS
up to the number of CPU cores.What did you see instead?
addtimer
anddeltimer
functions fromruntime/time.go
are at the top of CPU profile.iowait
reaches 20% due to memory ping-pong across CPU cores insidesiftupTimer
andsiftdownTimer
functions inruntime/time.go
.@dvyukov , could you look into this?
The text was updated successfully, but these errors were encountered: