По-хорошему надо написать benchmark-тесты, найти узкие места и устранить. И обычно, когда пишут на Go, стараются в main-е держать лишь парсер флагов и запускатель когда и другого пакета. Таким образом легко становится делать тесты и разбираться почему что-то медленно работает.Если это не очень актуально (как я понял), то, конечно же, лень тратить время на это. Поэтому давайте посмотрим хотя бы быстрым взглядом поверхностно (извиняюсь за низкое качество анализа):
1. Вы каждый раз выделаете огромный буфер:
buf := make([]byte, BUF_LEN)
Если убрать `go` от `serve`, то можно было бы один раз выделить этот буфер (просто вне `for`-а) и постоянно его использовать. Если `go` нужен. то можно постараться либо изменить serve (перенести функционал), чтобы ему не нужен был этот буфер, либо в крайнем случае хотя бы просто применить `sync.Pool` для переиспользования буферов. По факту в отдельной рутине (`go serve`) достаточно было оставить только ту часть, что с syscall-ами, ибо вычислительная часть занимает сильно меньше, что позволило бы сделать один статический буфер (и избежать даже `sync.Pool`).
Есть ощущение, что именно эта строчка и портит вам всю статистику.
2. Что такое `daemon`? Не вижу его в import-ах.
3.
ipv4 = append(ipv4, fmt.Sprintf("%b %s", (head>>1), ip.String()))
...
hostname = fmt.Sprintf("%s", buf[pointer+1:length-4-16])
...
report := fmt.Sprintf("addr=%s\nhost=%s\n", addr.String(), hostname)
if overhead {
report += "overhead\n"
}
... и т.п.
Это всё тоже в некоторых случаях нехило давит и на CPU и на GC. Опять же, sync.Pool + strings.Builder (вместо Sprintf).
Кроме того, преобразование []byte в string делает копию (давит и на CPU и на GC). Вы могли вообще без string тут обойтись (см. bytes.Builder, вместо strings.Builder).
То есть да, Sprintf удобен, но когда пишешь именно тот кусочек кода, где действительно важна производительность, то лучше избегать его использования.
---
В целом, если вам будет действительно важно что-то прооптимизировать на Go, то я буду рад разочек помочь. Если вам важно прооптимизировать именно эту программу, то с вашей стороны я бы попросил написать unit/integration тест для той части кода, что мы хотим оптимизировать. А я со своей стороны пообещаю добиться производительности близкой к Сишной без особых костылей.
Сделать unit/integration тест очень несложно: просто превратите listen_udp в функцию, которая принимает этот `pc` извне аргументом типа net.Conn и напишите тест, который вызывает эту функцию, посылает туда хотя бы парочку разных пакетов и проверяет правильность результата.
Я добавлю benchmark тест. Сделаю `go test ./ -bench=. -cpuprofile /tmp/cpu.pprof -memprofile /tmp/mem.pprof`, глянем на реально слабые места этой программы и исправим ;)
https://golang.org/pkg/testing/