Skip to content

Commit b4ffab2

Browse files
committed
p2p: cache per-message ingress gauges to avoid allocs in Peer.handle
1 parent 306e2c9 commit b4ffab2

2 files changed

Lines changed: 42 additions & 4 deletions

File tree

p2p/peer.go

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -364,9 +364,7 @@ func (p *Peer) handle(msg Msg) error {
364364
}
365365

366366
if p.metricsEnabled {
367-
localCode := msg.Code - proto.offset
368-
proto.meters[localCode].SetUint32(msg.meterSize)
369-
proto.meterPackets[localCode].Set(1)
367+
proto.meterIngress(msg.Code-proto.offset, msg.meterSize)
370368
}
371369
select {
372370
case proto.in <- msg:
@@ -468,11 +466,26 @@ type protoRW struct {
468466
w MsgWriter
469467
logger log.Logger
470468

471-
// cached gauge pointers indexed by local message code, avoids lock+map lookup on every message
472469
meters []metrics.Gauge
473470
meterPackets []metrics.Gauge
474471
}
475472

473+
func (rw *protoRW) meterIngress(code uint64, size uint32) {
474+
if rw.meters == nil {
475+
rw.meters = make([]metrics.Gauge, rw.Length)
476+
rw.meterPackets = make([]metrics.Gauge, rw.Length)
477+
}
478+
g := rw.meters[code]
479+
if g == nil {
480+
name := fmt.Sprintf("%s_%s_%d_%#02x", ingressMeterName, rw.Name, rw.Version, code)
481+
g = metrics.GetOrCreateGauge(name)
482+
rw.meters[code] = g
483+
rw.meterPackets[code] = metrics.GetOrCreateGauge(name + "_packets")
484+
}
485+
g.SetUint32(size)
486+
rw.meterPackets[code].Set(1)
487+
}
488+
476489
var traceMsg = false
477490

478491
func (rw *protoRW) WriteMsg(msg Msg) (err error) {

p2p/peer_test.go

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ import (
3232
"time"
3333

3434
"github.com/erigontech/erigon/common/log/v3"
35+
"github.com/erigontech/erigon/diagnostics/metrics"
3536
"github.com/erigontech/erigon/p2p/enode"
3637
"github.com/erigontech/erigon/p2p/enr"
3738
)
@@ -147,6 +148,30 @@ func TestPeerProtoReadMsg(t *testing.T) {
147148
}
148149
}
149150

151+
func TestProtoRWMeterIngress(t *testing.T) {
152+
rw := &protoRW{Protocol: Protocol{Name: "metertest", Version: 99, Length: 4}}
153+
154+
rw.meterIngress(2, 1234)
155+
156+
if got := metrics.GetOrCreateGauge("p2p_ingress_metertest_99_0x02").GetValue(); got != 1234 {
157+
t.Fatalf("ingress bytes gauge = %v, want 1234", got)
158+
}
159+
if got := metrics.GetOrCreateGauge("p2p_ingress_metertest_99_0x02_packets").GetValue(); got != 1 {
160+
t.Fatalf("ingress packets gauge = %v, want 1", got)
161+
}
162+
}
163+
164+
func TestProtoRWMeterIngressNoAllocs(t *testing.T) {
165+
rw := &protoRW{Protocol: Protocol{Name: "metertestnoalloc", Version: 1, Length: 8}}
166+
rw.meterIngress(3, 100) // warm the per-code gauge cache
167+
168+
if allocs := testing.AllocsPerRun(1000, func() {
169+
rw.meterIngress(3, 100)
170+
}); allocs != 0 {
171+
t.Errorf("meterIngress allocates %v objects per call after warmup, want 0", allocs)
172+
}
173+
}
174+
150175
func TestPeerProtoEncodeMsg(t *testing.T) {
151176
proto := Protocol{
152177
Name: "a",

0 commit comments

Comments
 (0)