Skip to content

Commit 7db96b6

Browse files
vloppinkripken
authored andcommitted
Speedup localgraph (#1610)
* LocalGraph : Replace seen unordered_set by boolean check. * LocalGraph : use unordered_map to store index -> last set_local instead of vector. * LocalGraph : - Use internal counter to avoid invalidation at each cycle. - Move all blocks structs into a contiguous vector of smaller ones.
1 parent 25890f5 commit 7db96b6

File tree

1 file changed

+64
-18
lines changed

1 file changed

+64
-18
lines changed

src/ir/LocalGraph.cpp

Lines changed: 64 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ struct Action {
5050
// information about a basic block
5151
struct Info {
5252
std::vector<Action> actions; // actions occurring in this block
53-
std::vector<SetLocal*> lastSets; // for each index, the last set_local for it
53+
std::unordered_map<Index, SetLocal*> lastSets; // for each index, the last set_local for it
5454

5555
void dump(Function* func) {
5656
if (actions.empty()) return;
@@ -76,11 +76,7 @@ struct Flower : public CFGWalker<Flower, Visitor<Flower>, Info> {
7676
}
7777

7878
BasicBlock* makeBasicBlock() {
79-
auto* ret = new BasicBlock();
80-
auto& lastSets = ret->contents.lastSets;
81-
lastSets.resize(getFunction()->getNumLocals());
82-
std::fill(lastSets.begin(), lastSets.end(), nullptr);
83-
return ret;
79+
return new BasicBlock();
8480
}
8581

8682
// cfg traversal work
@@ -103,12 +99,61 @@ struct Flower : public CFGWalker<Flower, Visitor<Flower>, Info> {
10399
}
104100

105101
void flow(Function* func) {
102+
// This block struct is optimized for this flow process (Minimal information, iteration index).
103+
struct FlowBlock {
104+
// last Traversed Iteration
105+
// This value help us to find if this block has been seen while traversing blocks.
106+
// We compare this value to the current iteration index in order to determine if we already process this block in the current iteration.
107+
// This speed up the processing compared to unordered_set or other struct usage. (No need to reset internal values, lookup into container, ...)
108+
size_t lastTraversedIteration;
109+
std::vector<Action> actions; // actions occurring in this block
110+
std::vector<FlowBlock*> in;
111+
// for each index, the last set_local for it
112+
// The unordered_map from BasicBlock is converted ther into a vector
113+
// This speed up search as there are almost always fewer than 100 items
114+
std::vector<std::pair<Index, SetLocal*>> lastSets;
115+
};
116+
106117
auto numLocals = func->getNumLocals();
107118
std::vector<std::vector<GetLocal*>> allGets;
108119
allGets.resize(numLocals);
109-
std::unordered_set<BasicBlock*> seen;
110-
std::vector<BasicBlock*> work;
111-
for (auto& block : basicBlocks) {
120+
std::vector<FlowBlock*> work;
121+
122+
123+
// Converts input blocks (basicBlocks) into more efficient blocks to improve memory access.
124+
std::vector<FlowBlock> flowBlocks;
125+
flowBlocks.resize(basicBlocks.size());
126+
127+
// Init mapping between basicblocks and flowBlocks
128+
std::unordered_map<BasicBlock*, FlowBlock*> basicToFlowMap;
129+
for (size_t i = 0; i < basicBlocks.size(); ++i) {
130+
basicToFlowMap.emplace(std::make_pair(basicBlocks[i].get(), &flowBlocks[i]));
131+
}
132+
133+
FlowBlock* entryFlowBlock = nullptr;
134+
for (size_t i = 0; i < flowBlocks.size(); ++i) {
135+
auto& optBlock = flowBlocks[i];
136+
auto& inBlock = basicBlocks[i];
137+
// Get the equivalent block to entry in the flow list
138+
if (inBlock.get() == entry) entryFlowBlock = &optBlock;
139+
// Initialize iteration index to max size_t to ensure we don't miss a block from wrong value.
140+
optBlock.lastTraversedIteration = -1;
141+
optBlock.actions.swap(inBlock->contents.actions);
142+
// Map in block to flow blocks
143+
auto& inBlocks = inBlock->in;
144+
optBlock.in.resize(inBlocks.size());
145+
std::transform(inBlocks.begin(), inBlocks.end(), optBlock.in.begin(), [&](BasicBlock* block) { return basicToFlowMap[block]; });
146+
147+
// Convert unordered_map to vector
148+
optBlock.lastSets.reserve(inBlock->contents.lastSets.size());
149+
for (auto set : inBlock->contents.lastSets) {
150+
optBlock.lastSets.emplace_back(std::make_pair(set.first, set.second));
151+
}
152+
}
153+
assert(entryFlowBlock != nullptr);
154+
155+
size_t currentIteration = 0;
156+
for (auto& block : flowBlocks) {
112157
#ifdef LOCAL_GRAPH_DEBUG
113158
std::cout << "basic block " << block.get() << " :\n";
114159
for (auto& action : block->contents.actions) {
@@ -120,7 +165,7 @@ struct Flower : public CFGWalker<Flower, Visitor<Flower>, Info> {
120165
#endif
121166
// go through the block, finding each get and adding it to its index,
122167
// and seeing how sets affect that
123-
auto& actions = block->contents.actions;
168+
auto& actions = block.actions;
124169
// move towards the front, handling things as we go
125170
for (int i = int(actions.size()) - 1; i >= 0; i--) {
126171
auto& action = actions[i];
@@ -142,8 +187,8 @@ struct Flower : public CFGWalker<Flower, Visitor<Flower>, Info> {
142187
for (Index index = 0; index < numLocals; index++) {
143188
auto& gets = allGets[index];
144189
if (gets.empty()) continue;
145-
work.push_back(block.get());
146-
seen.clear();
190+
work.push_back(&block);
191+
147192
// note that we may need to revisit the later parts of this initial
148193
// block, if we are in a loop, so don't mark it as seen
149194
while (!work.empty()) {
@@ -152,21 +197,21 @@ struct Flower : public CFGWalker<Flower, Visitor<Flower>, Info> {
152197
// we have gone through this block; now we must handle flowing to
153198
// the inputs
154199
if (curr->in.empty()) {
155-
if (curr == entry) {
200+
if (curr == entryFlowBlock) {
156201
// these receive a param or zero init value
157202
for (auto* get : gets) {
158203
getSetses[get].insert(nullptr);
159204
}
160205
}
161206
} else {
162207
for (auto* pred : curr->in) {
163-
if (seen.count(pred)) continue;
164-
seen.insert(pred);
165-
auto* lastSet = pred->contents.lastSets[index];
166-
if (lastSet) {
208+
if (pred->lastTraversedIteration == currentIteration) continue;
209+
pred->lastTraversedIteration = currentIteration;
210+
auto lastSet = std::find_if(pred->lastSets.begin(), pred->lastSets.end(), [&](std::pair<Index, SetLocal*>& value) { return value.first == index; });
211+
if (lastSet != pred->lastSets.end()) {
167212
// there is a set here, apply it, and stop the flow
168213
for (auto* get : gets) {
169-
getSetses[get].insert(lastSet);
214+
getSetses[get].insert(lastSet->second);
170215
}
171216
} else {
172217
// keep on flowing
@@ -176,6 +221,7 @@ struct Flower : public CFGWalker<Flower, Visitor<Flower>, Info> {
176221
}
177222
}
178223
gets.clear();
224+
currentIteration++;
179225
}
180226
}
181227
}

0 commit comments

Comments
 (0)