mirror of
https://github.com/soypat/lneto.git
synced 2026-08-24 16:39:07 +00:00
Reduce heap allocs 2 (#46)
* reduce heap allocations in tcp logging; omit use of AppendFloat which allocates a metric sh*tton * debugheaplog: better heap statistic logging * heap: use string for pcap.Frame.Protocol * add potential to eliminate Flags.String heap alloc, remove incorrect HEAP comments * add StackAsync.DebugErr and httpraw.SetBytes * many heap alloc reductions and replacement of bytes.Equal with internal.BytesEqual
This commit is contained in:
+80
-1
@@ -1,7 +1,86 @@
|
||||
package internal
|
||||
|
||||
import "log/slog"
|
||||
import (
|
||||
"log/slog"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"sync"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
const (
|
||||
LevelTrace slog.Level = slog.LevelDebug - 2
|
||||
|
||||
usePrintLogAllocs = true
|
||||
)
|
||||
|
||||
var (
|
||||
memstats runtime.MemStats
|
||||
lastAllocs uint64
|
||||
lastMallocs uint64
|
||||
allocmu sync.Mutex
|
||||
allocbuf [256]byte
|
||||
)
|
||||
|
||||
func LogAllocs(msg string) {
|
||||
allocmu.Lock()
|
||||
runtime.ReadMemStats(&memstats)
|
||||
if memstats.TotalAlloc == lastAllocs {
|
||||
allocmu.Unlock()
|
||||
return
|
||||
}
|
||||
if usePrintLogAllocs {
|
||||
print("[ALLOC] ", msg)
|
||||
print(" inc=", int64(memstats.TotalAlloc)-int64(lastAllocs))
|
||||
print(" n=", int64(memstats.Mallocs)-int64(lastMallocs))
|
||||
print(" heap=", memstats.HeapAlloc)
|
||||
print(" tot=", memstats.TotalAlloc)
|
||||
println()
|
||||
} else {
|
||||
n := copy(allocbuf[:], "[ALLOC] ")
|
||||
n += copy(allocbuf[n:], msg)
|
||||
n += copyValueInt(allocbuf[n:], "inc", int64(memstats.TotalAlloc)-int64(lastAllocs))
|
||||
n += copyValueInt(allocbuf[n:], "n", int64(memstats.Mallocs)-int64(lastMallocs))
|
||||
n += copyValueUint(allocbuf[n:], "heap", memstats.HeapAlloc)
|
||||
n += copyValueUint(allocbuf[n:], "tot", memstats.TotalAlloc)
|
||||
println(unsafe.String(&allocbuf[0], n))
|
||||
}
|
||||
lastAllocs = memstats.TotalAlloc
|
||||
lastMallocs = memstats.Mallocs
|
||||
allocmu.Unlock()
|
||||
}
|
||||
|
||||
func copyValueInt(buf []byte, key string, v int64) int {
|
||||
// ' ' + key + '=' + up to 20 chars for int64
|
||||
if len(buf) < 2+len(key)+20 {
|
||||
return 0
|
||||
}
|
||||
buf[0] = ' '
|
||||
n := 1 + copy(buf[1:], key)
|
||||
buf[n] = '='
|
||||
n++
|
||||
return n + copyInt(buf[n:], v)
|
||||
}
|
||||
|
||||
func copyValueUint(buf []byte, key string, v uint64) int {
|
||||
if len(buf) < 2+len(key)+20 {
|
||||
return 0
|
||||
}
|
||||
buf[0] = ' '
|
||||
n := 1 + copy(buf[1:], key)
|
||||
buf[n] = '='
|
||||
n++
|
||||
return n + copyUint(buf[n:], v)
|
||||
}
|
||||
|
||||
// copyInt formats v into buf and returns the number of bytes written.
|
||||
// Caller must ensure cap(buf) >= 20.
|
||||
func copyInt(buf []byte, v int64) int {
|
||||
return len(strconv.AppendInt(buf[:0], v, 10))
|
||||
}
|
||||
|
||||
// copyUint formats v into buf and returns the number of bytes written.
|
||||
// Caller must ensure cap(buf) >= 20.
|
||||
func copyUint(buf []byte, v uint64) int {
|
||||
return len(strconv.AppendUint(buf[:0], v, 10))
|
||||
}
|
||||
|
||||
@@ -15,9 +15,6 @@ const (
|
||||
)
|
||||
|
||||
var (
|
||||
memstats runtime.MemStats
|
||||
lastAllocs uint64
|
||||
|
||||
timebuf [len(timefmt) * 2]byte
|
||||
)
|
||||
|
||||
@@ -28,12 +25,7 @@ func LogEnabled(l *slog.Logger, lvl slog.Level) bool {
|
||||
func LogAttrs(_ *slog.Logger, level slog.Level, msg string, attrs ...slog.Attr) {
|
||||
now := time.Now()
|
||||
n := len(now.AppendFormat(timebuf[:0], timefmt))
|
||||
runtime.ReadMemStats(&memstats)
|
||||
if memstats.TotalAlloc != lastAllocs {
|
||||
print("[ALLOC] inc=", int64(memstats.TotalAlloc)-int64(lastAllocs))
|
||||
print(" tot=", memstats.TotalAlloc, " seqs")
|
||||
println()
|
||||
}
|
||||
LogAllocs(msg)
|
||||
print("time=", unsafe.String(&timebuf[0], n), " ")
|
||||
if level == LevelTrace {
|
||||
print("TRACE ")
|
||||
@@ -57,8 +49,12 @@ func LogAttrs(_ *slog.Logger, level slog.Level, msg string, attrs ...slog.Attr)
|
||||
}
|
||||
}
|
||||
println()
|
||||
allocmu.Lock()
|
||||
runtime.ReadMemStats(&memstats)
|
||||
if memstats.TotalAlloc != lastAllocs {
|
||||
lastAllocs = memstats.TotalAlloc
|
||||
if lastAllocs != memstats.TotalAlloc {
|
||||
print("alloc increase in heaplog")
|
||||
}
|
||||
lastAllocs = memstats.TotalAlloc
|
||||
lastMallocs = memstats.Mallocs
|
||||
allocmu.Unlock()
|
||||
}
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
package ltesto
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"math"
|
||||
"math/rand"
|
||||
|
||||
"github.com/soypat/lneto"
|
||||
"github.com/soypat/lneto/ethernet"
|
||||
"github.com/soypat/lneto/internal"
|
||||
"github.com/soypat/lneto/ipv4"
|
||||
"github.com/soypat/lneto/ipv4/icmpv4"
|
||||
"github.com/soypat/lneto/tcp"
|
||||
@@ -134,9 +134,9 @@ func (gen *PacketGen) AppendRandomIPv4TCPPacket(dst []byte, rng *rand.Rand, seg
|
||||
switch {
|
||||
case gen.SrcTCP != tfrm.SourcePort():
|
||||
panic("IP options overwrite TCP header")
|
||||
case !bytes.Equal(ifrm.Options(), ipOpts):
|
||||
case !internal.BytesEqual(ifrm.Options(), ipOpts):
|
||||
panic("bad ip options written, ensure ip options length is multiple of 4")
|
||||
case !bytes.Equal(tfrm.Options(), tcpOpts):
|
||||
case !internal.BytesEqual(tfrm.Options(), tcpOpts):
|
||||
panic("bad tcp options written, ensure tcp options length is multiple of 4")
|
||||
case *ifrm.DestinationAddr() != gen.DstIPv4:
|
||||
panic("IP options overwrite own header")
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package internal
|
||||
|
||||
import "unsafe"
|
||||
|
||||
// IsZeroed returns true if all arguments are set to their zero value.
|
||||
func IsZeroed[T comparable](a ...T) bool {
|
||||
var z T
|
||||
@@ -65,3 +67,15 @@ func SliceReclaim[T any](ptr *[]T) *T {
|
||||
}
|
||||
return &(*ptr)[n]
|
||||
}
|
||||
|
||||
// BytesEqual is heapless replacement of [bytes.Equal] since it allocates in tinygo.
|
||||
// https://github.com/tinygo-org/tinygo/issues/4045
|
||||
func BytesEqual(a, b []byte) bool {
|
||||
if len(a) != len(b) {
|
||||
return false
|
||||
}
|
||||
if len(a) == 0 {
|
||||
return true
|
||||
}
|
||||
return unsafe.String(&a[0], len(a)) == unsafe.String(&b[0], len(b))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user