Skip to content

Commit 47868f5

Browse files
Merge pull request #81 from Workiva/hilbert_tree
Hilbert tree
2 parents e419755 + dc2dd5b commit 47868f5

File tree

10 files changed

+1520
-3
lines changed

10 files changed

+1520
-3
lines changed

augmentedtree/multidimensional_test.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -194,7 +194,7 @@ func TestAddLargeNumbersMultiDimensions(t *testing.T) {
194194
}
195195

196196
func BenchmarkAddItemsMultiDimensions(b *testing.B) {
197-
numItems := int64(1000)
197+
numItems := int64(b.N)
198198
intervals := make(Intervals, 0, numItems)
199199

200200
for i := int64(0); i < numItems; i++ {
@@ -204,11 +204,11 @@ func BenchmarkAddItemsMultiDimensions(b *testing.B) {
204204
intervals = append(intervals, iv)
205205
}
206206

207+
it := newTree(2)
207208
b.ResetTimer()
208209

209210
for i := 0; i < b.N; i++ {
210-
it := newTree(2)
211-
it.Add(intervals...)
211+
it.Add(intervals[int64(i)%numItems])
212212
}
213213
}
214214

rtree/hilbert/action.go

Lines changed: 187 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,187 @@
1+
/*
2+
Copyright 2014 Workiva, LLC
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package hilbert
18+
19+
import (
20+
"runtime"
21+
"sync"
22+
"sync/atomic"
23+
24+
"github.com/Workiva/go-datastructures/rtree"
25+
)
26+
27+
type actions []action
28+
29+
type action interface {
30+
operation() operation
31+
keys() hilberts
32+
rects() []*hilbertBundle
33+
complete()
34+
addNode(int64, *node)
35+
nodes() []*node
36+
}
37+
38+
type getAction struct {
39+
result rtree.Rectangles
40+
completer *sync.WaitGroup
41+
lookup *rectangle
42+
}
43+
44+
func (ga *getAction) complete() {
45+
ga.completer.Done()
46+
}
47+
48+
func (ga *getAction) operation() operation {
49+
return get
50+
}
51+
52+
func (ga *getAction) keys() hilberts {
53+
return nil
54+
}
55+
56+
func (ga *getAction) addNode(i int64, n *node) {
57+
return // not necessary for gets
58+
}
59+
60+
func (ga *getAction) nodes() []*node {
61+
return nil
62+
}
63+
64+
func (ga *getAction) rects() []*hilbertBundle {
65+
return nil
66+
}
67+
68+
func newGetAction(rect rtree.Rectangle) *getAction {
69+
r := newRectangeFromRect(rect)
70+
ga := &getAction{
71+
completer: new(sync.WaitGroup),
72+
lookup: r,
73+
}
74+
ga.completer.Add(1)
75+
return ga
76+
}
77+
78+
type insertAction struct {
79+
rs []*hilbertBundle
80+
completer *sync.WaitGroup
81+
ns []*node
82+
}
83+
84+
func (ia *insertAction) complete() {
85+
ia.completer.Done()
86+
}
87+
88+
func (ia *insertAction) operation() operation {
89+
return add
90+
}
91+
92+
func (ia *insertAction) keys() hilberts {
93+
return nil
94+
}
95+
96+
func (ia *insertAction) addNode(i int64, n *node) {
97+
ia.ns[i] = n
98+
}
99+
100+
func (ia *insertAction) nodes() []*node {
101+
return ia.ns
102+
}
103+
104+
func (ia *insertAction) rects() []*hilbertBundle {
105+
return ia.rs
106+
}
107+
108+
func newInsertAction(rects rtree.Rectangles) *insertAction {
109+
ia := &insertAction{
110+
rs: bundlesFromRects(rects...),
111+
completer: new(sync.WaitGroup),
112+
ns: make([]*node, len(rects)),
113+
}
114+
ia.completer.Add(1)
115+
return ia
116+
}
117+
118+
type removeAction struct {
119+
*insertAction
120+
}
121+
122+
func (ra *removeAction) operation() operation {
123+
return remove
124+
}
125+
126+
func newRemoveAction(rects rtree.Rectangles) *removeAction {
127+
return &removeAction{
128+
newInsertAction(rects),
129+
}
130+
}
131+
132+
func minUint64(choices ...uint64) uint64 {
133+
min := choices[0]
134+
for i := 1; i < len(choices); i++ {
135+
if choices[i] < min {
136+
min = choices[i]
137+
}
138+
}
139+
140+
return min
141+
}
142+
143+
type interfaces []interface{}
144+
145+
func executeInterfacesInParallel(ifs interfaces, fn func(interface{})) {
146+
if len(ifs) == 0 {
147+
return
148+
}
149+
150+
done := int64(-1)
151+
numCPU := uint64(runtime.NumCPU())
152+
if numCPU > 1 {
153+
numCPU--
154+
}
155+
156+
numCPU = minUint64(numCPU, uint64(len(ifs)))
157+
158+
var wg sync.WaitGroup
159+
wg.Add(int(numCPU))
160+
161+
for i := uint64(0); i < numCPU; i++ {
162+
go func() {
163+
defer wg.Done()
164+
165+
for {
166+
i := atomic.AddInt64(&done, 1)
167+
if i >= int64(len(ifs)) {
168+
return
169+
}
170+
171+
fn(ifs[i])
172+
}
173+
}()
174+
}
175+
176+
wg.Wait()
177+
}
178+
179+
func executeInterfacesInSerial(ifs interfaces, fn func(interface{})) {
180+
if len(ifs) == 0 {
181+
return
182+
}
183+
184+
for _, ifc := range ifs {
185+
fn(ifc)
186+
}
187+
}

rtree/hilbert/cpu.prof

12 KB
Binary file not shown.

rtree/hilbert/hilbert.go

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
/*
2+
Copyright 2014 Workiva, LLC
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package hilbert
18+
19+
import (
20+
"runtime"
21+
"sync"
22+
23+
h "github.com/Workiva/go-datastructures/numerics/hilbert"
24+
"github.com/Workiva/go-datastructures/rtree"
25+
)
26+
27+
func getCenter(rect rtree.Rectangle) (int32, int32) {
28+
xlow, ylow := rect.LowerLeft()
29+
xhigh, yhigh := rect.UpperRight()
30+
31+
return (xhigh + xlow) / 2, (yhigh + ylow) / 2
32+
}
33+
34+
type hilbertBundle struct {
35+
hilbert hilbert
36+
rect rtree.Rectangle
37+
}
38+
39+
func bundlesFromRects(rects ...rtree.Rectangle) []*hilbertBundle {
40+
chunks := chunkRectangles(rects, int64(runtime.NumCPU()))
41+
bundleChunks := make([][]*hilbertBundle, len(chunks))
42+
var wg sync.WaitGroup
43+
wg.Add(len(chunks))
44+
45+
for i := 0; i < runtime.NumCPU(); i++ {
46+
if len(chunks[i]) == 0 {
47+
bundleChunks[i] = []*hilbertBundle{}
48+
wg.Done()
49+
continue
50+
}
51+
go func(i int) {
52+
bundles := make([]*hilbertBundle, 0, len(chunks[i]))
53+
for _, r := range chunks[i] {
54+
h := h.Encode(getCenter(r))
55+
bundles = append(bundles, &hilbertBundle{hilbert(h), r})
56+
}
57+
bundleChunks[i] = bundles
58+
wg.Done()
59+
}(i)
60+
}
61+
62+
wg.Wait()
63+
64+
bundles := make([]*hilbertBundle, 0, len(rects))
65+
for _, bc := range bundleChunks {
66+
bundles = append(bundles, bc...)
67+
}
68+
69+
return bundles
70+
}
71+
72+
// chunkRectangles takes a slice of rtree.Rectangle values and chunks it into `numParts` subslices.
73+
func chunkRectangles(slice rtree.Rectangles, numParts int64) []rtree.Rectangles {
74+
parts := make([]rtree.Rectangles, numParts)
75+
for i := int64(0); i < numParts; i++ {
76+
parts[i] = slice[i*int64(len(slice))/numParts : (i+1)*int64(len(slice))/numParts]
77+
}
78+
return parts
79+
}

rtree/hilbert/mock_test.go

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
/*
2+
Copyright 2014 Workiva, LLC
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package hilbert
18+
19+
type mockRectangle struct {
20+
xlow, ylow, xhigh, yhigh int32
21+
}
22+
23+
func (mr *mockRectangle) LowerLeft() (int32, int32) {
24+
return mr.xlow, mr.ylow
25+
}
26+
27+
func (mr *mockRectangle) UpperRight() (int32, int32) {
28+
return mr.xhigh, mr.yhigh
29+
}
30+
31+
func newMockRectangle(xlow, ylow, xhigh, yhigh int32) *mockRectangle {
32+
return &mockRectangle{
33+
xlow: xlow,
34+
ylow: ylow,
35+
xhigh: xhigh,
36+
yhigh: yhigh,
37+
}
38+
}

0 commit comments

Comments
 (0)