memory optimizations

increased b-tree degrees from 16 to 48
increased r-tree degress from 8 to 15
changed r-tree from float64 to float32
This commit is contained in:
Josh Baker 2016-10-24 15:35:47 -07:00
parent 6fa5f836d7
commit bb2bb451b2
4 changed files with 88 additions and 39 deletions

View File

@ -64,8 +64,8 @@ var counter uint64
func New() *Collection { func New() *Collection {
col := &Collection{ col := &Collection{
index: index.New(), index: index.New(),
items: btree.New(16, idOrdered), items: btree.New(48, idOrdered),
values: btree.New(16, valueOrdered), values: btree.New(48, valueOrdered),
fieldMap: make(map[string]int), fieldMap: make(map[string]int),
} }
return col return col
@ -76,6 +76,11 @@ func (c *Collection) Count() int {
return c.objects + c.nobjects return c.objects + c.nobjects
} }
// StringCount returns the number of string values.
func (c *Collection) StringCount() int {
return c.nobjects
}
// PointCount returns the number of points (lat/lon coordinates) in collection. // PointCount returns the number of points (lat/lon coordinates) in collection.
func (c *Collection) PointCount() int { func (c *Collection) PointCount() int {
return c.points return c.points

View File

@ -33,10 +33,10 @@ func (c *Controller) cmdStats(msg *server.Message) (res string, err error) {
col := c.getCol(key) col := c.getCol(key)
if col != nil { if col != nil {
m := make(map[string]interface{}) m := make(map[string]interface{})
points := col.PointCount() m["num_points"] = col.PointCount()
m["num_points"] = points
m["in_memory_size"] = col.TotalWeight() m["in_memory_size"] = col.TotalWeight()
m["num_objects"] = col.Count() m["num_objects"] = col.Count()
m["num_strings"] = col.StringCount()
switch msg.OutputType { switch msg.OutputType {
case server.JSON: case server.JSON:
ms = append(ms, m) ms = append(ms, m)
@ -92,14 +92,17 @@ func (c *Controller) cmdServer(msg *server.Message) (res string, err error) {
m["in_memory_size"] = sz m["in_memory_size"] = sz
points := 0 points := 0
objects := 0 objects := 0
strings := 0
c.cols.Ascend(func(item btree.Item) bool { c.cols.Ascend(func(item btree.Item) bool {
col := item.(*collectionT).Collection col := item.(*collectionT).Collection
points += col.PointCount() points += col.PointCount()
objects += col.Count() objects += col.Count()
strings += col.StringCount()
return true return true
}) })
m["num_points"] = points m["num_points"] = points
m["num_objects"] = objects m["num_objects"] = objects
m["num_strings"] = strings
var mem runtime.MemStats var mem runtime.MemStats
runtime.ReadMemStats(&mem) runtime.ReadMemStats(&mem)
avgsz := 0 avgsz := 0

View File

@ -67,6 +67,7 @@ func (tr *RTree) Bounds() (minX, minY, minZ, maxX, maxY, maxZ float64) {
} }
} }
} }
minX, minY, minZ, maxX, maxY, maxZ = rect.min[0], rect.min[1], rect.min[2], rect.max[0], rect.max[1], rect.max[2] minX, minY, minZ = float64(rect.min[0]), float64(rect.min[1]), float64(rect.min[2])
maxX, maxY, maxZ = float64(rect.max[0]), float64(rect.max[1]), float64(rect.max[2])
return return
} }

View File

@ -2,13 +2,17 @@ package rtree
import "math" import "math"
func d3fmin(a, b float64) float64 { type float float32
const d3roundValues = true // only set to true when using 32-bit floats
func d3fmin(a, b float) float {
if a < b { if a < b {
return a return a
} }
return b return b
} }
func d3fmax(a, b float64) float64 { func d3fmax(a, b float) float {
if a > b { if a > b {
return a return a
} }
@ -17,7 +21,7 @@ func d3fmax(a, b float64) float64 {
const ( const (
d3numDims = 3 d3numDims = 3
d3maxNodes = 8 d3maxNodes = 13
d3minNodes = d3maxNodes / 2 d3minNodes = d3maxNodes / 2
d3useSphericalVolume = true // Better split classification, may be slower on some systems d3useSphericalVolume = true // Better split classification, may be slower on some systems
) )
@ -38,8 +42,8 @@ type d3RTree struct {
/// Minimal bounding rectangle (n-dimensional) /// Minimal bounding rectangle (n-dimensional)
type d3rectT struct { type d3rectT struct {
min [d3numDims]float64 ///< Min dimensions of bounding box min [d3numDims]float ///< Min dimensions of bounding box
max [d3numDims]float64 ///< Max dimensions of bounding box max [d3numDims]float ///< Max dimensions of bounding box
} }
/// May be data or may be another subtree /// May be data or may be another subtree
@ -65,6 +69,41 @@ func (node *d3nodeT) isLeaf() bool {
return (node.level == 0) // A leaf, contains data return (node.level == 0) // A leaf, contains data
} }
// Rounding constants for float32 -> float64 conversion.
const d3RNDTOWARDS = (1.0 - 1.0/8388608.0) // Round towards zero
const d3RNDAWAY = (1.0 + 1.0/8388608.0) // Round away from zero
// Convert an sqlite3_value into an RtreeValue (presumably a float)
// while taking care to round toward negative or positive, respectively.
func d3rtreeValueDown(d float64) float {
if !d3roundValues {
return float(d)
}
f := float(d)
if float64(f) > d {
if d < 0 {
f = float(d * d3RNDAWAY)
} else {
f = float(d * d3RNDTOWARDS)
}
}
return f
}
func d3rtreeValueUp(d float64) float {
if !d3roundValues {
return float(d)
}
f := float(d)
if float64(f) < d {
if d < 0 {
f = float(d * d3RNDTOWARDS)
} else {
f = float(d * d3RNDAWAY)
}
}
return f
}
/// A link list of nodes for reinsertion after a delete operation /// A link list of nodes for reinsertion after a delete operation
type d3listNodeT struct { type d3listNodeT struct {
next *d3listNodeT ///< Next in list next *d3listNodeT ///< Next in list
@ -80,12 +119,12 @@ type d3partitionVarsT struct {
minFill int minFill int
count [2]int count [2]int
cover [2]d3rectT cover [2]d3rectT
area [2]float64 area [2]float
branchBuf [d3maxNodes + 1]d3branchT branchBuf [d3maxNodes + 1]d3branchT
branchCount int branchCount int
coverSplit d3rectT coverSplit d3rectT
coverSplitArea float64 coverSplitArea float
} }
func d3New() *d3RTree { func d3New() *d3RTree {
@ -104,8 +143,8 @@ func (tr *d3RTree) Insert(min, max [d3numDims]float64, dataId interface{}) {
var branch d3branchT var branch d3branchT
branch.data = dataId branch.data = dataId
for axis := 0; axis < d3numDims; axis++ { for axis := 0; axis < d3numDims; axis++ {
branch.rect.min[axis] = min[axis] branch.rect.min[axis] = d3rtreeValueDown(min[axis])
branch.rect.max[axis] = max[axis] branch.rect.max[axis] = d3rtreeValueUp(max[axis])
} }
d3insertRect(&branch, &tr.root, 0) d3insertRect(&branch, &tr.root, 0)
} }
@ -117,8 +156,8 @@ func (tr *d3RTree) Insert(min, max [d3numDims]float64, dataId interface{}) {
func (tr *d3RTree) Remove(min, max [d3numDims]float64, dataId interface{}) { func (tr *d3RTree) Remove(min, max [d3numDims]float64, dataId interface{}) {
var rect d3rectT var rect d3rectT
for axis := 0; axis < d3numDims; axis++ { for axis := 0; axis < d3numDims; axis++ {
rect.min[axis] = min[axis] rect.min[axis] = d3rtreeValueDown(min[axis])
rect.max[axis] = max[axis] rect.max[axis] = d3rtreeValueUp(max[axis])
} }
d3removeRect(&rect, dataId, &tr.root) d3removeRect(&rect, dataId, &tr.root)
} }
@ -133,8 +172,8 @@ func (tr *d3RTree) Remove(min, max [d3numDims]float64, dataId interface{}) {
func (tr *d3RTree) Search(min, max [d3numDims]float64, resultCallback func(data interface{}) bool) int { func (tr *d3RTree) Search(min, max [d3numDims]float64, resultCallback func(data interface{}) bool) int {
var rect d3rectT var rect d3rectT
for axis := 0; axis < d3numDims; axis++ { for axis := 0; axis < d3numDims; axis++ {
rect.min[axis] = min[axis] rect.min[axis] = d3rtreeValueDown(min[axis])
rect.max[axis] = max[axis] rect.max[axis] = d3rtreeValueUp(max[axis])
} }
foundCount, _ := d3search(tr.root, rect, 0, resultCallback) foundCount, _ := d3search(tr.root, rect, 0, resultCallback)
return foundCount return foundCount
@ -287,10 +326,10 @@ func d3disconnectBranch(node *d3nodeT, index int) {
// the best resolution when searching. // the best resolution when searching.
func d3pickBranch(rect *d3rectT, node *d3nodeT) int { func d3pickBranch(rect *d3rectT, node *d3nodeT) int {
var firstTime bool = true var firstTime bool = true
var increase float64 var increase float
var bestIncr float64 = -1 var bestIncr float = -1
var area float64 var area float
var bestArea float64 var bestArea float
var best int var best int
var tempRect d3rectT var tempRect d3rectT
@ -350,8 +389,8 @@ func d3splitNode(node *d3nodeT, branch *d3branchT, newNode **d3nodeT) {
} }
// Calculate the n-dimensional volume of a rectangle // Calculate the n-dimensional volume of a rectangle
func d3rectVolume(rect *d3rectT) float64 { func d3rectVolume(rect *d3rectT) float {
var volume float64 = 1 var volume float = 1
for index := 0; index < d3numDims; index++ { for index := 0; index < d3numDims; index++ {
volume *= rect.max[index] - rect.min[index] volume *= rect.max[index] - rect.min[index]
} }
@ -364,30 +403,31 @@ func d3rectSphericalVolume(rect *d3rectT) float64 {
var radius float64 var radius float64
for index := 0; index < d3numDims; index++ { for index := 0; index < d3numDims; index++ {
halfExtent := (rect.max[index] - rect.min[index]) * 0.5 halfExtent := float64(rect.max[index]-rect.min[index]) * 0.5
sumOfSquares += halfExtent * halfExtent sumOfSquares += halfExtent * halfExtent
} }
radius = math.Sqrt(sumOfSquares) radius = math.Sqrt(sumOfSquares)
// Pow maybe slow, so test for common dims just use x*x, x*x*x. // Pow maybe slow, so test for common dims just use x*x, x*x*x.
if d3numDims == 5 { switch d3numDims {
return (radius * radius * radius * radius * radius * d3unitSphereVolume) default:
} else if d3numDims == 4 {
return (radius * radius * radius * radius * d3unitSphereVolume)
} else if d3numDims == 3 {
return (radius * radius * radius * d3unitSphereVolume)
} else if d3numDims == 2 {
return (radius * radius * d3unitSphereVolume)
} else {
return (math.Pow(radius, d3numDims) * d3unitSphereVolume) return (math.Pow(radius, d3numDims) * d3unitSphereVolume)
case 2:
return (radius * radius * d3unitSphereVolume)
case 3:
return (radius * radius * radius * d3unitSphereVolume)
case 4:
return (radius * radius * radius * radius * d3unitSphereVolume)
case 5:
return (radius * radius * radius * radius * radius * d3unitSphereVolume)
} }
} }
// Use one of the methods to calculate retangle volume // Use one of the methods to calculate retangle volume
func d3calcRectVolume(rect *d3rectT) float64 { func d3calcRectVolume(rect *d3rectT) float {
if d3useSphericalVolume { if d3useSphericalVolume {
return d3rectSphericalVolume(rect) // Slower but helps certain merge cases return float(d3rectSphericalVolume(rect)) // Slower but helps certain merge cases
} else { // RTREE_USE_SPHERICAL_VOLUME } else { // RTREE_USE_SPHERICAL_VOLUME
return d3rectVolume(rect) // Faster but can cause poor merges return d3rectVolume(rect) // Faster but can cause poor merges
} // RTREE_USE_SPHERICAL_VOLUME } // RTREE_USE_SPHERICAL_VOLUME
@ -422,7 +462,7 @@ func d3getBranches(node *d3nodeT, branch *d3branchT, parVars *d3partitionVarsT)
// fill requirement) then other group gets the rest. // fill requirement) then other group gets the rest.
// These last are the ones that can go in either group most easily. // These last are the ones that can go in either group most easily.
func d3choosePartition(parVars *d3partitionVarsT, minFill int) { func d3choosePartition(parVars *d3partitionVarsT, minFill int) {
var biggestDiff float64 var biggestDiff float
var group, chosen, betterGroup int var group, chosen, betterGroup int
d3initParVars(parVars, parVars.branchCount, minFill) d3initParVars(parVars, parVars.branchCount, minFill)
@ -501,8 +541,8 @@ func d3initParVars(parVars *d3partitionVarsT, maxRects, minFill int) {
func d3pickSeeds(parVars *d3partitionVarsT) { func d3pickSeeds(parVars *d3partitionVarsT) {
var seed0, seed1 int var seed0, seed1 int
var worst, waste float64 var worst, waste float
var area [d3maxNodes + 1]float64 var area [d3maxNodes + 1]float
for index := 0; index < parVars.total; index++ { for index := 0; index < parVars.total; index++ {
area[index] = d3calcRectVolume(&parVars.branchBuf[index].rect) area[index] = d3calcRectVolume(&parVars.branchBuf[index].rect)