*Problem Description*
I’m running a simple TCP throughput benchmark using Go 1.24.6.
Both the client and server have abundant CPU, memory, and network
resources, and no system-level limits (ulimits, NIC queues, CPU throttling,
etc.) are in effect.
However, I’m observing an unexpected behavior:
*The throughput of each individual goroutine becomes higher when the
overall client concurrency increases.*
Example: per-goroutine throughput at concurrency=5 < concurrency=10 <
concurrency=15.
This is counterintuitive — I expect each goroutine's performance to remain
roughly the same regardless of how many other goroutines are running.
*Test Setup* *Server*
The server sends a fixed 64MiB random buffer to every client connection. It
uses a 1 MiB write buffer.
*Client*
The client spawns N concurrent goroutines (concurrency = 5, 10, 15...).
Each goroutine opens a TCP connection and reads until EOF using a 32 MiB
buffer.
*Observed Behavior*
-
When running with 1 goroutine, the read time is *longer*.
-
As I increase concurrency to 5, 10, 15... the *throughput of each
individual goroutine improves significantly*.
-
This happens even though:
-
CPU is not saturated
-
Network is not saturated
-
Disk is not involved
-
Both machines have very high bandwidth (10 GbE+)
*Expected Behavior*
Each goroutine should achieve similar throughput regardless of how many
goroutines are running concurrently, assuming sufficient system resources.
*Test code*
1、server:
package main
import (
"bytes"
"fmt"
"math/rand"
"net"
"net/http"
_ "net/http/pprof"
"time"
"io"
)
const dataSize = 64 * 1024 * 1024 // 64 MiB
func main() {
go func() {
fmt.Println("pprof listening on :6060")
http.ListenAndServe("10.58.3.23:6060", nil)
}()
ln, err := net.Listen("tcp", "10.58.3.23:9090")
if err != nil {
panic(err)
}
fmt.Println("Server listening on :9090")
data := make([]byte, dataSize)
rand.Read(data)
for {
conn, err := ln.Accept()
if err != nil {
fmt.Println("Accept error:", err)
continue
}
go handleConn(conn, data)
}
}
func handleConn(conn net.Conn, data []byte) {
defer conn.Close()
fmt.Println("Client connected:", conn.RemoteAddr())
start := time.Now()
r := bytes.NewReader(data)
_, err := io.Copy(conn, r)
if err != nil {
fmt.Println("Write error:", err)
return
}
duration := time.Since(start)
fmt.Printf("Sent %d bytes to %v, duration: %v\n", dataSize, conn.RemoteAddr
(), duration)
}
*2、client code:*
package main
import (
"fmt"
"io"
"net"
"sync"
"time"
)
const concurrency = 4
func main() {
var wg sync.WaitGroup
for i := 0; i < concurrency; i++ {
wg.Add(1)
go func(id int) {
defer wg.Done()
start := time.Now()
conn, err := net.Dial("tcp", "10.58.3.23:9090")
if err != nil {
fmt.Printf("[Worker %d] Dial error: %v\n", id, err)
return
}
defer conn.Close()
buf := make([]byte, 64*1024*1024) // 1 MiB buffer
total := 0
for {
n, err := conn.Read(buf)
total += n
if err == io.EOF {
break
}
if err != nil {
fmt.Printf("[Worker %d] Read error: %v\n", id, err)
return
}
}
duration := time.Since(start)
fmt.Printf("[Worker %d] Finished reading %d bytes, duration: %v\n", id,
total, duration)
}(i + 1)
}
wg.Wait()
fmt.Println("All goroutines completed")
}
--
You received this message because you are subscribed to the Google Groups
"golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
To view this discussion visit
https://groups.google.com/d/msgid/golang-nuts/f307dd0e-cc9d-434e-a452-8e367e987f3bn%40googlegroups.com.