stack_dist_calc.cc revision 10614:da37aec3ed1a
1/* 2 * Copyright (c) 2014 ARM Limited 3 * All rights reserved 4 * 5 * The license below extends only to copyright in the software and shall 6 * not be construed as granting a license to any other intellectual 7 * property including but not limited to intellectual property relating 8 * to a hardware implementation of the functionality of the software 9 * licensed hereunder. You may use the software subject to the license 10 * terms below provided that you ensure that this notice is replicated 11 * unmodified and in its entirety in all distributions of the software, 12 * modified or unmodified, in source code or in binary form. 13 * 14 * Redistribution and use in source and binary forms, with or without 15 * modification, are permitted provided that the following conditions are 16 * met: redistributions of source code must retain the above copyright 17 * notice, this list of conditions and the following disclaimer; 18 * redistributions in binary form must reproduce the above copyright 19 * notice, this list of conditions and the following disclaimer in the 20 * documentation and/or other materials provided with the distribution; 21 * neither the name of the copyright holders nor the names of its 22 * contributors may be used to endorse or promote products derived from 23 * this software without specific prior written permission. 24 * 25 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 26 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 27 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 28 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 29 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 30 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 31 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 32 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 33 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 34 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 35 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 36 * 37 * Authors: Kanishk Sugand 38 */ 39 40#include "base/intmath.hh" 41#include "base/trace.hh" 42#include "debug/StackDist.hh" 43#include "mem/stack_dist_calc.hh" 44 45StackDistCalc::StackDistCalc(const StackDistCalcParams* p) : 46 SimObject(p), index(0), verifyStack(p->verify), 47 disableLinearHists(p->disable_linear_hists), 48 disableLogHists(p->disable_log_hists) 49{ 50 // Instantiate a new root and leaf layer 51 // Map type variable, representing a layer in the tree 52 IndexNodeMap tree_level; 53 54 // Initialize tree count for leaves 55 nextIndex.push_back(0); 56 57 // Add the initial leaf layer to the tree 58 tree.push_back(tree_level); 59 60 // Create a root node. Node type variable in the topmost layer 61 Node* root_node = new Node(); 62 63 // Initialize tree count for root 64 nextIndex.push_back(1); 65 66 // Add the empty root layer to the tree 67 tree.push_back(tree_level); 68 69 // Add the initial root to the tree 70 tree[1][root_node->nodeIndex] = root_node; 71} 72 73StackDistCalc::~StackDistCalc() 74{ 75 // Walk through each layer and destroy the nodes 76 for (auto& layer : tree) { 77 for (auto& index_node : layer) { 78 // each map entry contains an index and a node 79 delete index_node.second; 80 } 81 // Clear each layer in the tree 82 layer.clear(); 83 } 84 85 // Clear the tree 86 tree.clear(); 87 aiMap.clear(); 88 nextIndex.clear(); 89 90 // For verification 91 stack.clear(); 92} 93 94void 95StackDistCalc::update(const MemCmd& cmd, Addr addr) 96{ 97 // only capturing read and write requests (which allocate in the 98 // cache) 99 if (cmd.isRead() || cmd.isWrite()) { 100 auto returnType = calcStackDistAndUpdate(addr); 101 102 uint64_t stackDist = returnType.first; 103 104 if (stackDist != Infinity) { 105 // Sample the stack distance of the address in linear bins 106 if (!disableLinearHists) { 107 if (cmd.isRead()) 108 readLinearHist.sample(stackDist); 109 else 110 writeLinearHist.sample(stackDist); 111 } 112 113 if (!disableLogHists) { 114 int stackDistLog2 = stackDist == 0 ? 1 : floorLog2(stackDist); 115 116 // Sample the stack distance of the address in log bins 117 if (cmd.isRead()) 118 readLogHist.sample(stackDistLog2); 119 else 120 writeLogHist.sample(stackDistLog2); 121 } 122 } 123 } 124} 125 126// The updateSum method is a recursive function which updates 127// the node sums till the root. It also deletes the nodes that 128// are not used anymore. 129uint64_t 130StackDistCalc::updateSum(Node* node, bool from_left, 131 uint64_t sum_from_below, uint64_t level, 132 uint64_t stack_dist, bool discard_node) 133{ 134 ++level; 135 136 // Make a copy of the node variables and work on them 137 // as a node may be deleted by this function 138 uint64_t node_sum_l = node->sumLeft; 139 uint64_t node_sum_r = node->sumRight; 140 bool node_left = node->isLeftNode; 141 bool node_discard_left = node->discardLeft; 142 bool node_discard_right = node->discardRight; 143 uint64_t node_n_index = node->nodeIndex; 144 Node* node_parent_ptr = node->parent; 145 146 // For verification 147 if (verifyStack) { 148 // This sanity check makes sure that the left_sum and 149 // right_sum of the node is not greater than the 150 // maximum possible value given by the leaves below it 151 // for example a node in layer 3 (tree[3]) can at most 152 // have 8 leaves (4 to the left and 4 to the right) 153 // thus left_sum and right_sum should be <= 4 154 panic_if(node_sum_l > (1 << (level - 1)), 155 "Error in sum left of level %ul, node index %ull, " 156 "Sum = %ull \n", level, node_n_index, node_sum_l); 157 158 panic_if(node_sum_r > (1 << (level - 1)), 159 "Error in sum right of level %ul, node index %ull, " 160 "Sum = %ull \n", level, node_n_index, node_sum_r); 161 } 162 163 // Update the left sum or the right sum depending on the 164 // from_left flag. Variable stack_dist is updated only 165 // when arriving from Left. 166 if (from_left) { 167 // update sumLeft 168 node_sum_l = sum_from_below; 169 stack_dist += node_sum_r; 170 } else { 171 // update sum_r 172 node_sum_r = sum_from_below; 173 } 174 175 // sum_from_below == 0 can be a leaf discard operation 176 if (discard_node && !sum_from_below) { 177 if (from_left) 178 node_discard_left = true; 179 else 180 node_discard_right = true; 181 } 182 183 // Update the node variables with new values 184 node->nodeIndex = node_n_index; 185 node->sumLeft = node_sum_l; 186 node->sumRight = node_sum_r; 187 node->isLeftNode = node_left; 188 node->discardLeft = node_discard_left; 189 node->discardRight = node_discard_right; 190 191 // Delete the node if it is not required anymore 192 if (node_discard_left && node_discard_right && 193 discard_node && node_parent_ptr && !sum_from_below) { 194 delete node; 195 tree[level].erase(node_n_index); 196 discard_node = true; 197 } else { 198 // propogate discard_node as false upwards if the 199 // above conditions are not met. 200 discard_node = false; 201 } 202 203 // Recursively call the updateSum operation till the 204 // root node is reached 205 if (node_parent_ptr) { 206 stack_dist = updateSum(node_parent_ptr, node_left, 207 node_sum_l + node_sum_r, 208 level, stack_dist, discard_node); 209 } 210 211 return stack_dist; 212} 213 214// This function is called by the calcStackDistAndUpdate function 215// If is_new_leaf is true then a new leaf is added otherwise a leaf 216// removed from the tree. In both cases the tree is updated using 217// the updateSum operation. 218uint64_t 219StackDistCalc::updateSumsLeavesToRoot(Node* node, bool is_new_leaf) 220{ 221 uint64_t level = 0; 222 uint64_t stack_dist = 0; 223 224 if (is_new_leaf) { 225 node->sumLeft = 1; 226 updateSum(node->parent, 227 node->isLeftNode, node->sumLeft, 228 level, 0, false); 229 230 stack_dist = Infinity; 231 } else { 232 node->sumLeft = 0; 233 stack_dist = updateSum(node->parent, 234 node->isLeftNode, 0, 235 level, stack_dist, true); 236 } 237 238 return stack_dist; 239} 240 241// This method is a recursive function which calculates 242// the node sums till the root. 243uint64_t 244StackDistCalc::getSum(Node* node, bool from_left, uint64_t sum_from_below, 245 uint64_t stack_dist, uint64_t level) const 246{ 247 ++level; 248 // Variable stack_dist is updated only 249 // when arriving from Left. 250 if(from_left) { 251 stack_dist += node->sumRight; 252 } 253 254 // Recursively call the getSum operation till the 255 // root node is reached 256 if(node->parent) { 257 stack_dist = getSum(node->parent, node->isLeftNode, 258 node->sumLeft + node->sumRight, 259 stack_dist, level); 260 } 261 262 return stack_dist; 263} 264 265// This function is called by the calcStackDistance function 266uint64_t 267StackDistCalc::getSumsLeavesToRoot(Node* node) const 268{ 269 return getSum(node->parent, node->isLeftNode, 0, 0, 0); 270} 271 272// Update tree is a tree balancing operation which maintains 273// the binary tree structure. This method is called whenever 274// index%2 == 0 (i.e. every alternate cycle) 275// The two main operation are : 276// OP1. moving the root node one layer up if index counter 277// crosses power of 2 278// OP2. Addition of intermediate nodes as and when required 279// and linking them to their parents in the layer above. 280void 281StackDistCalc::updateTree() 282{ 283 uint64_t i; 284 285 if (isPowerOf2(index)) { 286 // OP1. moving the root node one layer up if index counter 287 // crosses power of 2 288 // If index counter crosses a power of 2, then add a 289 // new tree layer above and create a new Root node in it. 290 // After the root is created the old node 291 // in the layer below is updated to point to this 292 // newly created root node. The sum_l of this new root node 293 // becomes the sum_l + sum_r of the old node. 294 // 295 // After this root update operation a chain of intermediate 296 // nodes is created from root layer to tree[1](one layer 297 // above the leaf layer) 298 299 // Create a new root node 300 Node* newRootNode = new Node(); 301 302 // Update its sum_l as the sum_l+sum_r from below 303 newRootNode->sumLeft = tree[getTreeDepth()][0]->sumRight + 304 tree[getTreeDepth()][0]->sumLeft; 305 // Update its discard left flag if the node below has 306 // has both discardLeft and discardRight set. 307 newRootNode->discardLeft = tree[getTreeDepth()][0]->discardLeft && 308 tree[getTreeDepth()][0]->discardRight; 309 310 // Map type variable, representing a layer in the tree 311 IndexNodeMap treeLevel; 312 // Add a new layer to the tree 313 tree.push_back(treeLevel); 314 nextIndex.push_back(1); 315 tree[getTreeDepth()][newRootNode->nodeIndex] = newRootNode; 316 317 // Update the parent pointer at lower layer to 318 // point to newly created root node 319 tree[getTreeDepth() - 1][0]->parent = tree[getTreeDepth()][0]; 320 321 // Add intermediate nodes from root till bottom (one layer above the 322 // leaf layer) 323 for (i = getTreeDepth() - 1; i >= 1; --i) { 324 Node* newINode = new Node(); 325 // newNode is left or right child depending on the number of nodes 326 // in that layer 327 if (nextIndex[i] % 2 == 0) { 328 newINode->isLeftNode = true; 329 } else { 330 newINode->isLeftNode = false; 331 } 332 333 newINode->parent = tree[i + 1][nextIndex[i + 1] - 1]; 334 newINode->nodeIndex = ++nextIndex[i] - 1; 335 tree[i][newINode->nodeIndex] = newINode; 336 } 337 } else { 338 // OP2. Addition of intermediate nodes as and when required 339 // and linking them to their parents in the layer above. 340 // 341 // At layer 1 a new INode is added whenever index%(2^1)==0 342 // (multiples of 2) 343 // 344 // At layer 2 a new INode is added whenever index%(2^2)==0 345 // (multiples of 4) 346 // 347 // At layer 3 a new INode is added whenever index%(2^3)==0 348 // (multiples of 8) 349 //... 350 // 351 // At layer N a new INode is added whenever index%(2^N)==0 352 // (multiples of 2^N) 353 for (i = getTreeDepth() - 1; i >= 1; --i) { 354 // Traverse each layer from root to leaves and add a new 355 // intermediate node if required. Link the parent_ptr of 356 // the new node to the parent in the above layer. 357 358 if ((index % (1 << i)) == 0) { 359 // Checks if current (index % 2^treeDepth) == 0 if true 360 // a new node at that layer is created 361 Node* newINode = new Node(); 362 363 // newNode is left or right child depending on the 364 // number of nodes in that layer. 365 if (nextIndex[i] % 2 == 0) { 366 newINode->isLeftNode = true; 367 } else { 368 newINode->isLeftNode = false; 369 } 370 371 // Pointing to its parent in the upper layer 372 newINode->parent = tree[i + 1][nextIndex[i + 1] - 1]; 373 newINode->nodeIndex = ++nextIndex[i] - 1; 374 tree[i][newINode->nodeIndex] = newINode; 375 } 376 } 377 } 378} 379 380// This function is called everytime to get the stack distance and add 381// a new node. A feature to mark an old node in the tree is 382// added. This is useful if it is required to see the reuse 383// pattern. For example, BackInvalidates from the lower level (Membus) 384// to L2, can be marked (isMarked flag of Node set to True). And then 385// later if this same address is accessed by L1, the value of the 386// isMarked flag would be True. This would give some insight on how 387// the BackInvalidates policy of the lower level affect the read/write 388// accesses in an application. 389std::pair< uint64_t, bool> 390StackDistCalc::calcStackDistAndUpdate(const Addr r_address, bool addNewNode) 391{ 392 Node* newLeafNode; 393 // Return index if the address was already present in stack 394 uint64_t r_index = index; 395 396 auto ai = aiMap.lower_bound(r_address); 397 398 // Default value of flag indicating as the left or right leaf 399 bool isLeft = true; 400 // Default value of isMarked flag for each node. 401 bool _mark = false; 402 // By default stackDistacne is treated as infinity 403 uint64_t stack_dist; 404 405 // Lookup aiMap by giving address as the key: 406 // If found take address and Lookup in tree 407 // Update tree from leaves by making B(found index) = 0 408 // Add sums to right till root while Updating them 409 // Stack Distance of that address sums to right 410 if (ai != aiMap.end() && !(aiMap.key_comp()(r_address, ai->first))) { 411 // key already exists 412 // save the index counter value when this address was 413 // encountered before and update it to the current index value 414 r_index = ai->second; 415 416 if (addNewNode) { 417 // Update aiMap aiMap(Address) = current index 418 ai->second = index; 419 } else { 420 aiMap.erase(r_address); 421 } 422 423 // Call update tree operation on the tree starting with 424 // the r_index value found above. This function would return 425 // the value of the stack distcance. 426 stack_dist = updateSumsLeavesToRoot(tree[0][r_index], false); 427 newLeafNode = tree[0][r_index]; 428 // determine if this node was marked earlier 429 _mark = newLeafNode->isMarked; 430 delete newLeafNode; 431 tree[0].erase(r_index); 432 } else { 433 if (addNewNode) { 434 // Update aiMap aiMap(Address) = current index 435 aiMap[r_address] = index; 436 } 437 // Update infinity bin count 438 // By default stackDistacne is treated as infinity 439 stack_dist = Infinity; 440 } 441 442 if (addNewNode) { 443 // If index%2 == 0 then update tree 444 if (index % 2 == 0) { 445 updateTree(); 446 } else { 447 // At odd values of index counter, a new right-type node is 448 // added to the leaf layer, else a left-type node is added 449 isLeft = false; 450 } 451 452 // Add new leaf node in the leaf layer (tree[0]) 453 // set n_index = current index 454 newLeafNode = new Node(); 455 ++nextIndex[0]; 456 newLeafNode->nodeIndex=index; 457 newLeafNode->isLeftNode=isLeft; 458 // Point the parent pointer to the intermediate node above 459 newLeafNode->parent = tree[1][nextIndex[1] - 1]; 460 tree[0][index] = newLeafNode; 461 // call an update operation which would update the tree after 462 // addition of this new leaf node. 463 updateSumsLeavesToRoot(tree[0][index], true); 464 465 // For verification 466 if (verifyStack) { 467 // This function checks the sanity of the tree to make sure the 468 // last node in the link of parent pointers is the root node. 469 // It takes a leaf node as an argument and traveses upwards till 470 // the root layer to check if the last parent is null 471 sanityCheckTree(tree[0][index]); 472 473 // Push the same element in debug stack, and check 474 uint64_t verify_stack_dist = verifyStackDist(r_address, true); 475 panic_if(verify_stack_dist != stack_dist, 476 "Expected stack-distance for address \ 477 %#lx is %#lx but found %#lx", 478 r_address, verify_stack_dist, stack_dist); 479 printStack(); 480 } 481 482 // The index counter is updated at the end of each transaction 483 // (unique or non-unique) 484 ++index; 485 } 486 487 return (std::make_pair(stack_dist, _mark)); 488} 489 490// This function is called everytime to get the stack distance 491// no new node is added. It can be used to mark a previous access 492// and inspect the value of the mark flag. 493std::pair< uint64_t, bool> 494StackDistCalc::calcStackDist(const Addr r_address, bool mark) 495{ 496 // Return index if the address was already present in stack 497 uint64_t r_index = index; 498 // Default value of isMarked flag for each node. 499 bool _mark = false; 500 501 auto ai = aiMap.lower_bound(r_address); 502 503 // By default stackDistacne is treated as infinity 504 uint64_t stack_dist = 0; 505 506 // Lookup aiMap by giving address as the key: 507 // If found take address and Lookup in tree 508 // Add sums to right till root 509 // Stack Distance of that address sums to right 510 if (ai != aiMap.end() && !(aiMap.key_comp()(r_address, ai->first))) { 511 // key already exists 512 // save the index counter value when this address was 513 // encountered before 514 r_index = ai->second; 515 516 // Get the value of mark flag if previously marked 517 _mark = tree[0][r_index]->isMarked; 518 // Mark the leaf node if required 519 tree[0][r_index]->isMarked = mark; 520 521 // Call get sums operation on the tree starting with 522 // the r_index value found above. This function would return 523 // the value of the stack distcance. 524 stack_dist = getSumsLeavesToRoot(tree[0][r_index]); 525 } else { 526 // Update infinity bin count 527 // By default stackDistacne is treated as infinity 528 stack_dist = Infinity; 529 } 530 531 // For verification 532 if (verifyStack) { 533 // Calculate the SD of the same address in the debug stack 534 uint64_t verify_stack_dist = verifyStackDist(r_address); 535 panic_if(verify_stack_dist != stack_dist, 536 "Expected stack-distance for address \ 537 %#lx is %#lx but found %#lx", 538 r_address, verify_stack_dist, stack_dist); 539 540 printStack(); 541 } 542 543 return std::make_pair(stack_dist, _mark); 544} 545 546// For verification 547// Simple sanity check for the tree 548void 549StackDistCalc::sanityCheckTree(const Node* node, uint64_t level) const 550{ 551 const Node* next_up = node->parent; 552 553 for (uint64_t i = level + 1; i < getTreeDepth() - level; ++i) { 554 next_up = next_up->parent; 555 panic_if(!next_up, "Sanity check failed for node %ull \n", 556 node->nodeIndex); 557 } 558 559 // At the root layer the parent_ptr should be null 560 panic_if(next_up->parent, "Sanity check failed for node %ull \n", 561 node->nodeIndex); 562} 563 564// This method can be called to compute the stack distance in a naive 565// way It can be used to verify the functionality of the stack 566// distance calculator. It uses std::vector to compute the stack 567// distance using a naive stack. 568uint64_t 569StackDistCalc::verifyStackDist(const Addr r_address, bool update_stack) 570{ 571 bool found = false; 572 uint64_t stack_dist = 0; 573 auto a = stack.rbegin(); 574 575 for (; a != stack.rend(); ++a) { 576 if (*a == r_address) { 577 found = true; 578 break; 579 } else { 580 ++stack_dist; 581 } 582 } 583 584 if (found) { 585 ++a; 586 if (update_stack) 587 stack.erase(a.base()); 588 } else { 589 stack_dist = Infinity; 590 } 591 592 if (update_stack) 593 stack.push_back(r_address); 594 595 return stack_dist; 596} 597 598// This method is useful to print top n entities in the stack. 599void 600StackDistCalc::printStack(int n) const 601{ 602 Node* node; 603 uint64_t r_index; 604 int count = 0; 605 606 DPRINTF(StackDist, "Printing last %d entries in tree\n", n); 607 608 // Walk through leaf layer to display the last n nodes 609 for (auto it = tree[0].rbegin(); (count < n) && (it != tree[0].rend()); 610 ++it, ++count) { 611 node = it->second; 612 r_index = node->nodeIndex; 613 614 // Lookup aiMap using the index returned by the leaf iterator 615 for (auto ai = aiMap.rbegin(); ai != aiMap.rend(); ++ai) { 616 if (ai->second == r_index) { 617 DPRINTF(StackDist,"Tree leaves, Rightmost-[%d] = %#lx\n", 618 count, ai->first); 619 break; 620 } 621 } 622 } 623 624 DPRINTF(StackDist,"Tree depth = %#ld\n", getTreeDepth()); 625 626 if (verifyStack) { 627 DPRINTF(StackDist,"Printing Last %d entries in VerifStack \n", n); 628 count = 0; 629 for (auto a = stack.rbegin(); (count < n) && (a != stack.rend()); 630 ++a, ++count) { 631 DPRINTF(StackDist, "Verif Stack, Top-[%d] = %#lx\n", count, *a); 632 } 633 } 634} 635 636void 637StackDistCalc::regStats() 638{ 639 using namespace Stats; 640 641 readLinearHist 642 .init(params()->linear_hist_bins) 643 .name(name() + ".readLinearHist") 644 .desc("Reads linear distribution") 645 .flags(disableLinearHists ? nozero : pdf); 646 647 readLogHist 648 .init(params()->log_hist_bins) 649 .name(name() + ".readLogHist") 650 .desc("Reads logarithmic distribution") 651 .flags(disableLogHists ? nozero : pdf); 652 653 writeLinearHist 654 .init(params()->linear_hist_bins) 655 .name(name() + ".writeLinearHist") 656 .desc("Writes linear distribution") 657 .flags(disableLinearHists ? nozero : pdf); 658 659 writeLogHist 660 .init(params()->log_hist_bins) 661 .name(name() + ".writeLogHist") 662 .desc("Writes logarithmic distribution") 663 .flags(disableLogHists ? nozero : pdf); 664} 665 666StackDistCalc* 667StackDistCalcParams::create() 668{ 669 return new StackDistCalc(this); 670} 671