util/interval/generic: stop lying about live memory#116663
util/interval/generic: stop lying about live memory#116663craig[bot] merged 3 commits intocockroachdb:masterfrom
Conversation
|
Thank you for contributing to CockroachDB. Please ensure you have followed the guidelines for creating a PR. Before a member of our team reviews your PR, I have some potential action items for you:
🦉 Hoot! I am a Blathers, a bot for CockroachDB. My owner is dev-inf. |
|
@nvanbenschoten this one is for you |
nvb
left a comment
There was a problem hiding this comment.
Nice! I was recently thinking about this and came to same conclusion about a nullable pointer to a optionally inlined array being the right approach to retain polymorphism while avoiding the cost of interfaces. Thanks for proving it out. Also, thanks for playing with the padding to keep the struct size the same.
Reviewed 5 of 5 files at r1, 10 of 10 files at r2, 10 of 10 files at r3, all commit messages.
Reviewable status:complete! 0 of 0 LGTMs obtained (waiting on @ajwerner)
-- commits line 26 at r2:
s/get reduce/reduce/
pkg/util/interval/generic/internal/interval_btree_tmpl.go line 195 at r1 (raw file):
} } in := (*interiorNode)(unsafe.Pointer(n))
Is it worth avoiding this use of unsafe and instead doing something like:
*n.children = childrenArray{}
*n = node{children: n.children}
nodePool.Put(n)
ajwerner
left a comment
There was a problem hiding this comment.
Reviewable status:
complete! 0 of 0 LGTMs obtained (waiting on @nvanbenschoten)
pkg/util/interval/generic/internal/interval_btree_tmpl.go line 195 at r1 (raw file):
Previously, nvanbenschoten (Nathan VanBenschoten) wrote…
Is it worth avoiding this use of unsafe and instead doing something like:
*n.children = childrenArray{} *n = node{children: n.children} nodePool.Put(n)
I'm happy to do it that way, it's how I wrote it initially. When it was written that way, there was a consistent regression in BTreeDeleteInsertCloneEachTime/count=.*/reset=true, so I changed it to this formulation which does somewhat better. Below is the diff between the current state of the PR (old) and the proposed change to avoid unsafe. Those few nanos for an edge-case benchmark may not be worth it. What would you prefer?
name old time/op new time/op delta
BTreeDeleteInsertCloneEachTime/count=16/reset=true-32 68.3ns ± 5% 70.2ns ± 3% +2.75% (p=0.000 n=40+35)
BTreeDeleteInsertCloneEachTime/count=128/reset=true-32 149ns ± 8% 157ns ±15% +5.36% (p=0.000 n=37+38)
BTreeDeleteInsertCloneEachTime/count=1024/reset=true-32 358ns ± 7% 373ns ± 6% +4.30% (p=0.000 n=39+39)
BTreeDeleteInsertCloneEachTime/count=8192/reset=true-32 454ns ± 2% 466ns ± 5% +2.70% (p=0.000 n=39+39)
BTreeDeleteInsertCloneEachTime/count=65536/reset=true-32 793ns ±29% 791ns ±29% ~ (p=0.914 n=40+39)
774e7b1 to
9829ce5
Compare
|
Thank you for updating your pull request. Before a member of our team reviews your PR, I have some potential action items for you:
🦉 Hoot! I am a Blathers, a bot for CockroachDB. My owner is dev-inf. |
Before this change, all nodes, including leaf nodes had a children array. For leaf nodes, this array was garbage. Having live pointers which contain garbage is bad for tools heap analysis tools like viewcore, and has unclear implications for garbage collection. This commit changes the situation by instead using a pointer for the children array. For interior nodes, this pointer is non-nil, and points to the next word in the interiorNode struct. For leaf nodes, the pointer is nil. This has a downside of using an extra word for all nodes. A subsequent commit claws back this word. Epic: none Release note: None
By taking the two fields of keyBound and putting them into the node directly, we can exploit some available padding for the `inc` bool and reduce the node size by a word. Epic: none Release note: None
9829ce5 to
cfe6684
Compare
ajwerner
left a comment
There was a problem hiding this comment.
TFTR!
Reviewable status:
complete! 0 of 0 LGTMs obtained (waiting on @nvanbenschoten)
Previously, nvanbenschoten (Nathan VanBenschoten) wrote…
s/get reduce/reduce/
Done.
pkg/util/interval/generic/internal/interval_btree_tmpl.go line 195 at r1 (raw file):
Previously, ajwerner wrote…
I'm happy to do it that way, it's how I wrote it initially. When it was written that way, there was a consistent regression in
BTreeDeleteInsertCloneEachTime/count=.*/reset=true, so I changed it to this formulation which does somewhat better. Below is the diff between the current state of the PR (old) and the proposed change to avoid unsafe. Those few nanos for an edge-case benchmark may not be worth it. What would you prefer?name old time/op new time/op delta BTreeDeleteInsertCloneEachTime/count=16/reset=true-32 68.3ns ± 5% 70.2ns ± 3% +2.75% (p=0.000 n=40+35) BTreeDeleteInsertCloneEachTime/count=128/reset=true-32 149ns ± 8% 157ns ±15% +5.36% (p=0.000 n=37+38) BTreeDeleteInsertCloneEachTime/count=1024/reset=true-32 358ns ± 7% 373ns ± 6% +4.30% (p=0.000 n=39+39) BTreeDeleteInsertCloneEachTime/count=8192/reset=true-32 454ns ± 2% 466ns ± 5% +2.70% (p=0.000 n=39+39) BTreeDeleteInsertCloneEachTime/count=65536/reset=true-32 793ns ±29% 791ns ±29% ~ (p=0.914 n=40+39)
I found clearing the children after the node does as well as the code that cleared the whole interiorNode. I think this is good enough.
nvb
left a comment
There was a problem hiding this comment.
Reviewed 10 of 10 files at r6, all commit messages.
Reviewable status:complete! 1 of 0 LGTMs obtained (waiting on @ajwerner)
pkg/util/interval/generic/internal/interval_btree_tmpl.go line 195 at r1 (raw file):
Previously, ajwerner wrote…
I found clearing the children after the node does as well as the code that cleared the whole interiorNode. I think this is good enough.
Strange. Any idea why that performed better?
pkg/util/interval/generic/internal/interval_btree_tmpl.go line 113 at r6 (raw file):
items [maxItems]T children *childrenArray
Mind giving this a comment explaining that it's nil for leaf nodes and non-nil for interior nodes?
If the children array pointer is set, it's a leaf node. No need for a separate bool. Epic: none Release note: None
cfe6684 to
8d33a8c
Compare
ajwerner
left a comment
There was a problem hiding this comment.
Reviewable status:
complete! 0 of 0 LGTMs obtained (and 1 stale) (waiting on @nvanbenschoten)
pkg/util/interval/generic/internal/interval_btree_tmpl.go line 195 at r1 (raw file):
Previously, nvanbenschoten (Nathan VanBenschoten) wrote…
Strange. Any idea why that performed better?
I guess we're now zeroing the memory more sequentially? Like when you zero the child array first you're zeroing the end of the structure and then coming back and zeroing the front. I suppose when you zero the end of the node you could be pulling in part of the cache line that has the children array?
pkg/util/interval/generic/internal/interval_btree_tmpl.go line 113 at r6 (raw file):
Previously, nvanbenschoten (Nathan VanBenschoten) wrote…
Mind giving this a comment explaining that it's nil for leaf nodes and non-nil for interior nodes?
Done.
|
bors r=nvanbenschoten |
|
Build succeeded: |
In cockroachdb#110516, the code started used a generated btree. Unfortunately, the code did not adopt bazel code generation, so missed a regeneration when the changes in cockroachdb#116663 merged. Also, it seems, the change rotted such that regenerating didn't work because the entry was renamed from `frontierEntry` to `btreeFrontierEntry` without updating the go:generate comment. I imagine somebody did a manual rename. This fixes the problem by adopting bazel generation. Epic: None Release note: None
99884: kvserver: remove unused snapshot fields r=kvoli a=andrewbaptist This commit removes a number of unused fields in snapshot messages. Release note: None Epic: none 118490: util/span: use bazel for codegen, regenerate r=miretskiy a=ajwerner In #110516, the code started used a generated btree. Unfortunately, the code did not adopt bazel code generation, so missed a regeneration when the changes in #116663 merged. Also, it seems, the change rotted such that regenerating didn't work because the entry was renamed from `frontierEntry` to `btreeFrontierEntry` without updating the go:generate comment. I imagine somebody did a manual rename. This fixes the problem by adopting bazel generation. Epic: None Release note: None Co-authored-by: Andrew Baptist <baptist@cockroachlabs.com> Co-authored-by: Andrew Werner <awerner32@gmail.com>
See individual commits. The basic problem this PR solves is that in the previous formulation, we used unsafe code to cast pointers to
nodes to point beyond their allocation. It's not clear that this is sound with regards to GC, and it's certain that this break tools like viewcore. The argument for this hack was performance. This change removes unsafe code and achieves effectively the same performance.