Skip to content
Permalink
97d9df599a
Switch branches/tags
Go to file
 
 
Cannot retrieve contributors at this time
374 lines (336 sloc) 10.9 KB
// Copyright 2019 The TCMalloc Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "tcmalloc/huge_address_map.h"
#include <stdlib.h>
#include <algorithm>
#include <new>
#include "absl/base/internal/cycleclock.h"
#include "tcmalloc/internal/logging.h"
GOOGLE_MALLOC_SECTION_BEGIN
namespace tcmalloc {
namespace tcmalloc_internal {
const HugeAddressMap::Node *HugeAddressMap::Node::next() const {
const Node *n = right_;
if (n) {
while (n->left_) n = n->left_;
return n;
}
n = parent_;
const Node *last = this;
while (n) {
if (n->left_ == last) return n;
last = n;
n = n->parent_;
}
return nullptr;
}
HugeAddressMap::Node *HugeAddressMap::Node::next() {
const Node *n = static_cast<const Node *>(this)->next();
return const_cast<Node *>(n);
}
void HugeAddressMap::Node::Check(size_t *num_nodes, HugeLength *size) const {
HugeLength longest = range_.len();
*num_nodes += 1;
*size += range_.len();
if (left_) {
// tree
CHECK_CONDITION(left_->range_.start() < range_.start());
// disjoint
CHECK_CONDITION(left_->range_.end_addr() < range_.start_addr());
// well-formed
CHECK_CONDITION(left_->parent_ == this);
// heap
CHECK_CONDITION(left_->prio_ <= prio_);
left_->Check(num_nodes, size);
if (left_->longest_ > longest) longest = left_->longest_;
}
if (right_) {
// tree
CHECK_CONDITION(right_->range_.start() > range_.start());
// disjoint
CHECK_CONDITION(right_->range_.start_addr() > range_.end_addr());
// well-formed
CHECK_CONDITION(right_->parent_ == this);
// heap
CHECK_CONDITION(right_->prio_ <= prio_);
right_->Check(num_nodes, size);
if (right_->longest_ > longest) longest = right_->longest_;
}
CHECK_CONDITION(longest_ == longest);
}
const HugeAddressMap::Node *HugeAddressMap::first() const {
const Node *n = root();
if (!n) return nullptr;
const Node *left = n->left_;
while (left) {
n = left;
left = n->left_;
}
return n;
}
HugeAddressMap::Node *HugeAddressMap::first() {
const Node *f = static_cast<const HugeAddressMap *>(this)->first();
return const_cast<Node *>(f);
}
void HugeAddressMap::Check() {
size_t nodes = 0;
HugeLength size = NHugePages(0);
if (root_) {
CHECK_CONDITION(root_->parent_ == nullptr);
root_->Check(&nodes, &size);
}
CHECK_CONDITION(nodes == nranges());
CHECK_CONDITION(size == total_mapped());
CHECK_CONDITION(total_nodes_ == used_nodes_ + freelist_size_);
}
size_t HugeAddressMap::nranges() const { return used_nodes_; }
HugeLength HugeAddressMap::total_mapped() const { return total_size_; }
void HugeAddressMap::Print(Printer *out) const {
out->printf("HugeAddressMap: treap %zu / %zu nodes used / created\n",
used_nodes_, total_nodes_);
const size_t longest = root_ ? root_->longest_.raw_num() : 0;
out->printf("HugeAddressMap: %zu contiguous hugepages available\n", longest);
}
void HugeAddressMap::PrintInPbtxt(PbtxtRegion *hpaa) const {
hpaa->PrintI64("num_huge_address_map_treap_nodes_used", used_nodes_);
hpaa->PrintI64("num_huge_address_map_treap_nodes_created", total_nodes_);
const size_t longest = root_ ? root_->longest_.in_bytes() : 0;
hpaa->PrintI64("contiguous_free_bytes", longest);
}
HugeAddressMap::Node *HugeAddressMap::Predecessor(HugePage p) {
Node *n = root();
Node *best = nullptr;
while (n) {
HugeRange here = n->range_;
if (here.contains(p)) return n;
if (p < here.start()) {
// p comes before here:
// our predecessor isn't here, nor in the right subtree.
n = n->left_;
} else {
// p comes after here:
// here is a valid candidate, and the right subtree might have better.
best = n;
n = n->right_;
}
}
return best;
}
void HugeAddressMap::Merge(Node *b, HugeRange r, Node *a) {
auto merge_when = [](HugeRange x, int64_t x_when, HugeRange y,
int64_t y_when) {
// avoid overflow with floating-point
const size_t x_len = x.len().raw_num();
const size_t y_len = y.len().raw_num();
const double x_weight = static_cast<double>(x_len) * x_when;
const double y_weight = static_cast<double>(y_len) * y_when;
return static_cast<int64_t>((x_weight + y_weight) / (x_len + y_len));
};
int64_t when = absl::base_internal::CycleClock::Now();
// Two way merges are easy.
if (a == nullptr) {
b->when_ = merge_when(b->range_, b->when(), r, when);
b->range_ = Join(b->range_, r);
FixLongest(b);
return;
} else if (b == nullptr) {
a->when_ = merge_when(r, when, a->range_, a->when());
a->range_ = Join(r, a->range_);
FixLongest(a);
return;
}
// Three way merge: slightly harder. We must remove one node
// (arbitrarily picking next).
HugeRange partial = Join(r, a->range_);
int64_t partial_when = merge_when(r, when, a->range_, a->when());
HugeRange full = Join(b->range_, partial);
int64_t full_when = merge_when(b->range_, b->when(), partial, partial_when);
// Removing a will reduce total_size_ by that length, but since we're merging
// we actually don't change lengths at all; undo that.
total_size_ += a->range_.len();
Remove(a);
b->range_ = full;
b->when_ = full_when;
FixLongest(b);
}
void HugeAddressMap::Insert(HugeRange r) {
total_size_ += r.len();
// First, try to merge if necessary. Note there are three possibilities:
// we might need to merge before with r, r with after, or all three together.
Node *before = Predecessor(r.start());
CHECK_CONDITION(!before || !before->range_.intersects(r));
Node *after = before ? before->next() : first();
CHECK_CONDITION(!after || !after->range_.intersects(r));
if (before && before->range_.precedes(r)) {
if (after && r.precedes(after->range_)) {
Merge(before, r, after);
} else {
Merge(before, r, nullptr);
}
return;
} else if (after && r.precedes(after->range_)) {
Merge(nullptr, r, after);
return;
}
CHECK_CONDITION(!before || !before->range_.precedes(r));
CHECK_CONDITION(!after || !r.precedes(after->range_));
// No merging possible; just add a new node.
Node *n = Get(r);
Node *curr = root();
Node *parent = nullptr;
Node **link = &root_;
// Walk down the tree to our correct location
while (curr != nullptr && curr->prio_ >= n->prio_) {
curr->longest_ = std::max(curr->longest_, r.len());
parent = curr;
if (curr->range_.start() < r.start()) {
link = &curr->right_;
curr = curr->right_;
} else {
link = &curr->left_;
curr = curr->left_;
}
}
*link = n;
n->parent_ = parent;
n->left_ = n->right_ = nullptr;
n->longest_ = r.len();
if (curr) {
HugePage p = r.start();
// We need to split the treap at curr into n's children.
// This will be two treaps: one less than p, one greater, and has
// a nice recursive structure.
Node **less = &n->left_;
Node *lp = n;
Node **more = &n->right_;
Node *mp = n;
while (curr) {
if (curr->range_.start() < p) {
*less = curr;
curr->parent_ = lp;
less = &curr->right_;
lp = curr;
curr = curr->right_;
} else {
*more = curr;
curr->parent_ = mp;
more = &curr->left_;
mp = curr;
curr = curr->left_;
}
}
*more = *less = nullptr;
// We ripped apart the tree along these two paths--fix longest pointers.
FixLongest(lp);
FixLongest(mp);
}
}
void HugeAddressMap::Node::FixLongest() {
const HugeLength l = left_ ? left_->longest_ : NHugePages(0);
const HugeLength r = right_ ? right_->longest_ : NHugePages(0);
const HugeLength c = range_.len();
const HugeLength new_longest = std::max({l, r, c});
longest_ = new_longest;
}
void HugeAddressMap::FixLongest(HugeAddressMap::Node *n) {
while (n) {
n->FixLongest();
n = n->parent_;
}
}
void HugeAddressMap::Remove(HugeAddressMap::Node *n) {
total_size_ -= n->range_.len();
// We need to merge the left and right children of n into one
// treap, then glue it into place wherever n was.
Node **link;
Node *parent = n->parent_;
Node *top = n->left_;
Node *bottom = n->right_;
const HugeLength child_longest =
std::max(top ? top->longest_ : NHugePages(0),
bottom ? bottom->longest_ : NHugePages(0));
if (!parent) {
link = &root_;
} else {
// Account for the removed child--might change longests.
// Easiest way: update this subtree to ignore the removed node,
// then fix the chain of parents.
n->longest_ = child_longest;
FixLongest(parent);
if (parent->range_.start() > n->range_.start()) {
link = &parent->left_;
} else {
link = &parent->right_;
}
}
// A routine op we'll need a lot: given two (possibly null)
// children, put the root-ier one into top.
auto reorder_maybe = [](Node **top, Node **bottom) {
Node *b = *bottom, *t = *top;
if (b && (!t || t->prio_ < b->prio_)) {
*bottom = t;
*top = b;
}
};
reorder_maybe(&top, &bottom);
// if we have two treaps to merge (top is always non-null if bottom is)
// Invariant: top, bottom are two valid (longest included)
// treaps. parent (and all above/elsewhere) have the correct longest
// values, though parent does not have the correct children (will be the
// merged value of top and bottom.)
while (bottom) {
*link = top;
top->parent_ = parent;
// We're merging bottom into top, so top might contain a longer
// chunk than it thinks.
top->longest_ = std::max(top->longest_, bottom->longest_);
parent = top;
if (bottom->range_.start() < top->range_.start()) {
link = &top->left_;
top = top->left_;
} else {
link = &top->right_;
top = top->right_;
}
reorder_maybe(&top, &bottom);
}
*link = top;
if (top) top->parent_ = parent;
Put(n);
}
void HugeAddressMap::Put(Node *n) {
freelist_size_++;
used_nodes_--;
n->left_ = freelist_;
freelist_ = n;
}
HugeAddressMap::Node *HugeAddressMap::Get(HugeRange r) {
CHECK_CONDITION((freelist_ == nullptr) == (freelist_size_ == 0));
used_nodes_++;
int prio = rand_r(&seed_);
if (freelist_size_ == 0) {
total_nodes_++;
Node *ret = reinterpret_cast<Node *>(meta_(sizeof(Node)));
return new (ret) Node(r, prio);
}
freelist_size_--;
Node *ret = freelist_;
freelist_ = ret->left_;
return new (ret) Node(r, prio);
}
HugeAddressMap::Node::Node(HugeRange r, int prio)
: range_(r), prio_(prio), when_(absl::base_internal::CycleClock::Now()) {}
} // namespace tcmalloc_internal
} // namespace tcmalloc
GOOGLE_MALLOC_SECTION_END