summaryrefslogtreecommitdiffstats
path: root/src/interface_layout_selection.c
blob: ce755c67150dc20938c78b6744c40fde2bdd0e74 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
#include "nvtop/interface_layout_selection.h"
#include "nvtop/interface.h"
#include "nvtop/interface_options.h"

#include <assert.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define max(a, b) ((a) > (b) ? (a) : (b))
#define min(a, b) ((a) < (b) ? (a) : (b))

static unsigned min_rows_taken_by_process(unsigned rows, unsigned num_devices) {
  return 1 + max(5, min(rows / 4, num_devices * 3));
}

static const unsigned cols_needed_box_drawing = 5;
static const unsigned min_plot_rows = 7;
static unsigned min_plot_cols(unsigned num_data_info_to_plot) {
  return cols_needed_box_drawing + 10 * num_data_info_to_plot;
}

// If true, returns two plot indices that yields to the lowest number of info in
// a plot when merged.
// In case of ties, plots at the end of the list are prioritized.
static bool who_to_merge(unsigned max_merge_size, unsigned plot_count, unsigned num_info_per_plot[plot_count],
                         unsigned merge_ids[2]) {
  unsigned smallest_merge = UINT_MAX;
  for (unsigned notEmptyPlotIdx = plot_count - 1; notEmptyPlotIdx < plot_count; --notEmptyPlotIdx) {
    if (!num_info_per_plot[notEmptyPlotIdx])
      continue;
    // We want to preserve the devices order when merging, hence we only look
    // for the closest non empty neighbor for each plot.
    unsigned merge_with = notEmptyPlotIdx;
    for (unsigned j = notEmptyPlotIdx - 1; j < notEmptyPlotIdx; --j) {
      if (num_info_per_plot[j]) {
        merge_with = j;
        break;
      }
    }
    unsigned num_info_merged = num_info_per_plot[notEmptyPlotIdx] + num_info_per_plot[merge_with];
    if (merge_with < notEmptyPlotIdx && num_info_merged <= max_merge_size && num_info_merged < smallest_merge) {
      smallest_merge = num_info_merged;
      merge_ids[0] = merge_with;
      merge_ids[1] = notEmptyPlotIdx;
    }
  }
  return smallest_merge != UINT_MAX;
}

static bool move_plot_to_stack(unsigned stack_max_cols, unsigned plot_id, unsigned destination_stack,
                               unsigned plot_count, unsigned stack_count, const unsigned num_info_per_plot[plot_count],
                               unsigned cols_allocated_in_stacks[stack_count], unsigned plot_in_stack[plot_count]) {
  if (plot_in_stack[plot_id] == destination_stack)
    return false;
  unsigned cols_used_by_plot_id = min_plot_cols(num_info_per_plot[plot_id]);
  unsigned cols_after_merge = cols_allocated_in_stacks[destination_stack] + cols_used_by_plot_id;
  if (cols_after_merge > stack_max_cols) {
    return false;
  } else {
    cols_allocated_in_stacks[plot_in_stack[plot_id]] -= cols_used_by_plot_id;
    cols_allocated_in_stacks[destination_stack] += cols_used_by_plot_id;
    plot_in_stack[plot_id] = destination_stack;
    return true;
  }
}

static unsigned info_in_plot(unsigned plot_id, unsigned devices_count, const unsigned map_device_to_plot[devices_count],
                             const nvtop_interface_gpu_opts gpuOpts[devices_count]) {
  unsigned sum = 0;
  for (unsigned dev_id = 0; dev_id < devices_count; ++dev_id) {
    if (map_device_to_plot[dev_id] == plot_id)
      sum += plot_count_draw_info(gpuOpts[dev_id].to_draw);
  }
  assert(sum > 0);
  return sum;
}

static unsigned cols_used_by_stack(unsigned stack_id, unsigned plot_count, const unsigned num_info_per_plot[plot_count],
                                   const unsigned plot_in_stack[plot_count]) {
  unsigned sum = 0;
  for (unsigned plot_id = 0; plot_id < plot_count; ++plot_id) {
    if (plot_in_stack[plot_id] == stack_id)
      sum += min_plot_cols(num_info_per_plot[plot_id]);
  }
  return sum;
}

