Skip to content

Commit c9730e3

Browse files
authored
optimize large slice alloc in Stats (#105)
1 parent 6d66090 commit c9730e3

File tree

2 files changed

+58
-19
lines changed

2 files changed

+58
-19
lines changed

ethtool.go

Lines changed: 38 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ import (
2929
"bytes"
3030
"encoding/hex"
3131
"fmt"
32-
"strings"
32+
"sync"
3333
"time"
3434
"unsafe"
3535

@@ -143,6 +143,23 @@ const (
143143
DEFAULT_BLINK_DURATION = 60 * time.Second
144144
)
145145

146+
var (
147+
gstringsPool = sync.Pool{
148+
New: func() interface{} {
149+
// new() will allocate and zero-initialize the struct.
150+
// The large data array within ethtoolGStrings will be zeroed.
151+
return new(ethtoolGStrings)
152+
},
153+
}
154+
statsPool = sync.Pool{
155+
New: func() interface{} {
156+
// new() will allocate and zero-initialize the struct.
157+
// The large data array within ethtoolStats will be zeroed.
158+
return new(ethtoolStats)
159+
},
160+
}
161+
)
162+
146163
type ifreq struct {
147164
ifr_name [IFNAMSIZ]byte
148165
ifr_data uintptr
@@ -1159,41 +1176,43 @@ func (e *Ethtool) Stats(intf string) (map[string]uint64, error) {
11591176
return nil, err
11601177
}
11611178

1162-
if drvinfo.n_stats*ETH_GSTRING_LEN > MAX_GSTRINGS*ETH_GSTRING_LEN {
1179+
if drvinfo.n_stats > MAX_GSTRINGS {
11631180
return nil, fmt.Errorf("ethtool currently doesn't support more than %d entries, received %d", MAX_GSTRINGS, drvinfo.n_stats)
11641181
}
11651182

1166-
gstrings := ethtoolGStrings{
1167-
cmd: ETHTOOL_GSTRINGS,
1168-
string_set: ETH_SS_STATS,
1169-
len: drvinfo.n_stats,
1170-
data: [MAX_GSTRINGS * ETH_GSTRING_LEN]byte{},
1171-
}
1183+
gstringsPtr := gstringsPool.Get().(*ethtoolGStrings)
1184+
defer gstringsPool.Put(gstringsPtr)
11721185

1173-
if err := e.ioctl(intf, uintptr(unsafe.Pointer(&gstrings))); err != nil {
1186+
gstringsPtr.cmd = ETHTOOL_GSTRINGS
1187+
gstringsPtr.string_set = ETH_SS_STATS
1188+
gstringsPtr.len = drvinfo.n_stats
1189+
1190+
if err := e.ioctl(intf, uintptr(unsafe.Pointer(gstringsPtr))); err != nil {
11741191
return nil, err
11751192
}
11761193

1177-
stats := ethtoolStats{
1178-
cmd: ETHTOOL_GSTATS,
1179-
n_stats: drvinfo.n_stats,
1180-
data: [MAX_GSTRINGS]uint64{},
1181-
}
1194+
statsPtr := statsPool.Get().(*ethtoolStats)
1195+
defer statsPool.Put(statsPtr)
1196+
1197+
statsPtr.cmd = ETHTOOL_GSTATS
1198+
statsPtr.n_stats = drvinfo.n_stats
11821199

1183-
if err := e.ioctl(intf, uintptr(unsafe.Pointer(&stats))); err != nil {
1200+
if err := e.ioctl(intf, uintptr(unsafe.Pointer(statsPtr))); err != nil {
11841201
return nil, err
11851202
}
11861203

1187-
result := make(map[string]uint64)
1204+
result := make(map[string]uint64, drvinfo.n_stats)
11881205
for i := 0; i != int(drvinfo.n_stats); i++ {
1189-
b := gstrings.data[i*ETH_GSTRING_LEN : i*ETH_GSTRING_LEN+ETH_GSTRING_LEN]
1190-
strEnd := strings.Index(string(b), "\x00")
1206+
b := gstringsPtr.data[i*ETH_GSTRING_LEN : (i+1)*ETH_GSTRING_LEN]
1207+
1208+
strEnd := bytes.IndexByte(b, 0)
11911209
if strEnd == -1 {
11921210
strEnd = ETH_GSTRING_LEN
11931211
}
11941212
key := string(b[:strEnd])
1213+
11951214
if len(key) != 0 {
1196-
result[key] = stats.data[i]
1215+
result[key] = statsPtr.data[i]
11971216
}
11981217
}
11991218

ethtool_test.go

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,3 +189,23 @@ func TestIdentity(t *testing.T) {
189189
t.Fatal("Unable to identity any interface of this system.")
190190
}
191191
}
192+
193+
func BenchmarkStats(b *testing.B) {
194+
intfs, err := net.Interfaces()
195+
if err != nil {
196+
b.Fatal(err)
197+
}
198+
for _, intf := range intfs {
199+
if intf.Name == "lo" {
200+
continue
201+
}
202+
b.Run(intf.Name, func(b *testing.B) {
203+
for i := 0; i < b.N; i++ {
204+
_, err := Stats(intf.Name)
205+
if err != nil {
206+
b.Fatal(err)
207+
}
208+
}
209+
})
210+
}
211+
}

0 commit comments

Comments
 (0)