Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove recomputeHeapList backing map; just use the linked list for storage #21

Merged
merged 3 commits into from
Apr 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 12 additions & 4 deletions bench_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -350,12 +350,16 @@ func benchmarkSize(size int, b *testing.B) {
if err != nil {
b.Fatal(err)
}
graph.SetStale(nodes[rand.Intn(size)])
for x := 0; x < size>>1; x++ {
graph.SetStale(nodes[rand.Intn(size)])
}
err = graph.Stabilize(ctx)
if err != nil {
b.Fatal(err)
}
graph.SetStale(nodes[rand.Intn(size)])
for x := 0; x < size>>1; x++ {
graph.SetStale(nodes[rand.Intn(size)])
}
err = graph.Stabilize(ctx)
if err != nil {
b.Fatal(err)
Expand All @@ -373,12 +377,16 @@ func benchmarkParallelSize(size int, b *testing.B) {
if err != nil {
b.Fatal(err)
}
graph.SetStale(nodes[rand.Intn(size)])
for x := 0; x < size>>1; x++ {
graph.SetStale(nodes[rand.Intn(size)])
}
err = graph.ParallelStabilize(ctx)
if err != nil {
b.Fatal(err)
}
graph.SetStale(nodes[rand.Intn(size)])
for x := 0; x < size>>1; x++ {
graph.SetStale(nodes[rand.Intn(size)])
}
err = graph.ParallelStabilize(ctx)
if err != nil {
b.Fatal(err)
Expand Down
6 changes: 4 additions & 2 deletions expert_graph.go
Original file line number Diff line number Diff line change
Expand Up @@ -109,8 +109,10 @@ func (eg *expertGraph) RecomputeHeapIDs() []Identifier {
output := make([]Identifier, 0, eg.graph.recomputeHeap.numItems)
for _, height := range eg.graph.recomputeHeap.heights {
if height != nil {
for key := range height.items {
output = append(output, key)
cursor := height.head
for cursor != nil {
output = append(output, cursor.Node().id)
cursor = cursor.Node().nextInRecomputeHeap
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion parallel_stabilize.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ func (graph *Graph) parallelStabilize(ctx context.Context) (err error) {

var iter recomputeHeapListIter
for graph.recomputeHeap.len() > 0 {
graph.recomputeHeap.removeMinHeightIter(&iter)
graph.recomputeHeap.setIterToMinHeight(&iter)
err = parallelBatch[INode](ctx, parallelRecomputeNode, iter.Next, graph.parallelism)
if err != nil {
break
Expand Down
6 changes: 3 additions & 3 deletions recompute_heap.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ func (i *recomputeHeapListIter) Next() (INode, bool) {
return prev, true
}

func (rh *recomputeHeap) removeMinHeightIter(iter *recomputeHeapListIter) {
func (rh *recomputeHeap) setIterToMinHeight(iter *recomputeHeapListIter) {
rh.mu.Lock()
defer rh.mu.Unlock()

Expand All @@ -102,8 +102,8 @@ func (rh *recomputeHeap) removeMinHeightIter(iter *recomputeHeapListIter) {
iter.cursor = heightBlock.head
heightBlock.head = nil
heightBlock.tail = nil
rh.numItems = rh.numItems - len(heightBlock.items)
clear(heightBlock.items)
rh.numItems = rh.numItems - heightBlock.len()
heightBlock.count = 0
rh.minHeight = rh.nextMinHeightUnsafe()
}

Expand Down
78 changes: 44 additions & 34 deletions recompute_heap_list.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,40 +4,27 @@ package incr
// as a ordered recomputeHeapList as well as a constant time
// map using a similar technique to high throughput LRU queues.
type recomputeHeapList struct {
// head is the "first" element in the list
head INode
// tail is the "last" element in the list
tail INode
// items is a map between the key and the actual list item(s)
items map[Identifier]INode
head INode
tail INode
count int
}

// func (l *recomputeHeapList) isEmpty() bool {
// return len(l.items) == 0
// }

func (l *recomputeHeapList) len() int {
if l == nil {
return 0
}
return len(l.items)
return l.count
}

func (l *recomputeHeapList) push(v INode) {
if l.items == nil {
l.items = make(map[Identifier]INode)
}

l.count = l.count + 1
v.Node().nextInRecomputeHeap = nil
v.Node().previousInRecomputeHeap = nil

l.items[v.Node().id] = v
if l.head == nil {
l.head = v
l.tail = v
return
}

l.tail.Node().nextInRecomputeHeap = v
v.Node().previousInRecomputeHeap = l.tail
l.tail = v
Expand All @@ -51,7 +38,7 @@ func (l *recomputeHeapList) pop() (k Identifier, v INode, ok bool) {
k = l.head.Node().id
v = l.head
ok = true
delete(l.items, k)
l.count = l.count - 1

if l.head == l.tail {
l.head = nil
Expand All @@ -64,50 +51,73 @@ func (l *recomputeHeapList) pop() (k Identifier, v INode, ok bool) {
next := l.head.Node().nextInRecomputeHeap
next.Node().previousInRecomputeHeap = nil
l.head = next

v.Node().nextInRecomputeHeap = nil
v.Node().previousInRecomputeHeap = nil
return
}

func (l *recomputeHeapList) consume(fn func(Identifier, INode)) {
if l.items == nil {
return
}
for key, value := range l.items {
value.Node().nextInRecomputeHeap = nil
value.Node().previousInRecomputeHeap = nil
fn(key, value)
func (l *recomputeHeapList) consume(fn func(INode)) {
cursor := l.head
var next INode
for cursor != nil {
next = cursor.Node().nextInRecomputeHeap
fn(cursor)
cursor.Node().nextInRecomputeHeap = nil
cursor.Node().previousInRecomputeHeap = nil
cursor = next
}
l.head = nil
l.tail = nil
clear(l.items)
l.count = 0
}

func (l *recomputeHeapList) has(k Identifier) (ok bool) {
if l == nil || l.items == nil {
if l == nil || l.head == nil {
return
}
cursor := l.head
for cursor != nil {
if cursor.Node().id == k {
ok = true
return
}
cursor = cursor.Node().nextInRecomputeHeap
}
return
}

func (l *recomputeHeapList) find(k Identifier) (n INode, ok bool) {
if l == nil || l.head == nil {
return
}
_, ok = l.items[k]
cursor := l.head
for cursor != nil {
if cursor.Node().id == k {
n = cursor
ok = true
return
}
cursor = cursor.Node().nextInRecomputeHeap
}
return
}

func (l *recomputeHeapList) remove(k Identifier) (ok bool) {
if len(l.items) == 0 {
if l.head == nil {
return
}

var node INode
node, ok = l.items[k]
node, ok = l.find(k)
if !ok {
return
}
l.count = l.count - 1
if l.head == node {
l.removeHeadItem()
} else {
l.removeLinkedItem(node)
}
delete(l.items, k)
return
}

Expand Down
4 changes: 2 additions & 2 deletions recompute_heap_list_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -599,8 +599,8 @@ func Test_recomputeHeapList_consume(t *testing.T) {
var seenIDs []Identifier
var seen []INode

q.consume(func(k Identifier, v INode) {
seenIDs = append(seenIDs, k)
q.consume(func(v INode) {
seenIDs = append(seenIDs, v.Node().id)
seen = append(seen, v)
})

Expand Down
10 changes: 5 additions & 5 deletions recompute_heap_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ func iterToArray[A any](fn func() (A, bool)) (output []A) {
return output
}

func Test_recomputeHeap_removeMinHeight(t *testing.T) {
func Test_recomputeHeap_setIterToMinHeight(t *testing.T) {
g := New()

rh := newRecomputeHeap(10)
Expand Down Expand Up @@ -106,7 +106,7 @@ func Test_recomputeHeap_removeMinHeight(t *testing.T) {

var iter recomputeHeapListIter
var iterValues []INode
rh.removeMinHeightIter(&iter)
rh.setIterToMinHeight(&iter)
iterValues = iterToArray(iter.Next)

testutil.Nil(t, rh.sanityCheck())
Expand All @@ -125,7 +125,7 @@ func Test_recomputeHeap_removeMinHeight(t *testing.T) {
testutil.Equal(t, 1, rh.minHeight)
testutil.Equal(t, 5, rh.maxHeight)

rh.removeMinHeightIter(&iter)
rh.setIterToMinHeight(&iter)
iterValues = iterToArray(iter.Next)
testutil.Nil(t, rh.sanityCheck())
testutil.Equal(t, 5, rh.len())
Expand All @@ -140,7 +140,7 @@ func Test_recomputeHeap_removeMinHeight(t *testing.T) {
testutil.Nil(t, n.Node().previousInRecomputeHeap)
}

rh.removeMinHeightIter(&iter)
rh.setIterToMinHeight(&iter)
iterValues = iterToArray(iter.Next)
testutil.Nil(t, rh.sanityCheck())
testutil.Equal(t, 0, rh.len())
Expand All @@ -166,7 +166,7 @@ func Test_recomputeHeap_removeMinHeight(t *testing.T) {
testutil.Equal(t, 5, rh.minHeight)
testutil.Equal(t, 5, rh.maxHeight)

rh.removeMinHeightIter(&iter)
rh.setIterToMinHeight(&iter)
iterValues = iterToArray(iter.Next)
testutil.Nil(t, rh.sanityCheck())
testutil.Equal(t, 0, rh.len())
Expand Down
4 changes: 2 additions & 2 deletions testutil/helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ func NoError(t *testing.T, err error, message ...any) {
func Equal(t *testing.T, expected, actual any, message ...any) {
t.Helper()
if !areEqual(expected, actual) {
fatalf(t, "expected %v to equal %v", []any{actual, expected}, message)
fatalf(t, "equal assertion failure; actual=%v expected=%v", []any{actual, expected}, message)
}
}

Expand All @@ -43,7 +43,7 @@ func Equal(t *testing.T, expected, actual any, message ...any) {
func NotEqual(t *testing.T, expected, actual any, message ...any) {
t.Helper()
if areEqual(expected, actual) {
fatalf(t, "expected %v not to equal %v", []any{actual, expected}, message)
fatalf(t, "not equal assertion failure; actual=%v expected=%v", []any{actual, expected}, message)
}
}

Expand Down