delve/proc/mem.go
aarzilli b839eda2a9 proc/variables: prefetch of target process memory
Prefetch the entire memory of structs and arrays and cache it instead
of issuing readMemory calls only when we get down to primitive types.
This reduces the number of system calls to ptrace that variables makes.

Improves performance in general, greatly improving it in some
particular cases (involving large structs).

Benchmarks without prefetching:
	BenchmarkArray-4         	      10	 132189944 ns/op	   0.06 MB/s
	BenchmarkArrayPointer-4  	       5	 202538503 ns/op	   0.04 MB/s
	BenchmarkMap-4           	     500	   3804336 ns/op	   0.27 MB/s
	BenchmarkGoroutinesInfo-4	      10	 126397104 ns/op
	BenchmarkLocalVariables-4	     500	   2494846 ns/op

Benchmarks with prefetching:
	BenchmarkArray-4         	     200	  10719087 ns/op	   0.76 MB/s
	BenchmarkArrayPointer-4  	     100	  11931326 ns/op	   0.73 MB/s
	BenchmarkMap-4           	    1000	   1466479 ns/op	   0.70 MB/s
	BenchmarkGoroutinesInfo-4	      10	 103407004 ns/op
	BenchmarkLocalVariables-4	    1000	   1530395 ns/op

Improvement factors:
	BenchmarkArray				12.33x
	BenchmarkArrayPointer		16.97x
	BenchmarkMap					 2.59x
	BenchmarkGoroutinesInfo		 1.22x
	BenchmarkLocalVariables		 1.63x
2016-01-10 13:49:03 +01:00

55 lines
1.3 KiB
Go

package proc
const cacheEnabled = true
type memoryReadWriter interface {
readMemory(addr uintptr, size int) (data []byte, err error)
writeMemory(addr uintptr, data []byte) (written int, err error)
}
type memCache struct {
cacheAddr uintptr
cache []byte
mem memoryReadWriter
}
func (m *memCache) contains(addr uintptr, size int) bool {
return addr >= m.cacheAddr && (addr+uintptr(size)) <= (m.cacheAddr+uintptr(len(m.cache)))
}
func (m *memCache) readMemory(addr uintptr, size int) (data []byte, err error) {
if m.contains(addr, size) {
d := make([]byte, size)
copy(d, m.cache[addr-m.cacheAddr:])
return d, nil
}
return m.mem.readMemory(addr, size)
}
func (m *memCache) writeMemory(addr uintptr, data []byte) (written int, err error) {
return m.mem.writeMemory(addr, data)
}
func cacheMemory(mem memoryReadWriter, addr uintptr, size int) memoryReadWriter {
if !cacheEnabled {
return mem
}
if cacheMem, isCache := mem.(*memCache); isCache {
if cacheMem.contains(addr, size) {
return mem
} else {
cache, err := cacheMem.mem.readMemory(addr, size)
if err != nil {
return mem
}
return &memCache{addr, cache, mem}
}
}
cache, err := mem.readMemory(addr, size)
if err != nil {
return mem
}
return &memCache{addr, cache, mem}
}