提交 b1bdb3f4 创建 作者: Austin Clements's avatar Austin Clements

Simpler, iterative, and bounded radix_iterator

Previously, the radix iterator unconditionally found the next non-null leaf node, even if it fell outside of the range the iterator was derived from. This was obviously an efficiency issue, but it also introduced unnecessary sharing because of the reads outside of the iterator's range. Now it takes an explicit upper bound and will stop resolving elements when it reaches that bound. The end() iterator is now naturally represented in terms of this bound, rather than in terms of a special-case key. For asharing vm, this eliminates all violations of the commutativity rule. Since I couldn't figure out how to nicely work this bound into the existing recursive traversal implementation, I reworked it to be iterative and, I think, made it much simpler in the process. This does make one notable semantic change: previously the radix iterator cached the element it pointed to, so an update to that element would not affect the value returned by the iterator. It no longer caches it, which makes it behave more like a regular collection iterator.
上级 6eabada8
......@@ -216,23 +216,23 @@ struct radix {
struct radix_iterator {
const radix* r_;
u64 k_;
// path_[i] is the node at level i. Note that the leaf is at zero
// and is radix_elem. The rest are radix_node. For now we assume all
// leaves are at level 0. Later we'll steal a bit for them. The root
// is path_[radix_levels].
radix_entry path_[radix_levels+1];
u32 leaf_;
// The key value just past the end of the range being iterator over.
u64 key_limit_;
// path_[i] points into the child array of the node at level i.
const radix_ptr *path_[radix_levels];
// The level of the current element.
u32 level_;
radix_iterator(const radix* r, u64 k);
radix_iterator(const radix* r, u64 k, u64 limit);
radix_iterator &operator++() {
if (!advance(radix_levels-1)) k_ = ~0ULL;
assert(k_ < key_limit_);
advance();
return *this;
}
radix_elem* operator*() {
return path_[leaf_].elem();
return path_[level_]->load().elem();
}
radix_node* node(u32 level) { return path_[level].node(); }
// Compare equality on just the key.
bool operator==(const radix_iterator &other) {
......@@ -241,19 +241,27 @@ struct radix_iterator {
return r_ != other.r_ || k_ != other.k_; }
private:
bool find_first_leaf(u32 level);
bool advance(u32 level);
// Advance to the next non-null leaf. This assumes that
// k_ < key_limit_.
void advance();
};
static inline radix_iterator
begin(const radix &r) { return radix_iterator(&r, 0); }
begin(const radix &r) {
return radix_iterator(&r, 0, (u64)1 << key_bits);
}
static inline radix_iterator
end(const radix &r) { return radix_iterator(&r, ~0ULL); }
// What we really need is one-past-the-last...
end(const radix &r) {
return radix_iterator(&r, (u64)1 << key_bits, (u64)1 << key_bits);
}
static inline radix_iterator
begin(const radix_range &rr) { return radix_iterator(rr.r_, rr.start_); }
begin(const radix_range &rr) {
return radix_iterator(rr.r_, rr.start_, rr.start_ + rr.size_);
}
static inline radix_iterator
end(const radix_range &rr) { return radix_iterator(rr.r_, rr.start_ + rr.size_); }
end(const radix_range &rr) {
return radix_iterator(rr.r_, rr.start_ + rr.size_, rr.start_ + rr.size_);
}
......@@ -246,65 +246,55 @@ radix_range::replace(u64 start, u64 size, radix_elem *val)
}, 0, 1L << key_bits, start, start + size);
}
radix_iterator::radix_iterator(const radix* r, u64 k)
: r_(r), k_(k) {
radix_iterator::radix_iterator(const radix* r, u64 k, u64 limit)
: r_(r), k_(k), key_limit_(limit) {
dprintf("%p: Made iterator with k = %lx\n", r_, k_);
if (k_ != ~0ULL) {
path_[radix_levels] = r_->root_.load();
if (path_[radix_levels].is_elem())
leaf_ = radix_levels; // Maybe best to not do this...
else if (!find_first_leaf(radix_levels - 1))
k_ = ~0ULL;
if (k_ == key_limit_)
return;
// Load the initial path
level_ = radix_levels;
const radix_ptr *node = &r_->root_;
radix_entry entry;
while ((entry = node->load()).is_node()) {
level_--;
path_[level_] = &entry.node()->child[index(k_, level_)];
node = path_[level_];
}
dprintf("%p: Adjusted: k = %lx\n", r_, k_);
}
bool
radix_iterator::advance(u32 level)
void
radix_iterator::advance()
{
// First, see if we can advance a lower level.
if (level > leaf_ && advance(level-1)) {
// Nothing more to do.
return true;
}
while (true) {
// As long as we haven't reached our limit or an element, advance
// to the next key.
k_ += (u64)1 << (level_ * bits_per_level);
if (k_ == key_limit_)
return;
// If we've reached the end of this node, move up the tree until
// we find a node that has more entries. (Note that we don't have
// to worry about going off the top because our key_limit_
// prevents that.)
while (index(k_, level_) == 0) {
level_++;
}
assert(level_ <= radix_levels);
// Try to advance this level, if we can.
u32 start_idx = index(k_, level)+1;
if (start_idx < (1<<bits_per_level)) {
// Find the first leaf starting at our sibling node.
k_ &= ~((1ULL<<((level+1) * bits_per_level)) - 1);
k_ |= (u64(start_idx) << (level * bits_per_level));
return find_first_leaf(level);
} else {
return false;
}
}
// Update our path pointer at the level we've moved over on
path_[level_]++;
bool
radix_iterator::find_first_leaf(u32 level)
{
// Find the first non-empty node after k_ on this level.
for (u32 idx = index(k_, level); idx < (1<<bits_per_level); idx++) {
radix_entry next = node(level+1)->child[idx].load();
if (!next.is_null()) {
if (index(k_, level) != idx) {
// We had to advance; clear everything this level and under
// and set this one.
k_ &= ~((1ULL<<((level+1) * bits_per_level)) - 1);
k_ |= (u64(idx) << (level * bits_per_level));
}
path_[level] = next;
if (next.is_elem()) {
// Found a leaf. Stop now.
leaf_ = level;
return true;
} else if (find_first_leaf(level-1))
// Keep looking.
return true;
// Move down the tree as much as we can
radix_entry entry;
while ((entry = path_[level_]->load()).is_node()) {
path_[level_ - 1] = &entry.node()->child[0];
level_--;
}
}
// Failed to find a leaf. Abort.
return false;
// Did we reach a non-null leaf?
if (!entry.is_null())
return;
}
}
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论