Skip to content
This repository has been archived by the owner on Sep 20, 2023. It is now read-only.

Commit

Permalink
sysfs: improve SPI heap utilization and performance
Browse files Browse the repository at this point in the history
- 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
  • Loading branch information
maruel committed May 9, 2018
1 parent a07da60 commit 113247b
Showing 1 changed file with 15 additions and 5 deletions.
20 changes: 15 additions & 5 deletions host/sysfs/spi.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,9 @@ type SPI struct {
mosi gpio.PinOut
miso gpio.PinIn
cs gpio.PinOut
spiConn spiConn
// Heap optimization: reduce the amount of memory allocations.
io [4]spiIOCTransfer
}

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

func (s *SPI) duplex() conn.Duplex {
Expand Down Expand Up @@ -218,9 +222,10 @@ func (s *SPI) txInternal(w, r []byte) (int, error) {
if s.maxHzDev != 0 && (s.maxHzPort == 0 || s.maxHzDev < s.maxHzPort) {
hz = s.maxHzDev
}
var m spiIOCTransfer
m.reset(w, r, hz, s.bitsPerWord)
if err := s.f.Ioctl(spiIOCTx(1), uintptr(unsafe.Pointer(&m))); err != nil {
// The Ioctl() call below is seen as a memory escape, so the spiIOCTransfer
// object cannot be on the stack.
s.io[0].reset(w, r, hz, s.bitsPerWord)
if err := s.f.Ioctl(spiIOCTx(1), uintptr(unsafe.Pointer(&s.io[0]))); err != nil {
return 0, fmt.Errorf("sysfs-spi: I/O failed: %v", err)
}
return l, nil
Expand Down Expand Up @@ -267,7 +272,12 @@ func (s *SPI) txPackets(p []spi.Packet) error {
if s.maxHzDev != 0 && (s.maxHzPort == 0 || s.maxHzDev < s.maxHzPort) {
hz = s.maxHzDev
}
m := make([]spiIOCTransfer, len(p))
var m []spiIOCTransfer
if len(p) > len(s.io) {
m = make([]spiIOCTransfer, len(p))
} else {
m = s.io[:len(p)]
}
for i := range p {
bits := p[i].BitsPerWord
if bits == 0 {
Expand Down

0 comments on commit 113247b

Please sign in to comment.