in glean/rts/ownership.cpp [157:262]
FOLLY_NOINLINE void completeOwnership(std::vector<Uset *> &facts, Usets& usets,
const Inventory &inventory,
Lookup &lookup) {
struct Stats {
Usets& usets;
size_t total_facts = 0;
size_t owned_facts = 0;
void bumpTotal() {
++total_facts;
}
void bumpOwned() {
++owned_facts;
if (owned_facts%1000000 == 0) {
dump();
}
}
void dump() {
auto ustats = usets.statistics();
LOG(INFO)
<< owned_facts << " of " << total_facts << " facts, "
<< usets.size() << " usets, "
<< ustats.promoted << " promoted, "
<< ustats.bytes << " bytes, "
<< ustats.adds << " adds, "
<< ustats.dups << " dups";
}
};
Stats stats{usets};
std::vector<Id> refs;
Traverser tracker([&](Id id, Pid) {
refs.push_back(id);
});
if (facts.size() == 0) {
return;
}
// Iterate over facts backwards - this ensures that we get all dependencies.
const auto max_id = Id::fromWord(facts.size());
for (auto iter = lookup.enumerateBack(max_id);
auto fact = iter->get();
iter->next()) {
stats.bumpTotal();
// `set == nullptr` means that the fact doesn't have an ownership set - we
// might want to make that an error eventually?
if (auto set = facts[fact.id.toWord()]) {
// TODO: Store the set in the DB and assign it to the current fact.
usets.promote(set);
// Collect all references to facts
const auto *predicate = inventory.lookupPredicate(fact.type);
assert(predicate);
predicate->traverse(tracker, fact.clause);
// For each fact we reference, add our ownership set to what we've
// computed for it so far. Use the `link` field to avoid computing the
// same set union multiple times.
//
// TODO: Try adding a fixed-size LRU (or LFU?) cache for set unions?
std::vector<Uset *> touched;
for (const auto id : refs) {
auto& me = facts[id.toWord()];
if (me == nullptr) {
// The fact didn't have ownership info before, assign the set to it.
me = set;
usets.use(me, 1);
} else if (const auto added = static_cast<Uset *>(me->link())) {
// The fact did have ownership info and we've already computed the
// union for that particular set.
usets.use(added);
usets.drop(me);
me = added;
} else {
// Compute the union.
const auto p = usets.merge(me, set);
me->link(p);
touched.push_back(me);
me = p;
}
}
// Reset the link fields. Note that we always add 1 refcount for sets we
// store in `touched` (to avoid having dangling references there) so drop
// it here.
for (const auto p : touched) {
p->link(nullptr);
usets.drop(p);
}
touched.clear();
refs.clear();
// TODO: We can drop the current's fact ownership info here - could shrink
// the vector.
// facts.shrink(fact.id);
stats.bumpOwned();
}
}
stats.dump();
}