From: Kanishk Sugand Date: Tue, 23 Dec 2014 14:31:18 +0000 (-0500) Subject: mem: Add a stack distance calculator X-Git-Tag: stable_2015_04_15~53 X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=888975b29d920df9acdcf55db491d802e607cea6;p=gem5.git mem: Add a stack distance calculator This patch adds a stand-alone stack distance calculator. The stack distance calculator is a passive SimObject that observes the addresses passed to it. It calculates stack distances (LRU Distances) of incoming addresses based on the partial sum hierarchy tree algorithm described by Alamasi et al. http://doi.acm.org/10.1145/773039.773043. For each transaction a hashtable look-up is performed. At every non-unique transaction the tree is traversed from the leaf at the returned index to the root, the old node is deleted from the tree, and the sums (to the right) are collected and decremented. The collected sum represets the stack distance of the found node. At every unique transaction the stack distance is returned as numeric_limits::max(). In addition to the basic stack distance calculation, a feature to mark an old node in the tree is added. This is useful if it is required to see the reuse pattern. For example, Writebacks to the lower level (e.g. membus from L2), can be marked instead of being removed from the stack (isMarked flag of Node set to True). And then later if this same address is accessed (by L1), the value of the isMarked flag would be True. This gives some insight on how the Writeback policy of the lower level affect the read/write accesses in an application. Debugging is enabled by setting the verify flag to true. Debugging is implemented using a dummy stack that behaves in a naive way, using STL vectors. Note that this has a large impact on run time. --- diff --git a/src/mem/SConscript b/src/mem/SConscript index e6973b1ac..50f58add1 100644 --- a/src/mem/SConscript +++ b/src/mem/SConscript @@ -44,6 +44,7 @@ SimObject('ExternalMaster.py') SimObject('ExternalSlave.py') SimObject('MemObject.py') SimObject('SimpleMemory.py') +SimObject('StackDistCalc.py') SimObject('XBar.py') Source('abstract_mem.cc') @@ -64,6 +65,7 @@ Source('port_proxy.cc') Source('physical.cc') Source('simple_mem.cc') Source('snoop_filter.cc') +Source('stack_dist_calc.cc') Source('tport.cc') Source('xbar.cc') @@ -101,7 +103,7 @@ DebugFlag('LLSC') DebugFlag('MMU') DebugFlag('MemoryAccess') DebugFlag('PacketQueue') - +DebugFlag('StackDist') DebugFlag("DRAMSim2") DebugFlag("MemChecker") diff --git a/src/mem/StackDistCalc.py b/src/mem/StackDistCalc.py new file mode 100644 index 000000000..4986da9be --- /dev/null +++ b/src/mem/StackDistCalc.py @@ -0,0 +1,54 @@ +# Copyright (c) 2014 ARM Limited +# All rights reserved. +# +# The license below extends only to copyright in the software and shall +# not be construed as granting a license to any other intellectual +# property including but not limited to intellectual property relating +# to a hardware implementation of the functionality of the software +# licensed hereunder. You may use the software subject to the license +# terms below provided that you ensure that this notice is replicated +# unmodified and in its entirety in all distributions of the software, +# modified or unmodified, in source code or in binary form. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer; +# redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution; +# neither the name of the copyright holders nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# +# Authors: Andreas Hansson + +from m5.SimObject import SimObject +from m5.params import * + +class StackDistCalc(SimObject): + type = 'StackDistCalc' + cxx_header = "mem/stack_dist_calc.hh" + + # enable verification stack + verify = Param.Bool(False, "Verify behaviuor with reference implementation") + + # linear histogram bins and enable/disable + linear_hist_bins = Param.Unsigned('16', "Bins in linear histograms") + disable_linear_hists = Param.Bool(False, "Disable linear histograms") + + # logarithmic histogram bins and enable/disable + log_hist_bins = Param.Unsigned('32', "Bins in logarithmic histograms") + disable_log_hists = Param.Bool(False, "Disable logarithmic histograms") diff --git a/src/mem/stack_dist_calc.cc b/src/mem/stack_dist_calc.cc new file mode 100644 index 000000000..c273ee7f4 --- /dev/null +++ b/src/mem/stack_dist_calc.cc @@ -0,0 +1,670 @@ +/* + * Copyright (c) 2014 ARM Limited + * All rights reserved + * + * The license below extends only to copyright in the software and shall + * not be construed as granting a license to any other intellectual + * property including but not limited to intellectual property relating + * to a hardware implementation of the functionality of the software + * licensed hereunder. You may use the software subject to the license + * terms below provided that you ensure that this notice is replicated + * unmodified and in its entirety in all distributions of the software, + * modified or unmodified, in source code or in binary form. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Kanishk Sugand + */ + +#include "base/intmath.hh" +#include "base/trace.hh" +#include "debug/StackDist.hh" +#include "mem/stack_dist_calc.hh" + +StackDistCalc::StackDistCalc(const StackDistCalcParams* p) : + SimObject(p), index(0), verifyStack(p->verify), + disableLinearHists(p->disable_linear_hists), + disableLogHists(p->disable_log_hists) +{ + // Instantiate a new root and leaf layer + // Map type variable, representing a layer in the tree + IndexNodeMap tree_level; + + // Initialize tree count for leaves + nextIndex.push_back(0); + + // Add the initial leaf layer to the tree + tree.push_back(tree_level); + + // Create a root node. Node type variable in the topmost layer + Node* root_node = new Node(); + + // Initialize tree count for root + nextIndex.push_back(1); + + // Add the empty root layer to the tree + tree.push_back(tree_level); + + // Add the initial root to the tree + tree[1][root_node->nodeIndex] = root_node; +} + +StackDistCalc::~StackDistCalc() +{ + // Walk through each layer and destroy the nodes + for (auto& layer : tree) { + for (auto& index_node : layer) { + // each map entry contains an index and a node + delete index_node.second; + } + // Clear each layer in the tree + layer.clear(); + } + + // Clear the tree + tree.clear(); + aiMap.clear(); + nextIndex.clear(); + + // For verification + stack.clear(); +} + +void +StackDistCalc::update(const MemCmd& cmd, Addr addr) +{ + // only capturing read and write requests (which allocate in the + // cache) + if (cmd.isRead() || cmd.isWrite()) { + auto returnType = calcStackDistAndUpdate(addr); + + uint64_t stackDist = returnType.first; + + if (stackDist != Infinity) { + // Sample the stack distance of the address in linear bins + if (!disableLinearHists) { + if (cmd.isRead()) + readLinearHist.sample(stackDist); + else + writeLinearHist.sample(stackDist); + } + + if (!disableLogHists) { + int stackDistLog2 = stackDist == 0 ? 1 : floorLog2(stackDist); + + // Sample the stack distance of the address in log bins + if (cmd.isRead()) + readLogHist.sample(stackDistLog2); + else + writeLogHist.sample(stackDistLog2); + } + } + } +} + +// The updateSum method is a recursive function which updates +// the node sums till the root. It also deletes the nodes that +// are not used anymore. +uint64_t +StackDistCalc::updateSum(Node* node, bool from_left, + uint64_t sum_from_below, uint64_t level, + uint64_t stack_dist, bool discard_node) +{ + ++level; + + // Make a copy of the node variables and work on them + // as a node may be deleted by this function + uint64_t node_sum_l = node->sumLeft; + uint64_t node_sum_r = node->sumRight; + bool node_left = node->isLeftNode; + bool node_discard_left = node->discardLeft; + bool node_discard_right = node->discardRight; + uint64_t node_n_index = node->nodeIndex; + Node* node_parent_ptr = node->parent; + + // For verification + if (verifyStack) { + // This sanity check makes sure that the left_sum and + // right_sum of the node is not greater than the + // maximum possible value given by the leaves below it + // for example a node in layer 3 (tree[3]) can at most + // have 8 leaves (4 to the left and 4 to the right) + // thus left_sum and right_sum should be <= 4 + panic_if(node_sum_l > (1 << (level - 1)), + "Error in sum left of level %ul, node index %ull, " + "Sum = %ull \n", level, node_n_index, node_sum_l); + + panic_if(node_sum_r > (1 << (level - 1)), + "Error in sum right of level %ul, node index %ull, " + "Sum = %ull \n", level, node_n_index, node_sum_r); + } + + // Update the left sum or the right sum depending on the + // from_left flag. Variable stack_dist is updated only + // when arriving from Left. + if (from_left) { + // update sumLeft + node_sum_l = sum_from_below; + stack_dist += node_sum_r; + } else { + // update sum_r + node_sum_r = sum_from_below; + } + + // sum_from_below == 0 can be a leaf discard operation + if (discard_node && !sum_from_below) { + if (from_left) + node_discard_left = true; + else + node_discard_right = true; + } + + // Update the node variables with new values + node->nodeIndex = node_n_index; + node->sumLeft = node_sum_l; + node->sumRight = node_sum_r; + node->isLeftNode = node_left; + node->discardLeft = node_discard_left; + node->discardRight = node_discard_right; + + // Delete the node if it is not required anymore + if (node_discard_left && node_discard_right && + discard_node && node_parent_ptr && !sum_from_below) { + delete node; + tree[level].erase(node_n_index); + discard_node = true; + } else { + // propogate discard_node as false upwards if the + // above conditions are not met. + discard_node = false; + } + + // Recursively call the updateSum operation till the + // root node is reached + if (node_parent_ptr) { + stack_dist = updateSum(node_parent_ptr, node_left, + node_sum_l + node_sum_r, + level, stack_dist, discard_node); + } + + return stack_dist; +} + +// This function is called by the calcStackDistAndUpdate function +// If is_new_leaf is true then a new leaf is added otherwise a leaf +// removed from the tree. In both cases the tree is updated using +// the updateSum operation. +uint64_t +StackDistCalc::updateSumsLeavesToRoot(Node* node, bool is_new_leaf) +{ + uint64_t level = 0; + uint64_t stack_dist = 0; + + if (is_new_leaf) { + node->sumLeft = 1; + updateSum(node->parent, + node->isLeftNode, node->sumLeft, + level, 0, false); + + stack_dist = Infinity; + } else { + node->sumLeft = 0; + stack_dist = updateSum(node->parent, + node->isLeftNode, 0, + level, stack_dist, true); + } + + return stack_dist; +} + +// This method is a recursive function which calculates +// the node sums till the root. +uint64_t +StackDistCalc::getSum(Node* node, bool from_left, uint64_t sum_from_below, + uint64_t stack_dist, uint64_t level) const +{ + ++level; + // Variable stack_dist is updated only + // when arriving from Left. + if(from_left) { + stack_dist += node->sumRight; + } + + // Recursively call the getSum operation till the + // root node is reached + if(node->parent) { + stack_dist = getSum(node->parent, node->isLeftNode, + node->sumLeft + node->sumRight, + stack_dist, level); + } + + return stack_dist; +} + +// This function is called by the calcStackDistance function +uint64_t +StackDistCalc::getSumsLeavesToRoot(Node* node) const +{ + return getSum(node->parent, node->isLeftNode, 0, 0, 0); +} + +// Update tree is a tree balancing operation which maintains +// the binary tree structure. This method is called whenever +// index%2 == 0 (i.e. every alternate cycle) +// The two main operation are : +// OP1. moving the root node one layer up if index counter +// crosses power of 2 +// OP2. Addition of intermediate nodes as and when required +// and linking them to their parents in the layer above. +void +StackDistCalc::updateTree() +{ + uint64_t i; + + if (isPowerOf2(index)) { + // OP1. moving the root node one layer up if index counter + // crosses power of 2 + // If index counter crosses a power of 2, then add a + // new tree layer above and create a new Root node in it. + // After the root is created the old node + // in the layer below is updated to point to this + // newly created root node. The sum_l of this new root node + // becomes the sum_l + sum_r of the old node. + // + // After this root update operation a chain of intermediate + // nodes is created from root layer to tree[1](one layer + // above the leaf layer) + + // Create a new root node + Node* newRootNode = new Node(); + + // Update its sum_l as the sum_l+sum_r from below + newRootNode->sumLeft = tree[getTreeDepth()][0]->sumRight + + tree[getTreeDepth()][0]->sumLeft; + // Update its discard left flag if the node below has + // has both discardLeft and discardRight set. + newRootNode->discardLeft = tree[getTreeDepth()][0]->discardLeft && + tree[getTreeDepth()][0]->discardRight; + + // Map type variable, representing a layer in the tree + IndexNodeMap treeLevel; + // Add a new layer to the tree + tree.push_back(treeLevel); + nextIndex.push_back(1); + tree[getTreeDepth()][newRootNode->nodeIndex] = newRootNode; + + // Update the parent pointer at lower layer to + // point to newly created root node + tree[getTreeDepth() - 1][0]->parent = tree[getTreeDepth()][0]; + + // Add intermediate nodes from root till bottom (one layer above the + // leaf layer) + for (i = getTreeDepth() - 1; i >= 1; --i) { + Node* newINode = new Node(); + // newNode is left or right child depending on the number of nodes + // in that layer + if (nextIndex[i] % 2 == 0) { + newINode->isLeftNode = true; + } else { + newINode->isLeftNode = false; + } + + newINode->parent = tree[i + 1][nextIndex[i + 1] - 1]; + newINode->nodeIndex = ++nextIndex[i] - 1; + tree[i][newINode->nodeIndex] = newINode; + } + } else { + // OP2. Addition of intermediate nodes as and when required + // and linking them to their parents in the layer above. + // + // At layer 1 a new INode is added whenever index%(2^1)==0 + // (multiples of 2) + // + // At layer 2 a new INode is added whenever index%(2^2)==0 + // (multiples of 4) + // + // At layer 3 a new INode is added whenever index%(2^3)==0 + // (multiples of 8) + //... + // + // At layer N a new INode is added whenever index%(2^N)==0 + // (multiples of 2^N) + for (i = getTreeDepth() - 1; i >= 1; --i) { + // Traverse each layer from root to leaves and add a new + // intermediate node if required. Link the parent_ptr of + // the new node to the parent in the above layer. + + if ((index % (1 << i)) == 0) { + // Checks if current (index % 2^treeDepth) == 0 if true + // a new node at that layer is created + Node* newINode = new Node(); + + // newNode is left or right child depending on the + // number of nodes in that layer. + if (nextIndex[i] % 2 == 0) { + newINode->isLeftNode = true; + } else { + newINode->isLeftNode = false; + } + + // Pointing to its parent in the upper layer + newINode->parent = tree[i + 1][nextIndex[i + 1] - 1]; + newINode->nodeIndex = ++nextIndex[i] - 1; + tree[i][newINode->nodeIndex] = newINode; + } + } + } +} + +// This function is called everytime to get the stack distance and add +// a new node. A feature to mark an old node in the tree is +// added. This is useful if it is required to see the reuse +// pattern. For example, BackInvalidates from the lower level (Membus) +// to L2, can be marked (isMarked flag of Node set to True). And then +// later if this same address is accessed by L1, the value of the +// isMarked flag would be True. This would give some insight on how +// the BackInvalidates policy of the lower level affect the read/write +// accesses in an application. +std::pair< uint64_t, bool> +StackDistCalc::calcStackDistAndUpdate(const Addr r_address, bool addNewNode) +{ + Node* newLeafNode; + // Return index if the address was already present in stack + uint64_t r_index = index; + + auto ai = aiMap.lower_bound(r_address); + + // Default value of flag indicating as the left or right leaf + bool isLeft = true; + // Default value of isMarked flag for each node. + bool _mark = false; + // By default stackDistacne is treated as infinity + uint64_t stack_dist; + + // Lookup aiMap by giving address as the key: + // If found take address and Lookup in tree + // Update tree from leaves by making B(found index) = 0 + // Add sums to right till root while Updating them + // Stack Distance of that address sums to right + if (ai != aiMap.end() && !(aiMap.key_comp()(r_address, ai->first))) { + // key already exists + // save the index counter value when this address was + // encountered before and update it to the current index value + r_index = ai->second; + + if (addNewNode) { + // Update aiMap aiMap(Address) = current index + ai->second = index; + } else { + aiMap.erase(r_address); + } + + // Call update tree operation on the tree starting with + // the r_index value found above. This function would return + // the value of the stack distcance. + stack_dist = updateSumsLeavesToRoot(tree[0][r_index], false); + newLeafNode = tree[0][r_index]; + // determine if this node was marked earlier + _mark = newLeafNode->isMarked; + delete newLeafNode; + tree[0].erase(r_index); + } else { + if (addNewNode) { + // Update aiMap aiMap(Address) = current index + aiMap[r_address] = index; + } + // Update infinity bin count + // By default stackDistacne is treated as infinity + stack_dist = Infinity; + } + + if (addNewNode) { + // If index%2 == 0 then update tree + if (index % 2 == 0) { + updateTree(); + } else { + // At odd values of index counter, a new right-type node is + // added to the leaf layer, else a left-type node is added + isLeft = false; + } + + // Add new leaf node in the leaf layer (tree[0]) + // set n_index = current index + newLeafNode = new Node(); + ++nextIndex[0]; + newLeafNode->nodeIndex=index; + newLeafNode->isLeftNode=isLeft; + // Point the parent pointer to the intermediate node above + newLeafNode->parent = tree[1][nextIndex[1] - 1]; + tree[0][index] = newLeafNode; + // call an update operation which would update the tree after + // addition of this new leaf node. + updateSumsLeavesToRoot(tree[0][index], true); + + // For verification + if (verifyStack) { + // This function checks the sanity of the tree to make sure the + // last node in the link of parent pointers is the root node. + // It takes a leaf node as an argument and traveses upwards till + // the root layer to check if the last parent is null + sanityCheckTree(tree[0][index]); + + // Push the same element in debug stack, and check + uint64_t verify_stack_dist = verifyStackDist(r_address, true); + panic_if(verify_stack_dist != stack_dist, + "Expected stack-distance for address \ + %#lx is %#lx but found %#lx", + r_address, verify_stack_dist, stack_dist); + printStack(); + } + + // The index counter is updated at the end of each transaction + // (unique or non-unique) + ++index; + } + + return (std::make_pair(stack_dist, _mark)); +} + +// This function is called everytime to get the stack distance +// no new node is added. It can be used to mark a previous access +// and inspect the value of the mark flag. +std::pair< uint64_t, bool> +StackDistCalc::calcStackDist(const Addr r_address, bool mark) +{ + // Return index if the address was already present in stack + uint64_t r_index = index; + // Default value of isMarked flag for each node. + bool _mark = false; + + auto ai = aiMap.lower_bound(r_address); + + // By default stackDistacne is treated as infinity + uint64_t stack_dist = 0; + + // Lookup aiMap by giving address as the key: + // If found take address and Lookup in tree + // Add sums to right till root + // Stack Distance of that address sums to right + if (ai != aiMap.end() && !(aiMap.key_comp()(r_address, ai->first))) { + // key already exists + // save the index counter value when this address was + // encountered before + r_index = ai->second; + + // Get the value of mark flag if previously marked + _mark = tree[0][r_index]->isMarked; + // Mark the leaf node if required + tree[0][r_index]->isMarked = mark; + + // Call get sums operation on the tree starting with + // the r_index value found above. This function would return + // the value of the stack distcance. + stack_dist = getSumsLeavesToRoot(tree[0][r_index]); + } else { + // Update infinity bin count + // By default stackDistacne is treated as infinity + stack_dist = Infinity; + } + + // For verification + if (verifyStack) { + // Calculate the SD of the same address in the debug stack + uint64_t verify_stack_dist = verifyStackDist(r_address); + panic_if(verify_stack_dist != stack_dist, + "Expected stack-distance for address \ + %#lx is %#lx but found %#lx", + r_address, verify_stack_dist, stack_dist); + + printStack(); + } + + return std::make_pair(stack_dist, _mark); +} + +// For verification +// Simple sanity check for the tree +void +StackDistCalc::sanityCheckTree(const Node* node, uint64_t level) const +{ + const Node* next_up = node->parent; + + for (uint64_t i = level + 1; i < getTreeDepth() - level; ++i) { + next_up = next_up->parent; + panic_if(!next_up, "Sanity check failed for node %ull \n", + node->nodeIndex); + } + + // At the root layer the parent_ptr should be null + panic_if(next_up->parent, "Sanity check failed for node %ull \n", + node->nodeIndex); +} + +// This method can be called to compute the stack distance in a naive +// way It can be used to verify the functionality of the stack +// distance calculator. It uses std::vector to compute the stack +// distance using a naive stack. +uint64_t +StackDistCalc::verifyStackDist(const Addr r_address, bool update_stack) +{ + bool found = false; + uint64_t stack_dist = 0; + auto a = stack.rbegin(); + + for (; a != stack.rend(); ++a) { + if (*a == r_address) { + found = true; + break; + } else { + ++stack_dist; + } + } + + if (found) { + ++a; + if (update_stack) + stack.erase(a.base()); + } else { + stack_dist = Infinity; + } + + if (update_stack) + stack.push_back(r_address); + + return stack_dist; +} + +// This method is useful to print top n entities in the stack. +void +StackDistCalc::printStack(int n) const +{ + Node* node; + uint64_t r_index; + int count = 0; + + DPRINTF(StackDist, "Printing last %d entries in tree\n", n); + + // Walk through leaf layer to display the last n nodes + for (auto it = tree[0].rbegin(); (count < n) && (it != tree[0].rend()); + ++it, ++count) { + node = it->second; + r_index = node->nodeIndex; + + // Lookup aiMap using the index returned by the leaf iterator + for (auto ai = aiMap.rbegin(); ai != aiMap.rend(); ++ai) { + if (ai->second == r_index) { + DPRINTF(StackDist,"Tree leaves, Rightmost-[%d] = %#lx\n", + count, ai->first); + break; + } + } + } + + DPRINTF(StackDist,"Tree depth = %#ld\n", getTreeDepth()); + + if (verifyStack) { + DPRINTF(StackDist,"Printing Last %d entries in VerifStack \n", n); + count = 0; + for (auto a = stack.rbegin(); (count < n) && (a != stack.rend()); + ++a, ++count) { + DPRINTF(StackDist, "Verif Stack, Top-[%d] = %#lx\n", count, *a); + } + } +} + +void +StackDistCalc::regStats() +{ + using namespace Stats; + + readLinearHist + .init(params()->linear_hist_bins) + .name(name() + ".readLinearHist") + .desc("Reads linear distribution") + .flags(disableLinearHists ? nozero : pdf); + + readLogHist + .init(params()->log_hist_bins) + .name(name() + ".readLogHist") + .desc("Reads logarithmic distribution") + .flags(disableLogHists ? nozero : pdf); + + writeLinearHist + .init(params()->linear_hist_bins) + .name(name() + ".writeLinearHist") + .desc("Writes linear distribution") + .flags(disableLinearHists ? nozero : pdf); + + writeLogHist + .init(params()->log_hist_bins) + .name(name() + ".writeLogHist") + .desc("Writes logarithmic distribution") + .flags(disableLogHists ? nozero : pdf); +} + +StackDistCalc* +StackDistCalcParams::create() +{ + return new StackDistCalc(this); +} diff --git a/src/mem/stack_dist_calc.hh b/src/mem/stack_dist_calc.hh new file mode 100644 index 000000000..881b71179 --- /dev/null +++ b/src/mem/stack_dist_calc.hh @@ -0,0 +1,454 @@ +/* + * Copyright (c) 2014 ARM Limited + * All rights reserved + * + * The license below extends only to copyright in the software and shall + * not be construed as granting a license to any other intellectual + * property including but not limited to intellectual property relating + * to a hardware implementation of the functionality of the software + * licensed hereunder. You may use the software subject to the license + * terms below provided that you ensure that this notice is replicated + * unmodified and in its entirety in all distributions of the software, + * modified or unmodified, in source code or in binary form. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Kanishk Sugand + * Andreas Hansson + */ + +#ifndef __MEM_STACK_DIST_CALC_HH__ +#define __MEM_STACK_DIST_CALC_HH__ + +#include +#include + +#include "base/types.hh" +#include "mem/packet.hh" +#include "params/StackDistCalc.hh" +#include "sim/sim_object.hh" +#include "sim/stats.hh" + +/** + * The stack distance calculator is a passive object that merely + * observes the addresses pass to it. It calculates stack distances + * of incoming addresses based on the partial sum hierarchy tree + * algorithm described by Alamasi et al. + * http://doi.acm.org/10.1145/773039.773043. + * + * A tree structure is maintained and updated at each transaction + * (unique or non-unique). The tree is implemented as an STL vector + * with layers of the form Each layer in this tree is an + * ordered map . Nodes are structs which take form + * of leaf, intermediate and root nodes. For example, in a tree with 3 + * layers, tree[0][5] gives a leaf node pointer for key=5 tree[1][1] + * gives an intermediate node pointer for key=1 tree[2][0] gives the + * root node in the tree. + * + * At every transaction a hash-map (aiMap) is looked up to check if + * the address was already encountered before. Based on this lookup a + * transaction can be termed as unique or non-unique. + * + * In addition to the normal stack distance calculation, a feature to + * mark an old node in the tree is added. This is useful if it is + * required to see the reuse pattern. For example, BackInvalidates + * from a lower level (e.g. membus to L2), can be marked (isMarked + * flag of Node set to True). Then later if this same address is + * accessed (by L1), the value of the isMarked flag would be + * True. This would give some insight on how the BackInvalidates + * policy of the lower level affect the read/write accesses in an + * application. + * + * There are two functions provided to interface with the calculator: + * 1. pair calcStackDistAndUpdate(Addr r_address, + * bool addNewNode) + * At every unique transaction a new leaf node is added at tree[0](leaf layer) + * and linked to the layer above (if addNewNode is True). The sums of all + * the intermediate nodes is updated till the root. The stack-distance is + * returned as a Constant representing INFINITY. + * + * At every non-unique transaction the tree is traversed from the + * leaf at the returned index to the root, the old node is deleted + * from the tree, and the sums (to the right are collected) and + * decremented. The collected sum represets the stack distance of the + * found node. If this node was marked then a bool flag set to True + * is returned with the stack_distance. During this operation a node + * is discarded at the leaf layer always. Moreover during the + * traversal upwards using the updateSum() method, if an intermediate + * node is found with no children connected to it, then that is + * discarded too. + * + * The return value of this function is a pair representing the + * stack_distance and the value of the marked flag. + * + * 2. pair calcStackDist(Addr r_address, bool mark) + * This is a stripped down version of the above function which is used to + * just inspect the tree, and mark a leaf node (if mark flag is set). The + * functionality to add a new node is removed. + * + * At every unique transaction the stack-distance is returned as a constant + * representing INFINITY. + * + * At every non-unique transaction the tree is traversed from the + * leaf at the returned index to the root, and the sums (to the right) + * are collected. The collected sum represets the stack distance of + * the found node. + * + * This function does NOT Modify the stack. (No node is added or + * deleted). It is just used to mark a node already created and get + * its stack distance. + * + * The return value of this function is a pair representing the stack + * distance and the value of the marked flag. + * + * The table below depicts the usage of the Algorithm using the functions: + * pair calcStackDistAndUpdate + * (Addr r_address, bool addNewNode) + * pair calcStackDist + * (Addr r_address, bool mark) + * + * | Function | Arguments |Return Val |Use For| + * |calcStackDistAndUpdate|r_address, True|I/SD,False |A,GD,GM| + * |calcStackDistAndUpdate|r_address,False|SD,prevMark|D,GD,GM| + * |calcStackDist |r_address,False|SD,prevMark| GD,GM| + * |calcStackDist |r_address, True|SD,prevMark| GD,GM| + * + * (*A: Allocate an address in stack, if old entry present then it is deleted, + * *U: Delete old-address from stack, no new entry is added + * *GD: Get-Stack distance of an address, + * *GM: Get value of Mark flag, indicates if that address has been touched + * before, + * *I: stack-distance = infinity, + * *SD: Stack Distance + * *r_address: address to be added, *prevMark: value of isMarked flag + * of the Node) + * + * Invalidates refer to a type of packet that removes something from + * a cache, either autonoumously (due-to cache's own replacement + * policy), or snoops from other caches which invalidate something + * inside our cache. + * + * Usage | Function to use |Typical Use | + * Add new entry |calcStackDistAndUpdate|Read/Write Allocate | + * Delete Old Entry |calcStackDistAndUpdate|Writebacks/Cleanevicts| + * Dist.of Old entry|calcStackDist |Cleanevicts/Invalidate| + * + * Node Balancing: The tree structure is maintained by an + * updateTree() operation called when an intermediate node is + * required. The update operation is roughly categorized as a root + * update or intermediate layer update. When number of leaf nodes + * grow over a power of 2 then a new layer is added at the top of the + * tree and a new root node is initialized. The old node at the lower + * layer is connected to this. In an intermediate node update + * operation a new intermediate node is added to the required layer. + * + * Debugging: Debugging can be enabled by setting the verifyStack flag + * true. Debugging is implemented using a dummy stack that behaves in + * a naive way, using STL vectors (i.e each unique address is pushed + * on the top of an STL vector stack, and SD is returned as + * Infinity. If a non unique address is encountered then the previous + * entry in the STL vector is removed, all the entities above it are + * pushed down, and the address is pushed at the top of the stack). + * + * A printStack(int numOfEntitiesToPrint) is provided to print top n entities + * in both (tree and STL based dummy stack). + */ +class StackDistCalc : public SimObject +{ + + private: + + struct Node; + + typedef std::map IndexNodeMap; + typedef std::map AddressIndexMap; + typedef std::vector TreeType; + + /** + * Gets sum from the node upwards recursively till the root. This + * function is called first by getSumsLeavesToRoot, and then + * recursively calls itself. + * + * @param node pointer to the node which is updated + * @param from_left variable which says that the request arrived + * from the left + * @param sum_from_below Sum of left and right children below + * @param level level in the tree the calling node is located + * @param stack_dist stack distance of the node below + * @return The stack distance of the current address. + * + */ + uint64_t getSum(Node* node, bool from_left, uint64_t sum_from_below, + uint64_t stack_dist, uint64_t level) const; + + /** + * Gets the sum from the leaf node specified. This function + * is called by calcStackDist. + * + * @param node pointer to the node which is updated + * @return The stack distance of the current address. + * + */ + uint64_t getSumsLeavesToRoot(Node* node) const; + + /** + * Updates the nodes upwards recursively till the root. + * This function is first called by updateSumsLeavesToRoot, + * and then it recursively calls itself. + * + * @param node pointer to the node which is updated + * @param from_left variable which says that the request arrived + * from the left + * @param sum_from_below Sum of left and right children below + * @param level level in the tree the calling node is located + * @param stack_dist stack distance of the node below + * @param discard_node whether the calling node was discarded or not + * @return The stack distance of the current address. + * + */ + uint64_t updateSum(Node* node, + bool from_left, uint64_t sum_from_below, uint64_t level, + uint64_t stack_dist, bool discard_node); + + /** + * Updates the leaf nodes and nodes above. This function is + * called by the calcStackDistAndUpdate. + * + * @param node pointer to the node which is updated + * @param is_new_leaf is true if this is a newly added node + * @return The stack distance of the current address. + * + */ + uint64_t updateSumsLeavesToRoot(Node* node, bool is_new_leaf); + + /** + * updateTree is a tree balancing operation, which maintains the + * binary tree structure. + * This method is called whenever index%2 == 0 (i.e. every + * alternate cycle) The two main operation are : + * OP1. Moving the root node one layer up if index counter + * crosses power of 2 + * OP2. Addition of intermediate nodes as and when required + * and linking them to their parents in the layer above. + */ + void updateTree(); + + /** + * This method is used for verification purposes + * It recursively traverses upwards from the given node till + * the root to check if the ultimate parent node (root-node) points + * to null. + * + * @param node pointer to the node whose sanity is being checked + * @param level the level at which this node is located in the tree + * + */ + void sanityCheckTree(const Node* node, uint64_t level = 0) const; + + /** + * A convenient way of refering to infinity. + */ + static constexpr uint64_t Infinity = std::numeric_limits::max(); + + /** + * Process the given address. If Mark is true then set the + * mark flag of the leaf node. + * This function returns the stack distance of the incoming + * address and the previous status of the mark flag. + * + * @param r_address The current address to process + * @param mark set the mark flag for the address. + * @return The stack distance of the current address and the mark flag. + */ + std::pair calcStackDist(const Addr r_address, + bool mark = false); + + /** + * Process the given address: + * - Lookup the tree for the given address + * - delete old node if found in tree + * - add a new node (if addNewNode flag is set) + * This function returns the stack distance of the incoming + * address and the status of the mark flag. + * + * @param r_address The current address to process + * @param addNewNode If true, a new node is added to the tree + * @return The stack distance of the current address and the mark flag. + */ + std::pair calcStackDistAndUpdate(const Addr r_address, + bool addNewNode = true); + + /** + * Return the counter for address accesses (unique and + * non-unique). This is further used to dump stats at + * regular intervals. + * + * @return The stack distance of the current address. + */ + uint64_t getIndex() const { return index; } + + /** + * Query depth of the tree (tree[0] represents leaf layer while + * tree[treeDepth] represents the root layer, all layers in + * between contain intermediate nodes) + * + * @return Tree depth + */ + uint64_t getTreeDepth() const { return tree.size() - 1; } + + /** + * Print the last n items on the stack. + * This method prints top n entries in the tree based implementation as + * well as dummy stack. + * @param n Number of entries to print + */ + void printStack(int n = 5) const; + + /** + * This is an alternative implementation of the stack-distance + * in a naive way. It uses simple STL vector to represent the stack. + * It can be used in parallel for debugging purposes. + * It is 10x slower than the tree based implemenation. + * + * @param r_address The current address to process + * @param update_stack Flag to indicate if stack should be updated + * @return Stack distance which is calculated by this alternative + * implementation + * + */ + uint64_t verifyStackDist(const Addr r_address, + bool update_stack = false); + + public: + + /** + * Convenience method to get the params when registering stats. + */ + const StackDistCalcParams* params() const + { return reinterpret_cast(_params); } + + StackDistCalc(const StackDistCalcParams* p); + + ~StackDistCalc(); + + void regStats(); + + /** + * Update the tree and the statistics. + * + * @param cmd Command from the packet + * @param addr Address to put on the stack + */ + void update(const MemCmd& cmd, Addr addr); + + private: + + /** + * Node which takes form of Leaf, INode or Root + */ + struct Node{ + // Sum of the left children + uint64_t sumLeft; + + // Sum of the right children + uint64_t sumRight; + + // Flag to indicate that sumLeft has gone from non-zero value to 0 + bool discardLeft; + + // Flag to indicate that sumRight has gone from non-zero value to 0 + bool discardRight; + + // Index of the current element in the Map + uint64_t nodeIndex; + + // Pointer to the parent + Node* parent; + + // Flag to mark the node as the right/left child + bool isLeftNode; + + /** + * Flag to indicate if this address is marked. Used in case + * where stack distance of a touched address is required. + */ + bool isMarked; + + /** + * The discard flags are false by default they become true if + * the node is reached again in a future lookup. + */ + Node() : sumLeft(0), sumRight(0), discardLeft(false), + discardRight(false), nodeIndex(0), + parent(nullptr), isLeftNode(true), isMarked(false) + { } + }; + + /** + * Internal counter for address accesses (unique and non-unique) + * This counter increments everytime the calcStackDist() method is + * called. This counter is used as a key for the hash- map at the + * leaf layer. Practically at every call to the calculator this + * counter is incremented and a new leaf node is added in the tree + * at the leaf layer using this counter value as the key. + */ + uint64_t index; + + // Binary tree of partial sums + TreeType tree; + + // Hash map which returns last seen index of each address + AddressIndexMap aiMap; + + // Keeps count of number of the next unique index for each + // level in the tree + std::vector nextIndex; + + // Dummy Stack for verification + std::vector stack; + + // Flag to enable verification of stack. (Slows down the simulation) + const bool verifyStack; + + // Disable the linear histograms + const bool disableLinearHists; + + // Disable the logarithmic histograms + const bool disableLogHists; + + // Reads linear histogram + Stats::Histogram readLinearHist; + + // Reads logarithmic histogram + Stats::SparseHistogram readLogHist; + + // Writes linear histogram + Stats::Histogram writeLinearHist; + + // Writes logarithmic histogram + Stats::SparseHistogram writeLogHist; + +}; + +#endif //__STACK_DIST_CALC_HH__