diff options
author | Loppin Vincent <vincent.loppin@gmail.com> | 2018-07-21 02:29:26 +0200 |
---|---|---|
committer | Alon Zakai <alonzakai@gmail.com> | 2018-07-20 17:29:26 -0700 |
commit | 7db96b6d0c93e3b92b396169506b5fd4f14a7dd3 (patch) | |
tree | a36e05f195adda66e343f6da1e773127f0dbab5c | |
parent | 25890f53cba4b9b05341ac146dde755a7fa4fddd (diff) | |
download | binaryen-7db96b6d0c93e3b92b396169506b5fd4f14a7dd3.tar.gz binaryen-7db96b6d0c93e3b92b396169506b5fd4f14a7dd3.tar.bz2 binaryen-7db96b6d0c93e3b92b396169506b5fd4f14a7dd3.zip |
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.
-rw-r--r-- | src/ir/LocalGraph.cpp | 82 |
1 files changed, 64 insertions, 18 deletions
diff --git a/src/ir/LocalGraph.cpp b/src/ir/LocalGraph.cpp index 77f5906f0..abbcf889f 100644 --- a/src/ir/LocalGraph.cpp +++ b/src/ir/LocalGraph.cpp @@ -50,7 +50,7 @@ struct Action { // information about a basic block struct Info { std::vector<Action> actions; // actions occurring in this block - std::vector<SetLocal*> lastSets; // for each index, the last set_local for it + std::unordered_map<Index, SetLocal*> lastSets; // for each index, the last set_local for it void dump(Function* func) { if (actions.empty()) return; @@ -76,11 +76,7 @@ struct Flower : public CFGWalker<Flower, Visitor<Flower>, Info> { } BasicBlock* makeBasicBlock() { - auto* ret = new BasicBlock(); - auto& lastSets = ret->contents.lastSets; - lastSets.resize(getFunction()->getNumLocals()); - std::fill(lastSets.begin(), lastSets.end(), nullptr); - return ret; + return new BasicBlock(); } // cfg traversal work @@ -103,12 +99,61 @@ struct Flower : public CFGWalker<Flower, Visitor<Flower>, Info> { } void flow(Function* func) { + // This block struct is optimized for this flow process (Minimal information, iteration index). + struct FlowBlock { + // last Traversed Iteration + // This value help us to find if this block has been seen while traversing blocks. + // We compare this value to the current iteration index in order to determine if we already process this block in the current iteration. + // This speed up the processing compared to unordered_set or other struct usage. (No need to reset internal values, lookup into container, ...) + size_t lastTraversedIteration; + std::vector<Action> actions; // actions occurring in this block + std::vector<FlowBlock*> in; + // for each index, the last set_local for it + // The unordered_map from BasicBlock is converted ther into a vector + // This speed up search as there are almost always fewer than 100 items + std::vector<std::pair<Index, SetLocal*>> lastSets; + }; + auto numLocals = func->getNumLocals(); std::vector<std::vector<GetLocal*>> allGets; allGets.resize(numLocals); - std::unordered_set<BasicBlock*> seen; - std::vector<BasicBlock*> work; - for (auto& block : basicBlocks) { + std::vector<FlowBlock*> work; + + + // Converts input blocks (basicBlocks) into more efficient blocks to improve memory access. + std::vector<FlowBlock> flowBlocks; + flowBlocks.resize(basicBlocks.size()); + + // Init mapping between basicblocks and flowBlocks + std::unordered_map<BasicBlock*, FlowBlock*> basicToFlowMap; + for (size_t i = 0; i < basicBlocks.size(); ++i) { + basicToFlowMap.emplace(std::make_pair(basicBlocks[i].get(), &flowBlocks[i])); + } + + FlowBlock* entryFlowBlock = nullptr; + for (size_t i = 0; i < flowBlocks.size(); ++i) { + auto& optBlock = flowBlocks[i]; + auto& inBlock = basicBlocks[i]; + // Get the equivalent block to entry in the flow list + if (inBlock.get() == entry) entryFlowBlock = &optBlock; + // Initialize iteration index to max size_t to ensure we don't miss a block from wrong value. + optBlock.lastTraversedIteration = -1; + optBlock.actions.swap(inBlock->contents.actions); + // Map in block to flow blocks + auto& inBlocks = inBlock->in; + optBlock.in.resize(inBlocks.size()); + std::transform(inBlocks.begin(), inBlocks.end(), optBlock.in.begin(), [&](BasicBlock* block) { return basicToFlowMap[block]; }); + + // Convert unordered_map to vector + optBlock.lastSets.reserve(inBlock->contents.lastSets.size()); + for (auto set : inBlock->contents.lastSets) { + optBlock.lastSets.emplace_back(std::make_pair(set.first, set.second)); + } + } + assert(entryFlowBlock != nullptr); + + size_t currentIteration = 0; + for (auto& block : flowBlocks) { #ifdef LOCAL_GRAPH_DEBUG std::cout << "basic block " << block.get() << " :\n"; for (auto& action : block->contents.actions) { @@ -120,7 +165,7 @@ struct Flower : public CFGWalker<Flower, Visitor<Flower>, Info> { #endif // go through the block, finding each get and adding it to its index, // and seeing how sets affect that - auto& actions = block->contents.actions; + auto& actions = block.actions; // move towards the front, handling things as we go for (int i = int(actions.size()) - 1; i >= 0; i--) { auto& action = actions[i]; @@ -142,8 +187,8 @@ struct Flower : public CFGWalker<Flower, Visitor<Flower>, Info> { for (Index index = 0; index < numLocals; index++) { auto& gets = allGets[index]; if (gets.empty()) continue; - work.push_back(block.get()); - seen.clear(); + work.push_back(&block); + // note that we may need to revisit the later parts of this initial // block, if we are in a loop, so don't mark it as seen while (!work.empty()) { @@ -152,7 +197,7 @@ struct Flower : public CFGWalker<Flower, Visitor<Flower>, Info> { // we have gone through this block; now we must handle flowing to // the inputs if (curr->in.empty()) { - if (curr == entry) { + if (curr == entryFlowBlock) { // these receive a param or zero init value for (auto* get : gets) { getSetses[get].insert(nullptr); @@ -160,13 +205,13 @@ struct Flower : public CFGWalker<Flower, Visitor<Flower>, Info> { } } else { for (auto* pred : curr->in) { - if (seen.count(pred)) continue; - seen.insert(pred); - auto* lastSet = pred->contents.lastSets[index]; - if (lastSet) { + if (pred->lastTraversedIteration == currentIteration) continue; + pred->lastTraversedIteration = currentIteration; + auto lastSet = std::find_if(pred->lastSets.begin(), pred->lastSets.end(), [&](std::pair<Index, SetLocal*>& value) { return value.first == index; }); + if (lastSet != pred->lastSets.end()) { // there is a set here, apply it, and stop the flow for (auto* get : gets) { - getSetses[get].insert(lastSet); + getSetses[get].insert(lastSet->second); } } else { // keep on flowing @@ -176,6 +221,7 @@ struct Flower : public CFGWalker<Flower, Visitor<Flower>, Info> { } } gets.clear(); + currentIteration++; } } } |