-
Notifications
You must be signed in to change notification settings - Fork 218
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
int: optimize performance by avoiding allocation
This change defines a new representation for Int on 64-bit machines running a POSIX operating system, by reserving a 4GB portion of the address space. Pointers to addresses in this region represent int32 values, and are disjoint from all *big.Int pointers, allowing us to represent the union in a single pointer. This means the conversion from Int to Value does not allocate. The gauss benchmark (added in this CL) shows -40% ns, -48% bytes, -63% allocs: Benchmark/bench_gauss-12 84 13648744 ns/op 3175816 B/op 105862 allocs/op (before) Benchmark/bench_gauss-12 55 24283703 ns/op 6119844 B/op 289862 allocs/op (after) On 32-but machines, or those running a non-POSIX system, we continue to use the old representation. Change-Id: Ib3b116760c5c9c0c8096314c90e68ddcb4b89bc0 . Change-Id: I512065d0f09c10cf55b87d28673282c078ae2e3c
- Loading branch information
Showing
5 changed files
with
99 additions
and
26 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
//+build !linux,!darwin | ||
//+build !amd64,!arm64,!mips64x,!ppc64x | ||
|
||
package starlark | ||
|
||
// generic Int implementation | ||
|
||
import "math/big" | ||
|
||
type intImpl struct { | ||
// We use only the signed 32-bit range of small to ensure | ||
// that small+small and small*small do not overflow. | ||
small_ int64 // minint32 <= small <= maxint32 | ||
big_ *big.Int // big != nil <=> value is not representable as int32 | ||
} | ||
|
||
// --- low-level accessors --- | ||
|
||
// get returns the small and big components of the Int. | ||
// small is defined only if big is nil. | ||
func (i Int) get() (small int64, big *big.Int) { | ||
return i.small_, i.big_ | ||
} | ||
|
||
// Precondition: math.MinInt32 <= x && x <= math.MaxInt32 | ||
func makeSmallInt(x int64) Int { | ||
return Int{intImpl{small_: x}} | ||
} | ||
|
||
// Precondition: x cannot be represented as int32. | ||
func makeBigInt(x *big.Int) Int { | ||
return Int{intImpl{big_: x}} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
//+build linux darwin | ||
//+build amd64 arm64 mips64x ppc64x | ||
|
||
package starlark | ||
|
||
// Optimized Int implementation for 64-bit machines running POSIX (for mmap). | ||
// It reserves a portion of the address space to represent int32 values. | ||
|
||
import ( | ||
"log" | ||
"math" | ||
"math/big" | ||
"unsafe" | ||
|
||
"golang.org/x/sys/unix" | ||
) | ||
|
||
// intImpl represents a union of (int32, *big.Int) in a single pointer, | ||
// so that Int-to-Value conversions need not allocate. | ||
// | ||
// The pointer is either a *big.Int, if the value is big, or a pointer into a | ||
// reserved portion of the address space (smallints), if the value is small. | ||
// | ||
// See int_generic.go for the basic representation concepts. | ||
type intImpl unsafe.Pointer | ||
|
||
// get returns the (small, big) arms of the union. | ||
func (i Int) get() (int64, *big.Int) { | ||
ptr := uintptr(i.impl) | ||
if ptr >= smallints && ptr < smallints+1<<32 { | ||
return math.MinInt32 + int64(ptr-smallints), nil | ||
} | ||
return 0, (*big.Int)(i.impl) | ||
} | ||
|
||
// Precondition: math.MinInt32 <= x && x <= math.MaxInt32 | ||
func makeSmallInt(x int64) Int { | ||
return Int{intImpl(uintptr(x-math.MinInt32) + smallints)} | ||
} | ||
|
||
// Precondition: x cannot be represented as int32. | ||
func makeBigInt(x *big.Int) Int { return Int{intImpl(x)} } | ||
|
||
// smallints is the base address of a 2^32 byte memory region. | ||
// Pointers to addresses in this region represent int32 values. | ||
// We assume smallints is not at the very top of the address space. | ||
var smallints = reserveAddresses(1 << 32) | ||
|
||
func reserveAddresses(len int) uintptr { | ||
b, err := unix.Mmap(-1, 0, len, unix.PROT_READ, unix.MAP_PRIVATE|unix.MAP_ANONYMOUS) | ||
if err != nil { | ||
log.Fatalf("mmap: %v", err) | ||
} | ||
return uintptr(unsafe.Pointer(&b[0])) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters