blob: daa4c1367385cd27f9c84fdbe6d770a201e69798 [file] [log] [blame]
/*****************************************************************************\
* 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;
}