提交 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 { ...@@ -216,23 +216,23 @@ struct radix {
struct radix_iterator { struct radix_iterator {
const radix* r_; const radix* r_;
u64 k_; u64 k_;
// path_[i] is the node at level i. Note that the leaf is at zero // The key value just past the end of the range being iterator over.
// and is radix_elem. The rest are radix_node. For now we assume all u64 key_limit_;
// leaves are at level 0. Later we'll steal a bit for them. The root // path_[i] points into the child array of the node at level i.
// is path_[radix_levels]. const radix_ptr *path_[radix_levels];
radix_entry path_[radix_levels+1]; // The level of the current element.
u32 leaf_; u32 level_;
radix_iterator(const radix* r, u64 k); radix_iterator(const radix* r, u64 k, u64 limit);
radix_iterator &operator++() { radix_iterator &operator++() {
if (!advance(radix_levels-1)) k_ = ~0ULL; assert(k_ < key_limit_);
advance();
return *this; return *this;
} }
radix_elem* operator*() { 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. // Compare equality on just the key.
bool operator==(const radix_iterator &other) { bool operator==(const radix_iterator &other) {
...@@ -241,19 +241,27 @@ struct radix_iterator { ...@@ -241,19 +241,27 @@ struct radix_iterator {
return r_ != other.r_ || k_ != other.k_; } return r_ != other.r_ || k_ != other.k_; }
private: private:
bool find_first_leaf(u32 level); // Advance to the next non-null leaf. This assumes that
bool advance(u32 level); // k_ < key_limit_.
void advance();
}; };
static inline radix_iterator 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 static inline radix_iterator
end(const radix &r) { return radix_iterator(&r, ~0ULL); } end(const radix &r) {
// What we really need is one-past-the-last... return radix_iterator(&r, (u64)1 << key_bits, (u64)1 << key_bits);
}
static inline radix_iterator 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 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) ...@@ -246,65 +246,55 @@ radix_range::replace(u64 start, u64 size, radix_elem *val)
}, 0, 1L << key_bits, start, start + size); }, 0, 1L << key_bits, start, start + size);
} }
radix_iterator::radix_iterator(const radix* r, u64 k) radix_iterator::radix_iterator(const radix* r, u64 k, u64 limit)
: r_(r), k_(k) { : r_(r), k_(k), key_limit_(limit) {
dprintf("%p: Made iterator with k = %lx\n", r_, k_); dprintf("%p: Made iterator with k = %lx\n", r_, k_);
if (k_ != ~0ULL) { if (k_ == key_limit_)
path_[radix_levels] = r_->root_.load(); return;
if (path_[radix_levels].is_elem())
leaf_ = radix_levels; // Maybe best to not do this... // Load the initial path
else if (!find_first_leaf(radix_levels - 1)) level_ = radix_levels;
k_ = ~0ULL; 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_); dprintf("%p: Adjusted: k = %lx\n", r_, k_);
} }
bool void
radix_iterator::advance(u32 level) radix_iterator::advance()
{ {
// First, see if we can advance a lower level. while (true) {
if (level > leaf_ && advance(level-1)) { // As long as we haven't reached our limit or an element, advance
// Nothing more to do. // to the next key.
return true; 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. // Update our path pointer at the level we've moved over on
u32 start_idx = index(k_, level)+1; path_[level_]++;
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;
}
}
bool // Move down the tree as much as we can
radix_iterator::find_first_leaf(u32 level) radix_entry entry;
{ while ((entry = path_[level_]->load()).is_node()) {
// Find the first non-empty node after k_ on this level. path_[level_ - 1] = &entry.node()->child[0];
for (u32 idx = index(k_, level); idx < (1<<bits_per_level); idx++) { level_--;
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;
} }
}
// Failed to find a leaf. Abort. // Did we reach a non-null leaf?
return false; if (!entry.is_null())
return;
}
} }
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论