1 /*
  2  * Copyright (c) 2019, 2020, Oracle and/or its affiliates. All rights reserved.
  3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  4  *
  5  * This code is free software; you can redistribute it and/or modify it
  6  * under the terms of the GNU General Public License version 2 only, as
  7  * published by the Free Software Foundation.
  8  *
  9  * This code is distributed in the hope that it will be useful, but WITHOUT
 10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 12  * version 2 for more details (a copy is included in the LICENSE file that
 13  * accompanied this code).
 14  *
 15  * You should have received a copy of the GNU General Public License version
 16  * 2 along with this work; if not, write to the Free Software Foundation,
 17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 18  *
 19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 20  * or visit www.oracle.com if you need additional information or have any
 21  * questions.
 22  *
 23  */
 24 
 25 #include "precompiled.hpp"
 26 #include "gc/g1/g1NUMAStats.hpp"
 27 #include "logging/logStream.hpp"
 28 
 29 double G1NUMAStats::Stat::rate() const {
 30   return _requested == 0 ? 0 : (double)_hit / _requested * 100;
 31 }
 32 
 33 G1NUMAStats::NodeDataArray::NodeDataArray(uint num_nodes) {
 34   // The row represents the number of nodes.
 35   _num_column = num_nodes;
 36   // +1 for G1MemoryNodeManager::AnyNodeIndex.
 37   _num_row = num_nodes + 1;
 38 
 39   _data = NEW_C_HEAP_ARRAY(size_t*, _num_row, mtGC);
 40   for (uint row = 0; row < _num_row; row++) {
 41     _data[row] = NEW_C_HEAP_ARRAY(size_t, _num_column, mtGC);
 42   }
 43 
 44   clear();
 45 }
 46 
 47 G1NUMAStats::NodeDataArray::~NodeDataArray() {
 48   for (uint row = 0; row < _num_row; row++) {
 49     FREE_C_HEAP_ARRAY(size_t, _data[row]);
 50   }
 51   FREE_C_HEAP_ARRAY(size_t*, _data);
 52 }
 53 
 54 void G1NUMAStats::NodeDataArray::create_hit_rate(Stat* result) const {
 55   size_t requested = 0;
 56   size_t hit = 0;
 57 
 58   for (size_t row = 0; row < _num_row; row++) {
 59     for (size_t column = 0; column < _num_column; column++) {
 60       requested += _data[row][column];
 61       if (row == column) {
 62         hit += _data[row][column];
 63       }
 64     }
 65   }
 66 
 67   assert(result != NULL, "Invariant");
 68   result->_hit = hit;
 69   result->_requested = requested;
 70 }
 71 
 72 void G1NUMAStats::NodeDataArray::create_hit_rate(Stat* result, uint req_index) const {
 73   size_t requested = 0;
 74   size_t hit = _data[req_index][req_index];
 75 
 76   for (size_t column = 0; column < _num_column; column++) {
 77     requested += _data[req_index][column];
 78   }
 79 
 80   assert(result != NULL, "Invariant");
 81   result->_hit = hit;
 82   result->_requested = requested;
 83 }
 84 
 85 size_t G1NUMAStats::NodeDataArray::sum(uint req_index) const {
 86   size_t sum = 0;
 87   for (size_t column = 0; column < _num_column; column++) {
 88     sum += _data[req_index][column];
 89   }
 90 
 91   return sum;
 92 }
 93 
 94 void G1NUMAStats::NodeDataArray::increase(uint req_index, uint alloc_index) {
 95   assert(req_index < _num_row,
 96          "Requested index %u should be less than the row size %u",
 97          req_index, _num_row);
 98   assert(alloc_index < _num_column,
 99          "Allocated index %u should be less than the column size %u",
100          alloc_index, _num_column);
101   _data[req_index][alloc_index] += 1;
102 }
103 
104 void G1NUMAStats::NodeDataArray::clear() {
105   for (uint row = 0; row < _num_row; row++) {
106     memset((void*)_data[row], 0, sizeof(size_t) * _num_column);
107   }
108 }
109 
110 size_t G1NUMAStats::NodeDataArray::get(uint req_index, uint alloc_index) {
111   return _data[req_index][alloc_index];
112 }
113 
114 void G1NUMAStats::NodeDataArray::copy(uint req_index, size_t* stat) {
115   assert(stat != NULL, "Invariant");
116 
117   for (uint column = 0; column < _num_column; column++) {
118     _data[req_index][column] += stat[column];
119   }
120 }
121 
122 G1NUMAStats::G1NUMAStats(const int* node_ids, uint num_node_ids) :
123   _node_ids(node_ids), _num_node_ids(num_node_ids), _node_data() {
124 
125   assert(_num_node_ids > 1, "Should have at least one node id: %u", _num_node_ids);
126 
127   for (int i = 0; i < NodeDataItemsSentinel; i++) {
128     _node_data[i] = new NodeDataArray(_num_node_ids);
129   }
130 }
131 
132 G1NUMAStats::~G1NUMAStats() {
133   for (int i = 0; i < NodeDataItemsSentinel; i++) {
134     delete _node_data[i];
135   }
136 }
137 
138 void G1NUMAStats::clear(G1NUMAStats::NodeDataItems phase) {
139   _node_data[phase]->clear();
140 }
141 
142 void G1NUMAStats::update(G1NUMAStats::NodeDataItems phase,
143                          uint requested_node_index,
144                          uint allocated_node_index) {
145   _node_data[phase]->increase(requested_node_index, allocated_node_index);
146 }
147 
148 void G1NUMAStats::copy(G1NUMAStats::NodeDataItems phase,
149                        uint requested_node_index,
150                        size_t* allocated_stat) {
151   _node_data[phase]->copy(requested_node_index, allocated_stat);
152 }
153 
154 static const char* phase_to_explanatory_string(G1NUMAStats::NodeDataItems phase) {
155   switch(phase) {
156     case G1NUMAStats::NewRegionAlloc:
157       return "Placement match ratio";
158     case G1NUMAStats::LocalObjProcessAtCopyToSurv:
159       return "Worker task locality match ratio";
160     default:
161       return "";
162   }
163 }
164 
165 #define RATE_TOTAL_FORMAT "%0.0f%% " SIZE_FORMAT "/" SIZE_FORMAT
166 
167 void G1NUMAStats::print_info(G1NUMAStats::NodeDataItems phase) {
168   LogTarget(Info, gc, heap, numa) lt;
169 
170   if (lt.is_enabled()) {
171     LogStream ls(lt);
172     Stat result;
173     size_t array_width = _num_node_ids;
174 
175     _node_data[phase]->create_hit_rate(&result);
176 
177     ls.print("%s: " RATE_TOTAL_FORMAT " (",
178              phase_to_explanatory_string(phase), result.rate(), result._hit, result._requested);
179 
180     for (uint i = 0; i < array_width; i++) {
181       if (i != 0) {
182         ls.print(", ");
183       }
184       _node_data[phase]->create_hit_rate(&result, i);
185       ls.print("%d: " RATE_TOTAL_FORMAT,
186                _node_ids[i], result.rate(), result._hit, result._requested);
187     }
188     ls.print_cr(")");
189   }
190 }
191 
192 void G1NUMAStats::print_mutator_alloc_stat_debug() {
193   LogTarget(Debug, gc, heap, numa) lt;
194 
195   if (lt.is_enabled()) {
196     LogStream ls(lt);
197     uint array_width = _num_node_ids;
198 
199     ls.print("Allocated NUMA ids    ");
200     for (uint i = 0; i < array_width; i++) {
201       ls.print("%8d", _node_ids[i]);
202     }
203     ls.print_cr("   Total");
204 
205     ls.print("Requested NUMA id ");
206     for (uint req = 0; req < array_width; req++) {
207       ls.print("%3d ", _node_ids[req]);
208       for (uint alloc = 0; alloc < array_width; alloc++) {
209         ls.print(SIZE_FORMAT_W(8), _node_data[NewRegionAlloc]->get(req, alloc));
210       }
211       ls.print(SIZE_FORMAT_W(8), _node_data[NewRegionAlloc]->sum(req));
212       ls.print_cr("");
213       // Add padding to align with the string 'Requested NUMA id'.
214       ls.print("                  ");
215     }
216     ls.print("Any ");
217     for (uint alloc = 0; alloc < array_width; alloc++) {
218       ls.print(SIZE_FORMAT_W(8), _node_data[NewRegionAlloc]->get(array_width, alloc));
219     }
220     ls.print(SIZE_FORMAT_W(8), _node_data[NewRegionAlloc]->sum(array_width));
221     ls.print_cr("");
222   }
223 }
224 
225 void G1NUMAStats::print_statistics() {
226   print_info(NewRegionAlloc);
227   print_mutator_alloc_stat_debug();
228 
229   print_info(LocalObjProcessAtCopyToSurv);
230 }