rose: role aliasing improvements

These changes improve the performance of the role aliasing passes on
very large cases and fix a couple of small errors in the left and right
merge passes as well.
This commit is contained in:
Justin Viiret 2017-01-18 10:06:10 +11:00 committed by Matthew Barr
parent a55bbe657c
commit 988ea6b4e1
2 changed files with 225 additions and 243 deletions

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2015-2016, Intel Corporation * Copyright (c) 2015-2017, Intel Corporation
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met: * modification, are permitted provided that the following conditions are met:
@ -47,6 +47,7 @@
#include "util/container.h" #include "util/container.h"
#include "util/graph.h" #include "util/graph.h"
#include "util/graph_range.h" #include "util/graph_range.h"
#include "util/hash.h"
#include "util/order_check.h" #include "util/order_check.h"
#include "util/ue2_containers.h" #include "util/ue2_containers.h"
@ -111,11 +112,14 @@ struct AliasInEdge : EdgeAndVertex {
class CandidateSet { class CandidateSet {
public: public:
typedef set<RoseVertex>::iterator iterator; using key_type = RoseVertex;
typedef RoseVertex key_type; using iterator = set<RoseVertex>::iterator;
using const_iterator = set<RoseVertex>::const_iterator;
iterator begin() { return main_cont.begin(); } iterator begin() { return main_cont.begin(); }
iterator end() { return main_cont.end(); } iterator end() { return main_cont.end(); }
const_iterator begin() const { return main_cont.begin(); }
const_iterator end() const { return main_cont.end(); }
bool contains(RoseVertex a) const { bool contains(RoseVertex a) const {
return hash_cont.find(a) != hash_cont.end(); return hash_cont.find(a) != hash_cont.end();
@ -451,37 +455,6 @@ bool sameRightRoleProperties(const RoseBuildImpl &build, RoseVertex a,
return true; return true;
} }
/**
* Hash on some deterministic props checked in sameRoleProperties + properties
* required for right equivalence.
*/
static
size_t hashRightRoleProperties(RoseVertex v, const RoseGraph &g) {
using boost::hash_combine;
using boost::hash_range;
const RoseVertexProps &props = g[v];
size_t val = 0;
hash_combine(val, hash_range(begin(props.reports), end(props.reports)));
if (props.suffix) {
const auto &suffix = props.suffix;
if (suffix.castle) {
hash_combine(val, suffix.castle->reach());
hash_combine(val, suffix.castle->repeats.size());
}
if (suffix.graph) {
hash_combine(val, num_vertices(*suffix.graph));
}
if (suffix.haig) {
hash_combine(val, hash_dfa(*suffix.haig));
}
}
return val;
}
static static
void mergeEdgeAdd(RoseVertex u, RoseVertex v, const RoseEdge &from_edge, void mergeEdgeAdd(RoseVertex u, RoseVertex v, const RoseEdge &from_edge,
const RoseEdge *to_edge, RoseGraph &g) { const RoseEdge *to_edge, RoseGraph &g) {
@ -684,16 +657,6 @@ void findCandidates(const RoseBuildImpl &build, CandidateSet *candidates) {
num_vertices(build.g)); num_vertices(build.g));
} }
static
RoseVertex pickSucc(const RoseVertex v, const RoseGraph &g) {
RoseGraph::adjacency_iterator ai, ae;
tie(ai, ae) = adjacent_vertices(v, g);
if (ai == ae) {
return RoseGraph::null_vertex();
}
return *ai;
}
static static
RoseVertex pickPred(const RoseVertex v, const RoseGraph &g, RoseVertex pickPred(const RoseVertex v, const RoseGraph &g,
const RoseBuildImpl &build) { const RoseBuildImpl &build) {
@ -854,7 +817,7 @@ void pruneUnusedTops(NGHolder &h, const RoseGraph &g,
return; return;
} }
assert(isCorrectlyTopped(h)); assert(isCorrectlyTopped(h));
DEBUG_PRINTF("prunning unused tops\n"); DEBUG_PRINTF("pruning unused tops\n");
ue2::flat_set<u32> used_tops; ue2::flat_set<u32> used_tops;
for (auto v : verts) { for (auto v : verts) {
assert(g[v].left.graph.get() == &h); assert(g[v].left.graph.get() == &h);
@ -1427,62 +1390,95 @@ bool attemptRoseMerge(RoseBuildImpl &build, bool preds_same, RoseVertex a,
return false; return false;
} }
/**
* \brief Buckets that only contain one vertex are never going to lead to a
* merge.
*/
static
void removeSingletonBuckets(vector<vector<RoseVertex>> &buckets) {
auto it = remove_if(
begin(buckets), end(buckets),
[](const vector<RoseVertex> &bucket) { return bucket.size() < 2; });
if (it != end(buckets)) {
DEBUG_PRINTF("deleting %zu singleton buckets\n",
distance(it, end(buckets)));
buckets.erase(it, end(buckets));
}
}
static
void buildInvBucketMap(const vector<vector<RoseVertex>> &buckets,
ue2::unordered_map<RoseVertex, size_t> &inv) {
inv.clear();
for (size_t i = 0; i < buckets.size(); i++) {
for (auto v : buckets[i]) {
assert(!contains(inv, v));
inv.emplace(v, i);
}
}
}
/**
* \brief Generic splitter that will use the given split function to partition
* the vector of buckets, then remove buckets with <= 1 entry.
*/
template <class SplitFunction>
void splitAndFilterBuckets(vector<vector<RoseVertex>> &buckets,
const SplitFunction &make_split_key) {
if (buckets.empty()) {
return;
}
vector<vector<RoseVertex>> out;
// Mapping from split key value to new bucket index.
using key_type = decltype(make_split_key(RoseGraph::null_vertex()));
unordered_map<key_type, size_t> dest_map;
dest_map.reserve(buckets.front().size());
for (const auto &bucket : buckets) {
assert(!bucket.empty());
dest_map.clear();
for (RoseVertex v : bucket) {
auto p = dest_map.emplace(make_split_key(v), out.size());
if (p.second) { // New key, add a bucket.
out.emplace_back();
}
auto out_bucket = p.first->second;
out[out_bucket].push_back(v);
}
}
if (out.size() == buckets.size()) {
return; // No new buckets created.
}
buckets = move(out);
removeSingletonBuckets(buckets);
}
static static
void splitByReportSuffixBehaviour(const RoseGraph &g, void splitByReportSuffixBehaviour(const RoseGraph &g,
vector<vector<RoseVertex>> &buckets, vector<vector<RoseVertex>> &buckets) {
ue2::unordered_map<RoseVertex, size_t> &inv) { // Split by report set and suffix info.
/* vertices with different report/suffixes can never be considered for right auto make_split_key = [&g](RoseVertex v) {
* merge. */ return hash_all(g[v].reports, g[v].suffix);
vector<vector<RoseVertex>> out; };
for (const vector<RoseVertex> &b : buckets) { splitAndFilterBuckets(buckets, make_split_key);
assert(!b.empty());
map<pair<flat_set<ReportID>, RoseSuffixInfo>, size_t> dest_map;
for (RoseVertex v : b) {
auto key = decltype(dest_map)::key_type(g[v].reports, g[v].suffix);
size_t out_bucket;
if (contains(dest_map, key)) {
out_bucket = dest_map[key];
} else {
out_bucket = out.size();
out.push_back(vector<RoseVertex>());
dest_map[key] = out_bucket;
}
out[out_bucket].push_back(v);
inv[v] = out_bucket;
}
}
buckets.swap(out);
} }
static static
void splitByLiteralTable(const RoseBuildImpl &build, void splitByLiteralTable(const RoseBuildImpl &build,
vector<vector<RoseVertex>> &buckets, vector<vector<RoseVertex>> &buckets) {
ue2::unordered_map<RoseVertex, size_t> &inv) {
const RoseGraph &g = build.g; const RoseGraph &g = build.g;
vector<vector<RoseVertex>> out; // Split by literal table.
auto make_split_key = [&](RoseVertex v) {
for (const auto &bucket : buckets) { const auto &lits = g[v].literals;
assert(!bucket.empty()); assert(!lits.empty());
map<rose_literal_table, size_t> dest_map; return build.literals.right.at(*lits.begin()).table;
for (RoseVertex v : bucket) { };
auto table = build.literals.right.at(*g[v].literals.begin()).table; splitAndFilterBuckets(buckets, make_split_key);
size_t out_bucket;
if (contains(dest_map, table)) {
out_bucket = dest_map[table];
} else {
out_bucket = out.size();
out.push_back(vector<RoseVertex>());
dest_map[table] = out_bucket;
}
out[out_bucket].push_back(v);
inv[v] = out_bucket;
}
}
buckets.swap(out);
} }
static static
@ -1543,6 +1539,9 @@ void splitByNeighbour(const RoseGraph &g, vector<vector<RoseVertex>> &buckets,
} }
insert(&buckets, buckets.end(), extras); insert(&buckets, buckets.end(), extras);
} }
removeSingletonBuckets(buckets);
buildInvBucketMap(buckets, inv);
} }
static static
@ -1551,16 +1550,35 @@ splitDiamondMergeBuckets(CandidateSet &candidates, const RoseBuildImpl &build) {
const RoseGraph &g = build.g; const RoseGraph &g = build.g;
vector<vector<RoseVertex>> buckets(1); vector<vector<RoseVertex>> buckets(1);
ue2::unordered_map<RoseVertex, size_t> inv; buckets[0].reserve(candidates.size());
for (RoseVertex v : candidates) { insert(&buckets[0], buckets[0].end(), candidates);
buckets[0].push_back(v);
inv[v] = 0; DEBUG_PRINTF("at start, %zu candidates in 1 bucket\n", candidates.size());
splitByReportSuffixBehaviour(g, buckets);
DEBUG_PRINTF("split by report/suffix, %zu buckets\n", buckets.size());
if (buckets.empty()) {
return buckets;
} }
splitByReportSuffixBehaviour(g, buckets, inv); splitByLiteralTable(build, buckets);
splitByLiteralTable(build, buckets, inv); DEBUG_PRINTF("split by lit table, %zu buckets\n", buckets.size());
if (buckets.empty()) {
return buckets;
}
// Neighbour splits require inverse map.
ue2::unordered_map<RoseVertex, size_t> inv;
buildInvBucketMap(buckets, inv);
splitByNeighbour(g, buckets, inv, true); splitByNeighbour(g, buckets, inv, true);
DEBUG_PRINTF("split by successor, %zu buckets\n", buckets.size());
if (buckets.empty()) {
return buckets;
}
splitByNeighbour(g, buckets, inv, false); splitByNeighbour(g, buckets, inv, false);
DEBUG_PRINTF("split by predecessor, %zu buckets\n", buckets.size());
return buckets; return buckets;
} }
@ -1677,55 +1695,62 @@ vector<RoseVertex>::iterator findLeftMergeSibling(
return end; return end;
} }
static never_inline static
void leftMergePass(CandidateSet &candidates, RoseBuildImpl &build, void getLeftMergeSiblings(const RoseBuildImpl &build, RoseVertex a,
vector<RoseVertex> *dead, RoseAliasingInfo &rai) { vector<RoseVertex> &siblings) {
DEBUG_PRINTF("begin (%zu)\n", candidates.size());
RoseGraph &g = build.g;
vector<RoseVertex> siblings;
CandidateSet::iterator it = candidates.begin();
while (it != candidates.end()) {
RoseVertex a = *it;
CandidateSet::iterator ait = it;
++it;
// We have to find a sibling to merge `a' with, and we select between // We have to find a sibling to merge `a' with, and we select between
// two approaches to minimize the number of vertices we have to // two approaches to minimize the number of vertices we have to
// examine; which we use depends on the shape of the graph. // examine; which we use depends on the shape of the graph.
const RoseGraph &g = build.g;
assert(!g[a].literals.empty()); assert(!g[a].literals.empty());
u32 lit_id = *g[a].literals.begin(); u32 lit_id = *g[a].literals.begin();
const auto &verts = build.literal_info.at(lit_id).vertices; const auto &verts = build.literal_info.at(lit_id).vertices;
RoseVertex pred = pickPred(a, g, build); RoseVertex pred = pickPred(a, g, build);
siblings.clear(); siblings.clear();
if (pred == RoseGraph::null_vertex() || build.isAnyStart(pred)
|| out_degree(pred, g) > verts.size()) { if (pred == RoseGraph::null_vertex() || build.isAnyStart(pred) ||
out_degree(pred, g) > verts.size()) {
// Select sibling from amongst the vertices that share a literal. // Select sibling from amongst the vertices that share a literal.
siblings.insert(siblings.end(), verts.begin(), verts.end()); insert(&siblings, siblings.end(), verts);
} else { } else {
// Select sibling from amongst the vertices that share a // Select sibling from amongst the vertices that share a
// predecessor. // predecessor.
insert(&siblings, siblings.end(), adjacent_vertices(pred, g)); insert(&siblings, siblings.end(), adjacent_vertices(pred, g));
} }
}
auto jt = findLeftMergeSibling(siblings.begin(), siblings.end(), a, static never_inline
build, rai, candidates); void leftMergePass(CandidateSet &candidates, RoseBuildImpl &build,
vector<RoseVertex> *dead, RoseAliasingInfo &rai) {
DEBUG_PRINTF("begin (%zu)\n", candidates.size());
vector<RoseVertex> siblings;
auto it = candidates.begin();
while (it != candidates.end()) {
RoseVertex a = *it;
CandidateSet::iterator ait = it;
++it;
getLeftMergeSiblings(build, a, siblings);
auto jt = siblings.begin();
while (jt != siblings.end()) {
jt = findLeftMergeSibling(jt, siblings.end(), a, build, rai,
candidates);
if (jt == siblings.end()) { if (jt == siblings.end()) {
continue; break;
} }
RoseVertex b = *jt; RoseVertex b = *jt;
if (attemptRoseMerge(build, true, a, b, false, rai)) {
if (!attemptRoseMerge(build, true, a, b, 0, rai)) {
DEBUG_PRINTF("rose fail\n");
continue;
}
mergeVerticesLeft(a, b, build, rai); mergeVerticesLeft(a, b, build, rai);
dead->push_back(a); dead->push_back(a);
candidates.erase(ait); candidates.erase(ait);
break; // consider next a
}
++jt;
}
} }
DEBUG_PRINTF("%zu candidates remaining\n", candidates.size()); DEBUG_PRINTF("%zu candidates remaining\n", candidates.size());
@ -1810,91 +1835,49 @@ vector<RoseVertex>::const_iterator findRightMergeSibling(
return end; return end;
} }
template<class Iter>
static static
void split(map<RoseVertex, size_t> &keys, size_t *next_key, Iter it, void splitByRightProps(const RoseGraph &g,
const Iter end) { vector<vector<RoseVertex>> &buckets) {
map<size_t, size_t> new_keys; // Successor vector used in make_split_key. We declare it here so we can
// reuse storage.
vector<RoseVertex> succ;
for (; it != end; ++it) { // Split by {successors, literals, reports}.
RoseVertex v = *it; auto make_split_key = [&](RoseVertex v) {
size_t ok = keys[v]; succ.clear();
size_t nk; insert(&succ, succ.end(), adjacent_vertices(v, g));
if (contains(new_keys, ok)) { sort(succ.begin(), succ.end());
nk = new_keys[ok]; return hash_all(g[v].literals, g[v].reports, succ);
} else { };
nk = (*next_key)++; splitAndFilterBuckets(buckets, make_split_key);
new_keys[ok] = nk;
}
keys[v] = nk;
}
} }
static never_inline static never_inline
void buildCandidateRightSiblings(CandidateSet &candidates, RoseBuildImpl &build, vector<vector<RoseVertex>>
map<size_t, vector<RoseVertex>> &sibling_cache, splitRightMergeBuckets(const CandidateSet &candidates,
map<RoseVertex, size_t> &keys_ext) { const RoseBuildImpl &build) {
RoseGraph &g = build.g; const RoseGraph &g = build.g;
size_t next_key = 1; vector<vector<RoseVertex>> buckets(1);
map<RoseVertex, size_t> keys; buckets[0].reserve(candidates.size());
insert(&buckets[0], buckets[0].end(), candidates);
for (const auto &c : candidates) { DEBUG_PRINTF("at start, %zu candidates in 1 bucket\n", candidates.size());
keys[c] = 0;
splitByReportSuffixBehaviour(g, buckets);
DEBUG_PRINTF("split by report/suffix, %zu buckets\n", buckets.size());
if (buckets.empty()) {
return buckets;
} }
set<RoseVertex> done_succ; splitByRightProps(g, buckets);
set<u32> done_lit; DEBUG_PRINTF("split by right-merge properties, %zu buckets\n",
buckets.size());
for (auto a : candidates) { if (buckets.empty()) {
assert(!g[a].literals.empty()); return buckets;
u32 lit_id = *g[a].literals.begin();
RoseVertex succ = pickSucc(a, g);
const auto &verts = build.literal_info.at(lit_id).vertices;
if (succ != RoseGraph::null_vertex()
&& in_degree(succ, g) < verts.size()) {
if (!done_succ.insert(succ).second) {
continue; // succ already in done_succ.
}
RoseGraph::inv_adjacency_iterator ai, ae;
tie (ai, ae) = inv_adjacent_vertices(succ, g);
split(keys, &next_key, ai, ae);
} else {
if (!done_lit.insert(lit_id).second) {
continue; // lit_id already in done_lit.
}
split(keys, &next_key, verts.begin(), verts.end());
}
} }
map<size_t, map<size_t, size_t>> int_to_ext; return buckets;
for (const auto &key : keys) {
RoseVertex v = key.first;
u32 ext;
size_t rph = hashRightRoleProperties(v, g);
if (contains(int_to_ext[key.second], rph)) {
ext = int_to_ext[key.second][rph];
} else {
ext = keys_ext.size();
int_to_ext[key.second][rph] = ext;
}
keys_ext[v] = ext;
sibling_cache[ext].push_back(v);
}
for (auto &siblings : sibling_cache | map_values) {
sort(siblings.begin(), siblings.end());
}
}
static
const vector<RoseVertex> &getCandidateRightSiblings(
const map<size_t, vector<RoseVertex>> &sibling_cache,
map<RoseVertex, size_t> &keys, RoseVertex a) {
size_t key = keys.at(a);
return sibling_cache.at(key);
} }
static never_inline static never_inline
@ -1903,45 +1886,31 @@ void rightMergePass(CandidateSet &candidates, RoseBuildImpl &build,
RoseAliasingInfo &rai) { RoseAliasingInfo &rai) {
DEBUG_PRINTF("begin\n"); DEBUG_PRINTF("begin\n");
map<size_t, vector<RoseVertex>> sibling_cache; if (candidates.empty()) {
map<RoseVertex, size_t> keys; return;
}
buildCandidateRightSiblings(candidates, build, sibling_cache, keys); auto buckets = splitRightMergeBuckets(candidates, build);
CandidateSet::iterator it = candidates.begin(); for (const auto &bucket : buckets) {
while (it != candidates.end()) { assert(!bucket.empty());
for (auto it = bucket.begin(); it != bucket.end(); it++) {
RoseVertex a = *it; RoseVertex a = *it;
CandidateSet::iterator ait = it; for (auto jt = bucket.begin(); jt != bucket.end(); jt++) {
++it; jt = findRightMergeSibling(jt, bucket.end(), a, build, rai,
// We have to find a sibling to merge `a' with, and we select between
// two approaches to minimize the number of vertices we have to
// examine; which we use depends on the shape of the graph.
const vector<RoseVertex> &siblings
= getCandidateRightSiblings(sibling_cache, keys, a);
auto jt = siblings.begin();
while (jt != siblings.end()) {
jt = findRightMergeSibling(jt, siblings.end(), a, build, rai,
candidates); candidates);
if (jt == siblings.end()) { if (jt == bucket.end()) {
break; break;
} }
if (attemptRoseMerge(build, false, a, *jt, !mergeRoses, rai)) {
break;
}
++jt;
}
if (jt == siblings.end()) {
continue;
}
RoseVertex b = *jt; RoseVertex b = *jt;
if (attemptRoseMerge(build, false, a, b, !mergeRoses, rai)) {
mergeVerticesRight(a, b, build, rai); mergeVerticesRight(a, b, build, rai);
dead->push_back(a); dead->push_back(a);
candidates.erase(ait); candidates.erase(a);
break; // consider next a
}
}
}
} }
DEBUG_PRINTF("%zu candidates remaining\n", candidates.size()); DEBUG_PRINTF("%zu candidates remaining\n", candidates.size());

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2015-2016, Intel Corporation * Copyright (c) 2015-2017, Intel Corporation
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met: * modification, are permitted provided that the following conditions are met:
@ -36,6 +36,7 @@
#include <type_traits> #include <type_traits>
#include <utility> #include <utility>
#include <boost/functional/hash.hpp>
#include <boost/iterator/iterator_facade.hpp> #include <boost/iterator/iterator_facade.hpp>
#include <boost/unordered/unordered_map.hpp> #include <boost/unordered/unordered_map.hpp>
#include <boost/unordered/unordered_set.hpp> #include <boost/unordered/unordered_set.hpp>
@ -318,6 +319,12 @@ public:
friend void swap(flat_set &a, flat_set &b) { friend void swap(flat_set &a, flat_set &b) {
a.swap(b); a.swap(b);
} }
// Free hash function.
friend size_t hash_value(const flat_set &a) {
using boost::hash_value;
return hash_value(a.data);
}
}; };
/** /**
@ -604,6 +611,12 @@ public:
friend void swap(flat_map &a, flat_map &b) { friend void swap(flat_map &a, flat_map &b) {
a.swap(b); a.swap(b);
} }
// Free hash function.
friend size_t hash_value(const flat_map &a) {
using boost::hash_value;
return hash_value(a.data);
}
}; };
} // namespace } // namespace