Skip to content
This repository was archived by the owner on Mar 2, 2026. It is now read-only.

Commit 113247b

Browse files
committed
sysfs: improve SPI heap utilization and performance
- Put spiConn inside the SPI object instead of allocating it on the heap. - Create a 4 items array of spiIOCTransfer inside the SPI object to remove memory allocation when doing transfers of 4 items or less. This results in dramatic improvement when using spi.Conn.Tx(). Test environment: - go 1.10.2 - Intel(R) Xeon(R) CPU E5-2690 0 @ 2.90GHz - Raspberry Pi 3 Intel: 60% faster Before: BenchmarkSPI 10000000 167 ns/op 32 B/op 1 allocs/op After: BenchmarkSPI 20000000 68.0 ns/op 0 B/op 0 allocs/op RPi3: 37% faster Before: BenchmarkSPI 1000000 1795 ns/op 32 B/op 1 allocs/op After: BenchmarkSPI 1000000 1125 ns/op 0 B/op 0 allocs/op
1 parent a07da60 commit 113247b

File tree

1 file changed

+15
-5
lines changed

1 file changed

+15
-5
lines changed

host/sysfs/spi.go

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,9 @@ type SPI struct {
6767
mosi gpio.PinOut
6868
miso gpio.PinIn
6969
cs gpio.PinOut
70+
spiConn spiConn
71+
// Heap optimization: reduce the amount of memory allocations.
72+
io [4]spiIOCTransfer
7073
}
7174

7275
// Close closes the handle to the SPI driver. It is not a requirement to close
@@ -137,7 +140,8 @@ func (s *SPI) Connect(maxHz int64, mode spi.Mode, bits int) (spi.Conn, error) {
137140
if err := s.setFlag(spiIOCMode, uint64(m)); err != nil {
138141
return nil, fmt.Errorf("sysfs-spi: setting mode %v failed: %v", mode, err)
139142
}
140-
return &spiConn{s}, nil
143+
s.spiConn.s = s
144+
return &s.spiConn, nil
141145
}
142146

143147
func (s *SPI) duplex() conn.Duplex {
@@ -218,9 +222,10 @@ func (s *SPI) txInternal(w, r []byte) (int, error) {
218222
if s.maxHzDev != 0 && (s.maxHzPort == 0 || s.maxHzDev < s.maxHzPort) {
219223
hz = s.maxHzDev
220224
}
221-
var m spiIOCTransfer
222-
m.reset(w, r, hz, s.bitsPerWord)
223-
if err := s.f.Ioctl(spiIOCTx(1), uintptr(unsafe.Pointer(&m))); err != nil {
225+
// The Ioctl() call below is seen as a memory escape, so the spiIOCTransfer
226+
// object cannot be on the stack.
227+
s.io[0].reset(w, r, hz, s.bitsPerWord)
228+
if err := s.f.Ioctl(spiIOCTx(1), uintptr(unsafe.Pointer(&s.io[0]))); err != nil {
224229
return 0, fmt.Errorf("sysfs-spi: I/O failed: %v", err)
225230
}
226231
return l, nil
@@ -267,7 +272,12 @@ func (s *SPI) txPackets(p []spi.Packet) error {
267272
if s.maxHzDev != 0 && (s.maxHzPort == 0 || s.maxHzDev < s.maxHzPort) {
268273
hz = s.maxHzDev
269274
}
270-
m := make([]spiIOCTransfer, len(p))
275+
var m []spiIOCTransfer
276+
if len(p) > len(s.io) {
277+
m = make([]spiIOCTransfer, len(p))
278+
} else {
279+
m = s.io[:len(p)]
280+
}
271281
for i := range p {
272282
bits := p[i].BitsPerWord
273283
if bits == 0 {

0 commit comments

Comments
 (0)