-
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: improve 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-bit machines, or those running a non-POSIX system, we continue to use the old representation. Change-Id: I2915a8f8abff18ab2eba2891c352700c3ab0d4c4
- Loading branch information
Showing
5 changed files
with
103 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 !amd64,!arm64,!mips64x,!ppc64x | ||
|
||
package starlark | ||
|
||
// generic Int implementation as a union | ||
|
||
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. | ||
// small is sign-extended to 64 bits for ease of subsequent arithmetic. | ||
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,59 @@ | ||
//+build linux darwin | ||
//+build amd64 arm64 mips64x ppc64x | ||
|
||
package starlark | ||
|
||
// This file defines an optimized Int implementation for 64-bit machines | ||
// running POSIX. It reserves a 4GB portion of the address space using | ||
// mmap and represents int32 values as addresses within that range. This | ||
// disambiguates int32 values from *big.Int pointers, letting all Int | ||
// values be represented as an unsafe.Pointer, so that Int-to-Value | ||
// interface conversion need not allocate. | ||
|
||
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