Go + DPDK in < 100 lines with packetio - Sending 148 million packets/sec
| //go:build dpdk | |
| // Blast dummy UDP packets over several queues (one core each) and print | |
| // packets/sec. ~45 lines of Go driving DPDK through github.com/atoonk/packetio, | |
| // hitting 64-byte line rate on a 100G ConnectX. | |
| // | |
| // Build & run (needs libdpdk installed, e.g. `apt install libdpdk-dev`): | |
| // | |
| // mkdir blast && cd blast # drop this file in as main.go | |
| // go mod init blast | |
| // go get github.com/atoonk/packetio | |
| // go build -tags dpdk -o blast . | |
| // sudo ./blast // needs root for DPDK | |
| // | |
| // Example output (4 queues, 64-byte frames, ConnectX-6 Dx at 100G): | |
| // | |
| // 148.0 Mpps | |
| // 148.7 Mpps | |
| // 148.7 Mpps | |
| // 148.7 Mpps | |
| // 148.7 Mpps | |
| // | |
| // One queue does ~58 Mpps; more queues = more cores = more pps, up to the wire. | |
| // The same code runs on mlx5, AF_XDP or AF_PACKET by changing the import. | |
| package main | |
| import ( | |
| "fmt" | |
| "log" | |
| "sync" | |
| "sync/atomic" | |
| "time" | |
| "github.com/atoonk/packetio" | |
| "github.com/atoonk/packetio/dpdk" | |
| ) | |
| const ( | |
| device = "0000:c1:00.0" // your NIC's PCI address | |
| queues = 4 // one core each; more queues = more cores = more pps | |
| seconds = 5 | |
| ) | |
| // Empty UDP datagram to 192.0.2.2, routed via the gateway. | |
| var packet = []byte{ | |
| 0xb0, 0x8b, 0xcf, 0x4b, 0xcf, 0x3d, // dst: router MAC (your next hop) | |
| 0x7c, 0xc2, 0x55, 0xbe, 0xf4, 0xe0, // src: this NIC's MAC | |
| 0x08, 0x00, // IPv4 | |
| 0x45, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x40, 0x00, 0x40, 0x11, 0xb6, 0xcd, | |
| 0xc0, 0x00, 0x02, 0x01, // src IP 192.0.2.1 | |
| 0xc0, 0x00, 0x02, 0x02, // dst IP 192.0.2.2 | |
| 0x30, 0x39, 0x23, 0x28, 0x00, 0x08, 0x00, 0x00, // UDP :12345 -> :9000, empty | |
| } | |
| func main() { | |
| d, err := dpdk.Open(device, dpdk.WithTxQueues(queues)) | |
| if err != nil { | |
| log.Fatal(err) | |
| } | |
| defer d.Close() | |
| var sent atomic.Uint64 | |
| var wg sync.WaitGroup | |
| start := time.Now() | |
| // One goroutine per queue: each runs on its own core. | |
| for q := 0; q < queues; q++ { | |
| wg.Add(1) | |
| go func(tx packetio.TxQueue) { | |
| defer wg.Done() | |
| for time.Since(start) < seconds*time.Second { | |
| n, err := tx.SendFunc(256, func(i int, frame []byte) int { | |
| return copy(frame, packet) | |
| }) | |
| if err != nil { | |
| log.Fatal(err) | |
| } | |
| sent.Add(uint64(n)) | |
| } | |
| }(d.TxQueue(q)) | |
| } | |
| // Print the running rate once a second. | |
| go func() { | |
| last := uint64(0) | |
| for range time.Tick(time.Second) { | |
| now := sent.Load() | |
| fmt.Printf("%.1f Mpps\n", float64(now-last)/1e6) | |
| last = now | |
| } | |
| }() | |
| wg.Wait() | |
| } |
评论
?
参与讨论