| /*****************************************************************************\ |
| * mapping.c - routines for compact process mapping representation |
| ***************************************************************************** |
| * Copyright (C) 2014 Institute of Semiconductor Physics |
| * Siberian Branch of Russian Academy of Science |
| * Written by Artem Polyakov <artpol84@gmail.com>. |
| * All rights reserved. |
| * |
| * This file is part of Slurm, a resource management program. |
| * For details, see <https://slurm.schedmd.com/>. |
| * Please also read the included file: DISCLAIMER. |
| * |
| * Slurm is free software; you can redistribute it and/or modify it under |
| * the terms of the GNU General Public License as published by the Free |
| * Software Foundation; either version 2 of the License, or (at your option) |
| * any later version. |
| * |
| * In addition, as a special exception, the copyright holders give permission |
| * to link the code of portions of this program with the OpenSSL library under |
| * certain conditions as described in each individual source file, and |
| * distribute linked combinations including the two. You must obey the GNU |
| * General Public License in all respects for all of the code used other than |
| * OpenSSL. If you modify file(s) with this exception, you may extend this |
| * exception to your version of the file(s), but you are not obligated to do |
| * so. If you do not wish to do so, delete this exception statement from your |
| * version. If you delete this exception statement from all source files in |
| * the program, then also delete it here. |
| * |
| * Slurm is distributed in the hope that it will be useful, but WITHOUT ANY |
| * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
| * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more |
| * details. |
| * |
| * You should have received a copy of the GNU General Public License along |
| * with Slurm; if not, write to the Free Software Foundation, Inc., |
| * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. |
| \*****************************************************************************/ |
| |
| #define _GNU_SOURCE |
| |
| #include "src/plugins/mpi/pmix/mapping.h" |
| |
| static void _dump_config(uint32_t node_cnt, uint32_t task_cnt, |
| uint16_t *tasks, uint32_t **tids, int offset) |
| { |
| int i, j; |
| |
| error("%s: Unable to find task offset %d", __func__, offset); |
| for (i = 0; i < node_cnt; i++) { |
| for (j = 0; j < tasks[i]; j++) { |
| error("TIDS[%d][%d]:%u", i, j, tids[i][j]); |
| } |
| } |
| } |
| |
| /* |
| * pack_process_mapping() - Return NULL on error |
| */ |
| char * |
| pack_process_mapping(uint32_t node_cnt, |
| uint32_t task_cnt, |
| uint16_t *tasks, |
| uint32_t **tids) |
| { |
| int offset, i; |
| int start_node, end_node; |
| char *packing = NULL; |
| |
| /* |
| * next_task[i] - next process for processing |
| */ |
| uint16_t *next_task = xcalloc(node_cnt, sizeof(uint16_t)); |
| |
| packing = xstrdup("(vector"); |
| offset = 0; |
| while (offset < task_cnt) { |
| int mapped = 0; |
| int depth = -1; |
| int j; |
| start_node = end_node = 0; |
| |
| /* find the task with id == offset */ |
| for (i = 0; i < node_cnt; i++) { |
| |
| if (next_task[i] < tasks[i]) { |
| /* |
| * if we didn't consume entire |
| * quota on this node |
| */ |
| if (offset > tids[i][next_task[i]]) { |
| _dump_config(node_cnt, task_cnt, |
| tasks, tids, offset); |
| abort(); |
| } |
| if (offset == tids[i][next_task[i]]) { |
| start_node = i; |
| break; |
| } |
| } |
| } |
| |
| end_node = node_cnt; |
| for (i = start_node; i < end_node; i++) { |
| if (next_task[i] >= tasks[i] ) { |
| /* |
| * Save first non-matching node index |
| * and interrupt loop |
| */ |
| end_node = i; |
| continue; |
| } |
| |
| for (j = next_task[i]; ((j + 1) < tasks[i]) |
| && ((tids[i][j]+1) == tids[i][j+1]); j++); |
| j++; |
| /* |
| * First run determines the depth |
| */ |
| if (depth < 0) { |
| depth = j - next_task[i]; |
| } else { |
| /* |
| * If this is not the first node in the bar |
| * check that: 1. First tid on this node is |
| * sequentially next after last tid |
| * on the previous node |
| */ |
| if (tids[i-1][next_task[i-1]-1] + 1 |
| != tids[i][next_task[i]]) { |
| end_node = i; |
| continue; |
| } |
| } |
| |
| if (depth == (j - next_task[i])) { |
| mapped += depth; |
| next_task[i] = j; |
| } else { |
| /* |
| * Save first non-matching node index |
| * and interrupt loop |
| */ |
| end_node = i; |
| } |
| } |
| |
| if (depth < 0) { |
| xfree(next_task); |
| xfree(packing); |
| return NULL; |
| } |
| |
| xstrfmtcat(packing,",(%u,%u,%u)", |
| start_node, end_node - start_node, depth); |
| offset += mapped; |
| } |
| xfree(next_task); |
| xstrcat(packing,")"); |
| return packing; |
| } |
| |
| uint32_t * |
| unpack_process_mapping_flat(char *map, |
| uint32_t node_cnt, |
| uint32_t task_cnt, |
| uint16_t *tasks) |
| { |
| /* |
| * Start from the flat array. For i'th task is located |
| * on the task_map[i]'th node |
| */ |
| uint32_t *task_map = xcalloc(task_cnt, sizeof(int)); |
| char *prefix = "(vector,", *p = NULL; |
| uint32_t taskid, i; |
| |
| if (tasks) { |
| for (i = 0; i < node_cnt; i++) { |
| tasks[i] = 0; |
| } |
| } |
| |
| if ((p = strstr(map, prefix)) == NULL) { |
| error("\ |
| unpack_process_mapping: The mapping string should start from %s", prefix); |
| goto err_exit; |
| } |
| |
| /* |
| * Skip prefix |
| */ |
| p += strlen(prefix); |
| taskid = 0; |
| while ((p = strchr(p,'('))) { |
| int depth, node, end_node; |
| p++; |
| if (3!= sscanf(p,"%d,%d,%d", &node, &end_node, &depth)) { |
| goto err_exit; |
| } |
| end_node += node; |
| xassert(node < node_cnt && end_node <= node_cnt ); |
| for (; node < end_node; node++) { |
| for (i = 0; i < depth; i++){ |
| task_map[taskid++] = node; |
| if (tasks != NULL) { |
| /* |
| * Cont tasks on each node if was |
| * requested |
| */ |
| tasks[node]++; |
| } |
| } |
| } |
| } |
| return task_map; |
| err_exit: |
| xfree(task_map); |
| return NULL; |
| } |
| |
| int |
| unpack_process_mapping(char *map, |
| uint32_t node_cnt, |
| uint32_t task_cnt, |
| uint16_t *tasks, |
| uint32_t **tids) |
| { |
| /* |
| * Start from the flat array. For i'th task is located |
| * on the task_map[i]'th node |
| */ |
| uint32_t *task_map = NULL; |
| uint16_t *node_task_cnt = NULL; |
| uint32_t i; |
| int rc = 0; |
| |
| task_map = unpack_process_mapping_flat(map, node_cnt, task_cnt, tasks); |
| if (task_map == NULL) { |
| rc = SLURM_ERROR; |
| goto err_exit; |
| } |
| |
| node_task_cnt = xcalloc(node_cnt, sizeof(uint16_t)); |
| for (i = 0; i < node_cnt; i++){ |
| tids[i] = xcalloc(tasks[i], sizeof(uint32_t)); |
| node_task_cnt[i] = 0; |
| } |
| |
| for (i = 0; i < task_cnt; i++) { |
| uint32_t node = task_map[i]; |
| tids[node][ node_task_cnt[node]++ ] = i; |
| xassert( node_task_cnt[node] <= tasks[node] ); |
| } |
| |
| goto exit; |
| err_exit: |
| error("unpack_process_mapping: bad mapping format"); |
| exit: |
| if (task_map != NULL){ |
| xfree(task_map); |
| } |
| |
| if (node_task_cnt != NULL){ |
| xfree(node_task_cnt); |
| } |
| return rc; |
| } |
| |
| |
| #if 0 |
| |
| /* |
| * Mutual check for both routines |
| */ |
| |
| /* |
| * Emulate 16-core nodes |
| */ |
| #define NCPUS 16 |
| #define NODES 200 |
| |
| static |
| void block_distr(uint32_t task_cnt, |
| uint16_t *tasks, |
| uint32_t **tids) |
| { |
| int i, j, tnum = 0; |
| |
| for (i = 0; i < NODES; i++) { |
| tasks[i] = 0; |
| } |
| |
| /* BLOCK distribution |
| */ |
| for (i = 0; (i < NODES) && (tnum < task_cnt); i++) { |
| for (j = 0; j < NCPUS && (tnum < task_cnt); j++) { |
| tids[i][j] = tnum++; |
| } |
| tasks[i] = j; |
| } |
| } |
| |
| static void |
| cyclic_distr(uint32_t task_cnt, |
| uint16_t *tasks, |
| uint32_t **tids) |
| { |
| int i, j, tnum = 0; |
| /* CYCLIC distribution |
| */ |
| tnum = 0; |
| for (i = 0; i < NODES; i++) { |
| tasks[i] = 0; |
| } |
| for (j = 0; j < NCPUS && (tnum < task_cnt); j++) { |
| for (i = 0; (i < NODES) && (tnum < task_cnt); i++ ) { |
| tids[i][j] = tnum++; |
| tasks[i]++; |
| } |
| } |
| } |
| |
| |
| static void |
| plane_distr(uint32_t task_cnt, |
| int plane_factor, |
| uint16_t *tasks, |
| uint32_t **tids) |
| { |
| int i, j, tnum = 0; |
| /* PLANE distribution |
| */ |
| tnum = 0; |
| for (i = 0; i < NODES; i++) { |
| tasks[i] = 0; |
| } |
| |
| while (tnum < task_cnt) { |
| for (i = 0; (i < NODES) && (tnum < task_cnt); i++) { |
| for (j = 0; |
| (j < plane_factor) |
| && (tasks[i] < NCPUS) |
| && (tnum < task_cnt); j++) { |
| tids[i][tasks[i]++] = tnum++; |
| } |
| } |
| } |
| } |
| |
| static void check(uint32_t node_cnt, uint32_t task_cnt, |
| uint16_t *tasks, uint32_t **tids) |
| { |
| uint16_t *new_tasks; |
| uint32_t **new_tids; |
| char *map = pack_process_mapping(node_cnt, task_cnt, tasks, tids); |
| int i,j; |
| |
| printf("mapping: %s\n", map); |
| |
| new_tasks = xcalloc(node_cnt, sizeof(uint16_t)); |
| new_tids = xcalloc(node_cnt, sizeof(uint32_t *)); |
| unpack_process_mapping(map,node_cnt,task_cnt,new_tasks,new_tids); |
| |
| for (i = 0; i < node_cnt; i++) { |
| |
| if (new_tasks[i] != tasks[i]) { |
| printf("Task count mismatch on node %d\n", i); |
| exit(0); |
| } |
| |
| for (j = 0; j< tasks[i]; j++) { |
| if (new_tids[i][j] != tids[i][j]){ |
| printf("\ |
| Task id mismatch on node %d, idx = %d\n", i, j); |
| exit(0); |
| } |
| } |
| } |
| |
| for (i = 0; i< node_cnt; i++) { |
| xfree(new_tids[i]); |
| } |
| xfree(new_tasks); |
| xfree(new_tids); |
| |
| xfree(map); |
| |
| } |
| |
| |
| int |
| main(int argc, char **argv) |
| { |
| uint16_t tasks[NODES] = { 0 }; |
| uint32_t **tids = NULL; |
| int tnum = 0, i; |
| |
| tids = xcalloc(NODES, sizeof(uint32_t *)); |
| for (i = 0; i< NODES; i++) { |
| tids[i] = xcalloc(NCPUS, sizeof(uint32_t)); |
| } |
| |
| for (tnum = 1; tnum < NCPUS*NODES; tnum++) { |
| |
| printf("Map %d tasks into cluster %dx%d\n", tnum, NODES, NCPUS); |
| block_distr(tnum, tasks, tids); |
| check(NODES,tnum, tasks, tids); |
| |
| cyclic_distr(tnum, tasks, tids); |
| check(NODES,tnum, tasks, tids); |
| |
| plane_distr(tnum,2,tasks, tids); |
| check(NODES,tnum, tasks, tids); |
| |
| plane_distr(tnum,4,tasks, tids); |
| check(NODES,tnum, tasks, tids); |
| |
| plane_distr(tnum,6,tasks, tids); |
| check(NODES,tnum, tasks, tids); |
| |
| plane_distr(tnum,8,tasks, tids); |
| check(NODES,tnum, tasks, tids); |
| } |
| |
| for (i = 0; i < NODES; i++){ |
| xfree(tids[i]); |
| } |
| xfree(tids); |
| |
| return 0; |
| } |
| |
| #endif |