static unsigned size_differences_between_stacks(unsigned plot_count, unsigned stack_count,
                                                unsigned cols_allocated_in_stacks[plot_count]) {
  unsigned sum = 0;
  for (unsigned i = 0; i < stack_count; ++i) {
    for (unsigned j = i + 1; j < stack_count; ++j) {
      if (cols_allocated_in_stacks[i] > cols_allocated_in_stacks[j]) {
        sum += cols_allocated_in_stacks[i] - cols_allocated_in_stacks[j];
      } else {
        sum += cols_allocated_in_stacks[j] - cols_allocated_in_stacks[i];
      }
    }
  }
  return sum;
}

static void preliminary_plot_positioning(unsigned rows_for_plots, unsigned plot_total_cols, unsigned devices_count,
                                         const nvtop_interface_gpu_opts gpuOpts[devices_count],
                                         unsigned map_device_to_plot[devices_count],
                                         unsigned plot_in_stack[devices_count], unsigned *num_plots,
                                         unsigned *plot_stack_count) {

  // Used to handle the merging process
  unsigned num_info_per_plot[MAX_CHARTS];

  bool plot_anything = false;
  for (unsigned i = 0; i < devices_count; ++i) {
    num_info_per_plot[i] = plot_count_draw_info(gpuOpts[i].to_draw);
    map_device_to_plot[i] = i;
    if (num_info_per_plot[i])
      plot_anything = true;
  }

  // Get the most packed configuration possible with one chart per device if
  // possible.
  // If there is not enough place, merge the charts and retry.
  unsigned num_plot_stacks = 0;
  bool search_a_window_configuration = plot_anything && rows_for_plots >= min_plot_rows;
  while (search_a_window_configuration) {
    search_a_window_configuration = false;
    unsigned plot_id = 0;
    num_plot_stacks = 1;
    unsigned cols_used_in_stack = 0;
    unsigned rows_left_to_allocate = rows_for_plots - min_plot_rows;

    for (unsigned i = 0; i < devices_count; ++i) {
      unsigned num_info_for_this_plot = num_info_per_plot[i];
      if (num_info_for_this_plot == 0)
        continue;

      unsigned cols_this_plot = min_plot_cols(num_info_for_this_plot);
      // If there is enough horizontal space left, allocate side by side
      if (plot_total_cols >= cols_this_plot + cols_used_in_stack) {
        cols_used_in_stack += cols_this_plot;
        plot_in_stack[plot_id] = num_plot_stacks - 1;
        plot_id++;
      } else {
        // This plot is too wide for an empty stack, abort
        if (cols_used_in_stack == 0) {
          num_plot_stacks = 0;
          break;
        }
        // Else allocate a new stack and retry
        if (rows_left_to_allocate >= min_plot_rows) {
          rows_left_to_allocate -= min_plot_rows;
          num_plot_stacks++;
          cols_used_in_stack = 0;
          i--;
        } else { // Not enough space for a stack: retry and merge one more
          unsigned to_merge[2];
          if (who_to_merge(MAX_LINES_PER_PLOT, devices_count, num_info_per_plot, to_merge)) {
            num_info_per_plot[to_merge[0]] += num_info_per_plot[to_merge[1]];
            num_info_per_plot[to_merge[1]] = 0;
            unsigned oldLocation = map_device_to_plot[to_merge[1]];
            for (unsigned devId = 0; devId < devices_count; ++devId) {
              if (map_device_to_plot[devId] == oldLocation)
                map_device_to_plot[devId] = map_device_to_plot[to_merge[0]];
            }
            search_a_window_configuration = true;
          } else { // No merge left
            num_plot_stacks = 0;
          }
          break;
        }
      }
    }
  }

  // Compute the number of plots, the mapping and the size
  *num_plots = 0;
  *plot_stack_count = num_plot_stacks;
  if (num_plot_stacks > 0) {
    // Move non-empty plots over empty ones caused by merges
    for (unsigned idx = 0; idx < devices_count; ++idx) {
      if (!num_info_per_plot[idx]) {
        // Search next non-empty and move it here
        for (unsigned nextIdx = idx + 1; nextIdx < devices_count; ++nextIdx) {
          if (num_info_per_plot[nextIdx]) {
            num_info_per_plot[idx] = num_info_per_plot[nextIdx];
            num_info_per_plot[nextIdx] = 0;
            for