UringNet is a high performance network I/O framework. It is light weighted and developed by Golang. The foundation of UringNet is io_uring - a new async IO interface - from Linux Kernel since version 5.1 in 2019. This project just comes from experimental projects in my edge computing and IoT research. I am just want to find a way to build a simple but high performance network package transmit tool in IoT gateways. I have tried traditional select/epoll and then tried io_uring, finally , the io_uring get much better performance. In most cases, the performance improvement ranges from 10% to 30%, while in the most extreme case, the performance improvement can reach up to 66%.
Though UringNet is designed to be used in IoT platforms originally, it does provide basic TCP and UDP network functions. If your project really need to handle extremely huge data traffic, you can give it a try.
In most cases, you could just use more mature network frameworks, for example, Go net/http, netty, libuv.
UringNet extensively references existing network frameworks such as gnet and Netty, with some usage patterns closely resembling those of gnet. Please note that UringNet is based on io_uring and, therefore, requires running on the Linux operating system with a kernel version higher than or equal to 5.1.
go get -u github.com/y001j/uringnet
package main
import (
"github.com/y001j/uringnet"
socket "github.com/y001j/uringnet/sockets"
"os"
"sync"
)
type testServer struct {
UringNet.BuiltinEventEngine
testloop *UringNet.Ringloop
//ring *uring_net.URingNet
addr string
multicore bool
}
// OnTraffic
//
// @Description: OnTraffic is a hook function that runs every read event completed
// @receiver ts
// @param data
// @return uring_net.Action
func (ts *testServer) OnTraffic(data *UringNet.UserData, ringnet *UringNet.URingNet) UringNet.Action {
data.WriteBuf = data.Buffer
return UringNet.Echo
}
func (ts *testServer) OnWritten(data UringNet.UserData) UringNet.Action {
return UringNet.None
}
func (ts *testServer) OnOpen(data *UringNet.UserData) ([]byte, UringNet.Action) {
return nil, UringNet.None
}
func main() {
addr := os.Args[1]
options := socket.SocketOptions{TCPNoDelay: socket.TCPNoDelay, ReusePort: true}
ringNets, _ := UringNet.NewMany(UringNet.NetAddress{socket.Tcp4, addr}, 3200, true, 8, options, &testServer{}) //runtime.NumCPU()
loop := UringNet.SetLoops(ringNets, 3000)
var waitgroup sync.WaitGroup
waitgroup.Add(1)
loop.RunMany()
waitgroup.Wait()
}
UringNet.NewMany(UringNet.NetAddress{socket.Tcp4, addr}, 3200, true, 8, options, &testServer{})
In echo stress testing, the benchmark compared go net, gnet and UringNet
Echo test tool: rust echo bench
Test environment:
Host:
CPU:Intel Core i5 12400F 6 core 12 thread
Memory: DDR4 32GB
OS:Windows 11
Virtual Machine:
VMware® Workstation 16 Pro
Processors: 8P ; Memory 16GB; Intel VT-x
OS:Ubuntu 22.04.1 LTS, Kernel:5.19.3
In HTTP stress testing, the benchmark compared gnet, fasthttp and UringNet
Echo test tool: wrk
Test Enviroment
Host:
CPU:Intel Core i5 12400F 6 core 12 thread
Memory: DDR4 32GB
OS:Windows 11
Testing Virtual Machine:
VMware® Workstation 16 Pro
Processors: 8P ; Memory 16GB; Intel VT-x
OS:Ubuntu 22.04.1 LTS, Kernel:5.19.3
wrk Virtual Machine:
VMware® Workstation 16 Pro
Processors: 4P ; Memory 8GB; Intel VT-x
OS:Ubuntu 20.04.5 LTS, Kernel:5.15.0
Warning: The project is still in development, there are still many bugs and performance issues. If you find any bugs or have any suggestions, please feel free to open an issue.
During our performance tests with wrk, we observed that UringNet requires a warm-up period during HTTP stress testing. Typically, a warm-up time of around 5 minutes is necessary to ensure that the UringNet test code reaches its peak performance, which surpasses the current fastest framework by more than 10%. I am still investigating the root cause of this behavior.