| /*****************************************************************************\ | |
| * src/common/reverse_tree.c | |
| ***************************************************************************** | |
| * Copyright (C) 2006 The Regents of the University of California. | |
| * Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER). | |
| * Written by Christopher J. Morrone <morrone2@llnl.gov> | |
| * CODE-OCEC-09-009. All rights reserved. | |
| * Portions copyright (C) 2014 Institute of Semiconductor Physics | |
| * Siberian Branch of Russian Academy of Science | |
| * Written by Artem Polyakov <artpol84@gmail.com>. | |
| * All rights reserved. | |
| * Portions copyright (C) 2017 Mellanox Technologies. | |
| * 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. | |
| \*****************************************************************************/ | |
| #include <stdio.h> | |
| #include <stdlib.h> | |
| static inline int int_pow(int num, int power) | |
| { | |
| int res; | |
| int i; | |
| if (power == 0) | |
| res = 1; | |
| else if (power == 1) | |
| res = num; | |
| else { | |
| res = num; | |
| for (i = 1; i < power; i++) | |
| res *= num; | |
| } | |
| return res; | |
| } | |
| static inline int geometric_series(int width, int depth) | |
| { | |
| /* | |
| * the main idea behind this formula is the following math base: | |
| * a^n - b^n = (a-b)*(a^(n-1) + b*a^(n-2) + b^2*a^(n-3) ... +b^(n-1)) | |
| * if we set a = 1, b = w (width) we will turn it to: | |
| * | |
| * 1 - w^n = (1-w)*(1 + w + w^2 + ... +w^(n-1)) (1) | |
| * C = (1 + w + w^2 + ... +w^(n-1)) (2) | |
| * | |
| * is perfectly a number of children in the tree with width w. | |
| * So we can calculate C from (1): | |
| * | |
| * 1 - w^n = (1-w)*C => C = (1-w^n)/(1-w) (3) | |
| * | |
| * (2) takes (n-1) sums and (n-2) multiplication (or (n-2) exponentiations). | |
| * (3) takes n+1 multiplications (or one exponentiation), 2 subtractions, | |
| * 1 division. | |
| * However if more optimal exponentiation algorithm is used like this | |
| * (https://en.wikipedia.org/wiki/Exponentiation_by_squaring) number of | |
| * multiplications will be O(log(n)). | |
| * In case of (2) we will still need all the intermediate powers which | |
| * doesn't allow to benefit from efficient exponentiation. | |
| * As it is now - int_pow(x, n) is a primitive O(n) algorithm. | |
| * | |
| * w = 1 is a special case that will lead to divide by 0. | |
| * as C (2) corresponds to a full number of nodes in the tree including | |
| * the root - we need to return analog in the w=1 tree which is | |
| * (depth+1). | |
| */ | |
| return (width == 1) ? | |
| (depth+1) : (1 - (int_pow(width, (depth+1)))) / (1 - width); | |
| } | |
| static inline int dep(int total, int width) | |
| { | |
| int i; | |
| int x = 0; | |
| for (i = 1; x < total-1; i++) { | |
| x += int_pow(width, i); | |
| } | |
| return i-1; | |
| } | |
| static int search_tree(int id, int node, int max_children, int width, | |
| int *parent_id, int *next_max_children, int *depth) | |
| { | |
| int current, next, next_children; | |
| int i; | |
| *depth = *depth + 1; | |
| current = node + 1; | |
| next_children = (max_children / width) - 1; | |
| if (id == current) { | |
| *parent_id = node; | |
| *next_max_children = next_children; | |
| return 1; | |
| } | |
| for (i = 1; i <= width; i++) { | |
| next = current + next_children + 1; | |
| if (id == next) { | |
| *parent_id = node; | |
| *next_max_children = next_children; | |
| return 1; | |
| } | |
| if (id > current && id < next) { | |
| return search_tree(id, current, next_children, width, | |
| parent_id, next_max_children, | |
| depth); | |
| } | |
| current = next; | |
| } | |
| *parent_id = -1; | |
| *next_max_children = -1; | |
| return 0; | |
| } | |
| void | |
| reverse_tree_info(int rank, int num_nodes, int width, | |
| int *parent, int *num_children, | |
| int *depth, int *max_depth) | |
| { | |
| int max_children; | |
| int p, c; | |
| /* sanity check */ | |
| if (rank >= num_nodes) { | |
| *parent = -1; | |
| *num_children = -1; | |
| *depth = -1; | |
| *max_depth = -1; | |
| return; | |
| } | |
| /* | |
| * If width is more than nodes total, then don't bother trying to | |
| * figure out the tree as there isn't a tree. All nodes just directly | |
| * talk to the controller. | |
| */ | |
| if (width > num_nodes) { | |
| *parent = -1; | |
| *num_children = 0; | |
| *depth = 0; /* not used currently */ | |
| *max_depth = 0; /* not used currently */ | |
| return; | |
| } | |
| *max_depth = dep(num_nodes, width); | |
| if (rank == 0) { | |
| *parent = -1; | |
| *num_children = num_nodes - 1; | |
| *depth = 0; | |
| return; | |
| } | |
| max_children = geometric_series(width, *max_depth); | |
| *depth = 0; | |
| search_tree(rank, 0, max_children, width, &p, &c, depth); | |
| if ((rank + c) >= num_nodes) | |
| c = num_nodes - rank - 1; | |
| *parent = p; | |
| *num_children = c; | |
| return; | |
| } | |
| int reverse_tree_direct_children(int rank, int num_nodes, int width, | |
| int depth, int *children) | |
| { | |
| int current, child_distance; | |
| int max_depth, sub_depth, max_rank_children; | |
| int i; | |
| /* no children if tree is disabled */ | |
| if (width > num_nodes) | |
| return 0; | |
| max_depth = dep(num_nodes, width); | |
| sub_depth = max_depth - depth; | |
| if( sub_depth == 0 ){ | |
| return 0; | |
| } | |
| max_rank_children = geometric_series(width, sub_depth); | |
| current = rank + 1; | |
| child_distance = (max_rank_children / width); | |
| for (i = 0; i < width && current < num_nodes; i++) { | |
| children[i] = current; | |
| current += child_distance; | |
| } | |
| return i; | |
| } |