mirror of https://github.com/tidwall/tile38.git
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:
parent
6fa5f836d7
commit
bb2bb451b2
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
|
|
Loading…
Reference in New Issue