Fasthttp: Prefork method

Created on 23 May 2016  路  3Comments  路  Source: valyala/fasthttp

Run a separate server instance per CPU core with GOMAXPROCS=1.

Question: Why does prefork tend to perform faster than one proc that's spawning a goroutine per request (eg FrameworkBenchmarks)? For Apache, spawning a thread per connection usually is faster, so it seems the same should apply to Go (especially since they are green threads).

question

Most helpful comment

Why does prefork tend to perform faster than one proc that's spawning a goroutine per request?

Because distinct Go processes don't share state between each other in prefork mode. They work independently - the OS just spreads incoming connections among available independent processes via SO_REUSEPORT. Shared state requires access serialization (aka synchronization), which hurts scalability for programs running on multiple CPU cores. See Amdahl's law for details.

Even if your request handlers don't modify global shared resources such as maps, lists, etc., fasthttp server and go runtime may do this. For example, GC and timer subsystem modify global state.

Why would prefork method work better than spawning a goroutine for each CPU to handle connections?

Actually each fasthttp process (which may be preforked) maintains a dynamic pool of worker goroutines for serving incoming connections. See workerpool.go. Worker goroutines are created and destroyed on demand depending on the current number of established concurrent connections.

Initially fasthttp server was creating a goroutine per each incoming connection as net/http.Server do. Spawning a goroutine per connection is fast - multi-million goroutines per second may be created on an average hardware. I just optimized it further with workerpool.go.

All 3 comments

Also, why would prefork method work better than spawning a goroutine for each CPU to handle connections? It's effectively the same thing, but I assume instancing multiple versions of Go helps minimize GC pauses (or something).

Why does prefork tend to perform faster than one proc that's spawning a goroutine per request?

Because distinct Go processes don't share state between each other in prefork mode. They work independently - the OS just spreads incoming connections among available independent processes via SO_REUSEPORT. Shared state requires access serialization (aka synchronization), which hurts scalability for programs running on multiple CPU cores. See Amdahl's law for details.

Even if your request handlers don't modify global shared resources such as maps, lists, etc., fasthttp server and go runtime may do this. For example, GC and timer subsystem modify global state.

Why would prefork method work better than spawning a goroutine for each CPU to handle connections?

Actually each fasthttp process (which may be preforked) maintains a dynamic pool of worker goroutines for serving incoming connections. See workerpool.go. Worker goroutines are created and destroyed on demand depending on the current number of established concurrent connections.

Initially fasthttp server was creating a goroutine per each incoming connection as net/http.Server do. Spawning a goroutine per connection is fast - multi-million goroutines per second may be created on an average hardware. I just optimized it further with workerpool.go.

I noticed that you recently removed the prefork method from your TechEmpower benchmark code. Do you no longer believe the prefork method to offer better performance?

Was this page helpful?
0 / 5 - 0 ratings

Related issues

jeremyjpj0916 picture jeremyjpj0916  路  5Comments

shkreios picture shkreios  路  3Comments

imgurbot12 picture imgurbot12  路  5Comments

unisqu picture unisqu  路  4Comments

horgh picture horgh  路  5Comments