diff --git a/benchmark/netpoll/concurrent/main.go b/benchmark/netpoll/concurrent/main.go new file mode 100644 index 0000000..2746360 --- /dev/null +++ b/benchmark/netpoll/concurrent/main.go @@ -0,0 +1,124 @@ +package main + +import ( + "fmt" + "net" + "time" + "sync" + "bufio" + "context" + "math/rand" + + "github.com/cloudwego/netpoll" +) + +func runServer(port string, stopChan chan interface{}) { + listener, err := netpoll.CreateListener("tcp", port) + if err != nil { + panic("can't failed here") + } + eventLoop, err := netpoll.NewEventLoop(handleConnection) + if err != nil { + panic("can't failed here") + } + go eventLoop.Serve(listener) + + go func() { + <- stopChan + eventLoop.Shutdown(context.Background()) + listener.Close() + }() +} + +func handleConnection(ctx context.Context, conn netpoll.Connection) error { + defer conn.Close() + + reader := conn.Reader() + writer := conn.Writer() + + for { + message, err := reader.Until('\n') + if err != nil { + return err + } + _, err = writer.WriteBinary(message) + if err != nil { + return err + } + err = writer.Flush() + if err != nil { + return err + } + } +} + +const charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" + +func randomString(length int) string { + seed := rand.New(rand.NewSource(time.Now().UnixNano())) + b := make([]byte, length) + for i := range b { + b[i] = charset[seed.Intn(len(charset))] + } + return string(b) +} + +func main() { + port := ":8000" + stopchan := make(chan interface{}) + runServer(port, stopchan) + defer close(stopchan) + + c := 12 + m := 10000 + n := 100 + messageLength := 48 + + start := time.Now() + + var wg sync.WaitGroup + for i := 0; i < c; i++ { + wg.Add(1) + go func() { + defer wg.Done() + for j := 0; j < m; j++ { + conn, err := net.Dial("tcp", port) + if err != nil { + fmt.Printf("failed to connect to server: %v\n", err) + conn.Close() + continue + } + + for k := 0; k < n; k++ { + message := randomString(messageLength) + "\n" + _, err = conn.Write([]byte(message)) + if err != nil { + fmt.Printf("failed to send message: %v\n", err) + break + } + + response, err := bufio.NewReader(conn).ReadString('\n') + if err != nil { + fmt.Printf("failed to read response: %v\n", err) + break + } + + if message != response { + fmt.Printf("%v %v %v failed\n", i, j, k) + fmt.Printf("expect: %s\n", message) + fmt.Printf("actual: %s\n", response) + break + } + } + + conn.Close() + } + }() + } + wg.Wait() + + elapsed := time.Since(start) + minutes := int(elapsed.Minutes()) + seconds := int(elapsed.Seconds()) % 60 + fmt.Printf("the total time for netpoll to execute %dk connections, with %d writes per connection, is: %d minutes %d seconds\n", m / 1000, n, minutes, seconds) +} diff --git a/benchmark/netpoll/serial/main.go b/benchmark/netpoll/serial/main.go new file mode 100644 index 0000000..ae1647d --- /dev/null +++ b/benchmark/netpoll/serial/main.go @@ -0,0 +1,114 @@ +package main + +import ( + "bufio" + "context" + "fmt" + "math/rand" + "net" + "time" + + "github.com/cloudwego/netpoll" +) + +func runServer(port string, stopChan chan interface{}) { + listener, err := netpoll.CreateListener("tcp", port) + if err != nil { + panic("can't failed here") + } + eventLoop, err := netpoll.NewEventLoop(handleConnection) + if err != nil { + panic("can't failed here") + } + go eventLoop.Serve(listener) + + go func() { + <- stopChan + eventLoop.Shutdown(context.Background()) + listener.Close() + }() +} + +func handleConnection(ctx context.Context, conn netpoll.Connection) error { + defer conn.Close() + + reader := conn.Reader() + writer := conn.Writer() + + for { + message, err := reader.Until('\n') + if err != nil { + return err + } + _, err = writer.WriteBinary(message) + if err != nil { + return err + } + err = writer.Flush() + if err != nil { + return err + } + } +} + +const charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" + +func randomString(length int) string { + seed := rand.New(rand.NewSource(time.Now().UnixNano())) + b := make([]byte, length) + for i := range b { + b[i] = charset[seed.Intn(len(charset))] + } + return string(b) +} + +func main() { + port := ":8000" + stopchan := make(chan interface{}) + runServer(port, stopchan) + defer close(stopchan) + + m := 100000 + n := 100 + messageLength := 48 + + start := time.Now() + + for i := 0; i < m; i++ { + conn, err := net.Dial("tcp", port) + if err != nil { + fmt.Printf("failed to connect to server: %v\n", err) + conn.Close() + continue + } + + for j := 0; j < n; j++ { + message := randomString(messageLength) + "\n" + _, err = conn.Write([]byte(message)) + if err != nil { + fmt.Printf("failed to send message: %v\n", err) + break + } + + response, err := bufio.NewReader(conn).ReadString('\n') + if err != nil { + fmt.Printf("failed to read response: %v\n", err) + break + } + + if message != response { + fmt.Printf("%v %v failed\n", i, j) + fmt.Printf("expect: %s\n", message) + fmt.Printf("actual: %s\n", response) + break + } + } + + conn.Close() + } + + elapsed := time.Since(start) + minutes := int(elapsed.Minutes()) + seconds := int(elapsed.Seconds()) % 60 + fmt.Printf("the total time for netpoll to execute %dk connections, with %d writes per connection, is: %d minutes %d seconds\n", m / 1000, n, minutes, seconds) +} diff --git a/benchmark/run_benchmark.sh b/benchmark/run_benchmark.sh index 68d0a25..9926dc3 100755 --- a/benchmark/run_benchmark.sh +++ b/benchmark/run_benchmark.sh @@ -2,10 +2,14 @@ go build -v -o ./net/bench_serial ./net/serial go build -v -o ./net/bench_concurrent ./net/concurrent +go build -v -o ./netpoll/bench_serial ./netpoll/serial +go build -v -o ./netpoll/bench_concurrent ./netpoll/concurrent go build -v -o ./uring/bench_serial ./uring/serial go build -v -o ./uring/bench_concurrent ./uring/concurrent ./net/bench_serial ./net/bench_concurrent +./netpoll/bench_serial +./netpoll/bench_concurrent ./uring/bench_serial ./uring/bench_concurrent diff --git a/go.mod b/go.mod index 75e23bd..e7f15e9 100644 --- a/go.mod +++ b/go.mod @@ -3,12 +3,14 @@ module github.com/zjregee/anet go 1.22.0 require ( + github.com/cloudwego/netpoll v0.6.2 github.com/google/uuid v1.6.0 github.com/sirupsen/logrus v1.9.3 github.com/stretchr/testify v1.7.0 ) require ( + github.com/bytedance/gopkg v0.0.0-20240507064146-197ded923ae3 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect golang.org/x/sys v0.20.0 // indirect diff --git a/go.sum b/go.sum index 2ab6ecc..3087e97 100644 --- a/go.sum +++ b/go.sum @@ -1,3 +1,7 @@ +github.com/bytedance/gopkg v0.0.0-20240507064146-197ded923ae3 h1:ZKUHguI38SRQJkq7hhmwn8lAv3xM6B5qkj1IneS15YY= +github.com/bytedance/gopkg v0.0.0-20240507064146-197ded923ae3/go.mod h1:FtQG3YbQG9L/91pbKSw787yBQPutC+457AvDW77fgUQ= +github.com/cloudwego/netpoll v0.6.2 h1:+KdILv5ATJU+222wNNXpHapYaBeRvvL8qhJyhcxRxrQ= +github.com/cloudwego/netpoll v0.6.2/go.mod h1:kaqvfZ70qd4T2WtIIpCOi5Cxyob8viEpzLhCrTrz3HM= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -10,9 +14,16 @@ github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVs github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +golang.org/x/net v0.0.0-20221014081412-f15817d10f9b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=