Commit d9622de8 authored by Kenneth Moreland's avatar Kenneth Moreland
Browse files

Add new radix-kr single image composite algorithm

The radix-kr is essentially the radix-k algorithm with some ideas from
2-3 swap added in. Rather than forcing all the k's in radix-k to be
factors, radix-kr allows the k to have a remainder when splitting the
process. The remaining processes participate in that round by splitting
their image and sending them off, but they receive nothing that round
and then go idle. Although this adds some imbalance, it does not add
much. It also makes keeping track of partitions easier than 2-3 swap.
parent b3afdbeb
......@@ -27,6 +27,7 @@ SET(ICET_SRCS
../strategies/vtree.c
../strategies/bswap.c
../strategies/radixk.c
../strategies/radixkr.c
../strategies/tree.c
../strategies/automatic.c
)
......
......@@ -219,6 +219,7 @@ ICET_EXPORT const char *icetGetStrategyName(void);
#define ICET_SINGLE_IMAGE_STRATEGY_BSWAP (IceTEnum)0x7002
#define ICET_SINGLE_IMAGE_STRATEGY_TREE (IceTEnum)0x7003
#define ICET_SINGLE_IMAGE_STRATEGY_RADIXK (IceTEnum)0x7004
#define ICET_SINGLE_IMAGE_STRATEGY_RADIXKR (IceTEnum)0x7005
ICET_EXPORT void icetSingleImageStrategy(IceTEnum strategy);
......
......@@ -21,8 +21,8 @@ void icetAutomaticCompose(const IceTInt *compose_group,
IceTSizeType *piece_offset)
{
if (group_size > 1) {
icetRaiseDebug("Doing radix-k compose");
icetInvokeSingleImageStrategy(ICET_SINGLE_IMAGE_STRATEGY_RADIXK,
icetRaiseDebug("Doing radix-kr compose");
icetInvokeSingleImageStrategy(ICET_SINGLE_IMAGE_STRATEGY_RADIXKR,
compose_group,
group_size,
image_dest,
......@@ -34,7 +34,7 @@ void icetAutomaticCompose(const IceTInt *compose_group,
*result_image = input_image;
*piece_offset = 0;
} else {
icetRaiseDebug("Clearing pixels");
icetRaiseDebug("Clearing pixels");
icetClearSparseImage(input_image);
*result_image = input_image;
*piece_offset = 0;
......
......@@ -1495,7 +1495,7 @@ static IceTBoolean radixkTryTelescopeSendReceive(IceTInt *main_group,
return ICET_TRUE;
}
ICET_EXPORT IceTBoolean icetRadixTelescopeSendReceiveTest(void)
ICET_EXPORT IceTBoolean icetRadixkTelescopeSendReceiveTest(void)
{
IceTInt main_group_size;
......
/* -*- c -*- *******************************************************/
/*
* Copyright (C) 2010 Sandia Corporation
* Under the terms of Contract DE-AC04-94AL85000 with Sandia Corporation,
* the U.S. Government retains certain rights in this software.
*
* This source code is released under the New BSD License.
*/
/* The Radix-k algorithm was designed by Tom Peterka at Argonne National
Laboratory.
Copyright (c) University of Chicago
Permission is hereby granted to use, reproduce, prepare derivative works, and
to redistribute to others.
The Radix-k algorithm was ported to IceT by Wesley Kendall from University
of Tennessee at Knoxville.
*/
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <mpi.h>
#include <string.h>
#include <IceT.h>
#include <IceTDevCommunication.h>
#include <IceTDevDiagnostics.h>
#include <IceTDevImage.h>
#define RADIXKR_SWAP_IMAGE_TAG_START 2200
#define RADIXKR_RECEIVE_BUFFER ICET_SI_STRATEGY_BUFFER_0
#define RADIXKR_SEND_BUFFER ICET_SI_STRATEGY_BUFFER_1
#define RADIXKR_SPARE_BUFFER ICET_SI_STRATEGY_BUFFER_2
#define RADIXKR_INTERLACED_IMAGE_BUFFER ICET_SI_STRATEGY_BUFFER_3
#define RADIXKR_PARTITION_INFO_BUFFER ICET_SI_STRATEGY_BUFFER_4
#define RADIXKR_RECEIVE_REQUEST_BUFFER ICET_SI_STRATEGY_BUFFER_5
#define RADIXKR_SEND_REQUEST_BUFFER ICET_SI_STRATEGY_BUFFER_6
#define RADIXKR_FACTORS_ARRAY_BUFFER ICET_SI_STRATEGY_BUFFER_7
#define RADIXKR_SPLIT_OFFSET_ARRAY_BUFFER ICET_SI_STRATEGY_BUFFER_8
#define RADIXKR_SPLIT_IMAGE_ARRAY_BUFFER ICET_SI_STRATEGY_BUFFER_9
typedef struct radixkrRoundInfoStruct {
IceTInt k; /* k value for this round. */
IceTInt r; /* remainder for this round (number of processes dropped). */
IceTInt step; /* Ranks jump by this much in this round. */
IceTInt split_factor; /* Number of new image partitions made from each partition. */
IceTBoolean has_image; /* True if local process collects image data this round. */
IceTBoolean last_partition; /* True if local process is part of the last partition. */
IceTInt partition_index; /* Index of partition at this round (if has_image true). */
} radixkrRoundInfo;
typedef struct radixkrInfoStruct {
radixkrRoundInfo *rounds; /* Array of per round info. */
IceTInt num_rounds;
} radixkrInfo;
typedef struct radixkrPartnerInfoStruct {
IceTInt rank; /* Rank of partner. */
IceTSizeType offset; /* Offset of partner's partition in image. */
IceTVoid *receiveBuffer; /* A buffer for receiving data from partner. */
IceTSparseImage sendImage; /* A buffer to hold data being sent to partner */
IceTSparseImage receiveImage; /* Hold for received non-composited image. */
IceTInt compositeLevel; /* Level in compositing tree for round. */
} radixkrPartnerInfo;
typedef struct radixkrPartnerGroupInfoStruct {
radixkrPartnerInfo *partners; /* Array of partners in this group. */
IceTInt num_partners; /* Number of partners in this group. */
} radixkrPartnerGroupInfo;
/* BEGIN_PIVOT_FOR(loop_var, low, pivot, high)...END_PIVOT_FOR() provides a
special looping mechanism that iterates over the numbers pivot, pivot-1,
pivot+1, pivot-2, pivot-3,... until all numbers between low (inclusive) and
high (exclusive) are visited. Any numbers outside [low,high) are skipped. */
#define BEGIN_PIVOT_FOR(loop_var, low, pivot, high) \
{ \
IceTInt loop_var##_true_iter; \
IceTInt loop_var##_max = 2*( ((pivot) < ((high)+(low))/2) \
? ((high)-(pivot)) : ((pivot)-(low)+1) ); \
for (loop_var##_true_iter = 1; \
loop_var##_true_iter < loop_var##_max; \
loop_var##_true_iter ++) { \
if ((loop_var##_true_iter % 2) == 0) { \
loop_var = (pivot) - loop_var##_true_iter/2; \
if (loop_var < (low)) continue; \
} else { \
loop_var = (pivot) + loop_var##_true_iter/2; \
if ((high) <= loop_var) continue; \
}
#define END_PIVOT_FOR() \
} \
}
static IceTInt radixkrFindFloorLog2(IceTInt x)
{
IceTInt lg;
for (lg = 0; (IceTUInt)(1 << lg) <= (IceTUInt)x; lg++);
lg--;
return lg;
}
static void radixkrSwapImages(IceTSparseImage *image1, IceTSparseImage *image2)
{
IceTSparseImage old_image1 = *image1;
*image1 = *image2;
*image2 = old_image1;
}
/* radixkrGetPartitionIndices
my position in each round forms an num_rounds-dimensional vector
[round 0 pos, round 1 pos, ... round num_rounds-1 pos]
where pos is my position in the group of partners within that round
inputs:
info: holds the number of rounds and k values for each round
group_rank: my rank in composite order (compose_group in icetRadixkrCompose)
outputs:
fills info with step, split_factor, has_image, and partition_index for
each round.
*/
static void radixkrGetPartitionIndices(radixkrInfo info,
IceTInt group_size,
IceTInt group_rank)
{
IceTInt step; /* step size in rank for a lattice direction */
IceTInt total_partitions;
IceTInt current_group_size;
IceTInt current_round;
IceTInt max_image_split;
icetGetIntegerv(ICET_MAX_IMAGE_SPLIT, &max_image_split);
total_partitions = 1;
step = 1;
current_group_size = group_size;
current_round = 0;
while (current_round < info.num_rounds) {
radixkrRoundInfo *round_info = &info.rounds[current_round];
IceTInt split;
if (total_partitions * round_info->k <= max_image_split) {
split = round_info->k;
} else {
split = max_image_split/total_partitions;
}
total_partitions *= split;
round_info->split_factor = split;
round_info->partition_index = (group_rank / step) % round_info->k;
round_info->has_image = (group_rank < (step * current_group_size));
round_info->has_image &= (round_info->partition_index < split);
round_info->last_partition = ((group_rank/step) >= (group_size/step-1));
round_info->step = step;
current_group_size = current_group_size / round_info->k;
step *= round_info->k;
current_round++;
}
}
static radixkrInfo radixkrGetK(IceTInt compose_group_size,
IceTInt group_rank)
{
/* Divide the world size into groups that are closest to the magic k
value. */
radixkrInfo info;
IceTInt magic_k;
IceTInt max_num_k;
IceTInt next_divide;
/* Special case of when compose_group_size == 1. */
if (compose_group_size < 2) {
info.rounds = icetGetStateBuffer(RADIXKR_FACTORS_ARRAY_BUFFER,
sizeof(radixkrRoundInfo) * 1);
info.rounds[0].k = 1;
info.rounds[0].r = 0;
info.rounds[0].step = 1;
info.rounds[0].split_factor = 1;
info.rounds[0].has_image = ICET_TRUE;
info.rounds[0].partition_index = 0;
info.num_rounds = 1;
return info;
}
info.num_rounds = 0;
icetGetIntegerv(ICET_MAGIC_K, &magic_k);
/* The maximum number of factors possible is the floor of log base 2. */
max_num_k = radixkrFindFloorLog2(compose_group_size);
info.rounds = icetGetStateBuffer(RADIXKR_FACTORS_ARRAY_BUFFER,
sizeof(radixkrRoundInfo) * max_num_k);
next_divide = compose_group_size;
while (next_divide > 1) {
IceTInt next_k;
IceTInt next_r;
if (next_divide > magic_k) {
/* First guess is the magic k. */
next_k = magic_k;
next_r = next_divide % magic_k;
} else {
/* Can't do better than doing direct send. */
next_k = next_divide;
next_r = 0;
}
/* If the k value we picked is not a perfect factor, try to find
* another value that is a perfect factor or has a smaller remainder.
*/
if (next_r > 0) {
IceTInt try_k;
for (try_k = magic_k-1; try_k >= 2; try_k--) {
IceTInt try_r = next_divide % try_k;
if (try_r < next_r) {
next_k = try_k;
next_r = try_r;
if (next_r == 0) { break; }
}
}
}
/* Set the k value in the array. */
info.rounds[info.num_rounds].k = next_k;
info.rounds[info.num_rounds].r = next_r;
next_divide /= next_k;
info.num_rounds++;
if (info.num_rounds > max_num_k) {
icetRaiseError("Somehow we got more factors than possible.",
ICET_SANITY_CHECK_FAIL);
}
}
/* Sanity check to make sure that the k's actually multiply to the number
* of processes. */
{
IceTInt product = 1;
IceTInt round;
for (round = info.num_rounds-1; round >= 0; --round) {
product = product*info.rounds[round].k + info.rounds[round].r;
}
if (product != compose_group_size) {
icetRaiseError("Product of k's not equal to number of processes.",
ICET_SANITY_CHECK_FAIL);
}
}
radixkrGetPartitionIndices(info, compose_group_size, group_rank);
return info;
}
/* radixkrGetFinalPartitionIndex
After radix-k completes on a group of size p, the image is partitioned into
p pieces (with a caveat for the maximum number of partitions and remainder).
This function finds the index for the final partition (with respect to all
partitions, not just one within a round) for a given rank.
inputs:
info: information about rounds
returns:
index of final global partition. -1 if this process does not end up with
a piece.
*/
static IceTInt radixkrGetFinalPartitionIndex(const radixkrInfo *info)
{
IceTInt current_round;
IceTInt partition_index;
partition_index = 0;
for (current_round = 0; current_round < info->num_rounds; current_round++) {
const radixkrRoundInfo *r = &info->rounds[current_round];
if (r->has_image) {
partition_index *= r->split_factor;
partition_index += r->partition_index;
} else /* !r->has_image */ {
/* local process has no partition */
return -1;
}
}
return partition_index;
}
/* radixkrGetTotalNumPartitions
After radix-kr completes on a group of size p, the image is partitioned into
p pieces with some set maximum. This function finds the index for the final
partition (with respect to all partitions, not just one within a round) for
a given rank.
inputs:
info: information about rounds
returns:
total number of partitions created
*/
static IceTInt radixkrGetTotalNumPartitions(const radixkrInfo *info)
{
IceTInt current_round;
IceTInt num_partitions;
num_partitions = 1;
for (current_round = 0; current_round < info->num_rounds; current_round++) {
num_partitions *= info->rounds[current_round].split_factor;
}
return num_partitions;
}
/* radixkrGetGroupRankForFinalPartitionIndex
After radix-kr completes on a group of size p, the image is partitioned into
p pieces. This function finds the group rank for the given index of the
final partition. This function is the inverse if
radixkrGetFinalPartitionIndex.
inputs:
info: information about rounds
partition_index: index of final global partition
returns:
group rank holding partition_index
*/
static IceTInt radixkrGetGroupRankForFinalPartitionIndex(
const radixkrInfo *info, IceTInt partition_index)
{
IceTInt current_round;
IceTInt partition_up_to_round;
IceTInt group_rank;
partition_up_to_round = partition_index;
group_rank = 0;
for (current_round = info->num_rounds - 1;
current_round >= 0;
current_round--) {
const IceTInt step = info->rounds[current_round].step;
const IceTInt split = info->rounds[current_round].split_factor;
group_rank += step * (partition_up_to_round % split);
partition_up_to_round /= split;
}
return group_rank;
}
/* radixkrGetPartners
gets the ranks of my trading partners
inputs:
round_info: structure with information on the current round
remaining_partitions: Number of pieces the image will be split into by
the end of the algorithm.
compose_group: array of world ranks representing the group of processes
participating in compositing (passed into icetRadixkrCompose)
group_rank: Index in compose_group that represents me
start_size: Size of image partition that is being divided in current_round
output:
partner_group: Structure of information about the group of processes that
are partnering in this round.
*/
static radixkrPartnerGroupInfo radixkrGetPartners(
const radixkrRoundInfo *round_info,
IceTInt remaining_partitions,
const IceTInt *compose_group,
IceTInt group_rank,
IceTSizeType start_size)
{
const IceTInt current_k = round_info->k;
const IceTInt current_r = round_info->r;
const IceTInt split_factor = round_info->split_factor;
const IceTInt step = round_info->step;
radixkrPartnerGroupInfo p_group;
IceTInt num_partners;
IceTBoolean receiving_data;
IceTBoolean sending_data;
IceTVoid *recv_buf_pool;
IceTVoid *send_buf_pool;
IceTSizeType partition_num_pixels;
IceTSizeType sparse_image_size;
IceTInt first_partner_group_rank;
IceTInt i;
num_partners = current_k;
if (round_info->last_partition) {
num_partners += current_r;
}
p_group.partners = icetGetStateBuffer(
RADIXKR_PARTITION_INFO_BUFFER,
sizeof(radixkrPartnerInfo) * num_partners);
p_group.num_partners = num_partners;
/* Allocate arrays that can be used as send/receive buffers. */
receiving_data = round_info->has_image;
if (split_factor > 1) {
partition_num_pixels
= icetSparseImageSplitPartitionNumPixels(start_size,
split_factor,
remaining_partitions);
sending_data = ICET_TRUE;
} else {
/* Not really splitting image, and the receiver does not need to send
* at all. */
partition_num_pixels = start_size;
sending_data = !receiving_data;
}
sparse_image_size = icetSparseImageBufferSize(partition_num_pixels, 1);
if (receiving_data) {
recv_buf_pool = icetGetStateBuffer(RADIXKR_RECEIVE_BUFFER,
sparse_image_size * num_partners);
} else {
recv_buf_pool = NULL;
}
if (sending_data) {
/* Only need send buff when splitting, always need when splitting. */
send_buf_pool = icetGetStateBuffer(RADIXKR_SEND_BUFFER,
sparse_image_size * split_factor);
} else {
send_buf_pool = NULL;
}
first_partner_group_rank
= group_rank % step + (group_rank/(step*current_k))*(step*current_k);
for (i = 0; i < num_partners; i++) {
radixkrPartnerInfo *p = &p_group.partners[i];
IceTInt partner_group_rank = first_partner_group_rank + i*step;
p->rank = compose_group[partner_group_rank];
/* To be filled later. */
p->offset = -1;
if (receiving_data) {
p->receiveBuffer = ((IceTByte*)recv_buf_pool + i*sparse_image_size);
} else {
p->receiveBuffer = NULL;
}
if (sending_data && (i < split_factor)) {
IceTVoid *send_buffer
= ((IceTByte*)send_buf_pool + i*sparse_image_size);
p->sendImage = icetSparseImageAssignBuffer(send_buffer,
partition_num_pixels, 1);
} else {
p->sendImage = icetSparseImageNull();
}
p->receiveImage = icetSparseImageNull();
p->compositeLevel = -1;
}
return p_group;
}
/* As applicable, posts an asynchronous receive for each process from which
we are receiving an image piece. */
static IceTCommRequest *radixkrPostReceives(radixkrPartnerGroupInfo p_group,
const radixkrRoundInfo *round_info,
IceTInt current_round,
IceTInt remaining_partitions,
IceTSizeType start_size)
{
IceTCommRequest *receive_requests;
IceTSizeType partition_num_pixels;
IceTSizeType sparse_image_size;
IceTInt tag;
IceTInt i;
/* If not collecting any image partition, post no receives. */
if (!round_info->has_image) { return NULL; }
receive_requests =icetGetStateBuffer(
RADIXKR_RECEIVE_REQUEST_BUFFER,
p_group.num_partners * sizeof(IceTCommRequest));
if (round_info->split_factor) {
partition_num_pixels
= icetSparseImageSplitPartitionNumPixels(start_size,
round_info->split_factor,
remaining_partitions);
} else {
partition_num_pixels = start_size;
}
sparse_image_size = icetSparseImageBufferSize(partition_num_pixels, 1);
tag = RADIXKR_SWAP_IMAGE_TAG_START + current_round;
for (i = 0; i < p_group.num_partners; i++) {
radixkrPartnerInfo *p = &p_group.partners[i];
if (i != round_info->partition_index) {
receive_requests[i] = icetCommIrecv(p->receiveBuffer,
sparse_image_size,
ICET_BYTE,
p->rank,
tag);
p->compositeLevel = -1;
} else {
/* No need to send to myself. */
receive_requests[i] = ICET_COMM_REQUEST_NULL;
}
}
return receive_requests;
}
/* As applicable, posts an asynchronous send for each process to which we are
sending an image piece. */
static IceTCommRequest *radixkrPostSends(radixkrPartnerGroupInfo p_group,
const radixkrRoundInfo *round_info,
IceTInt current_round,
IceTInt remaining_partitions,
IceTSizeType start_offset,
const IceTSparseImage image)
{
IceTCommRequest *send_requests;
IceTInt *piece_offsets;
IceTSparseImage *image_pieces;
IceTInt tag;
IceTInt i;
tag = RADIXKR_SWAP_IMAGE_TAG_START + current_round;
if (round_info->split_factor > 1) {
send_requests=icetGetStateBuffer(
RADIXKR_SEND_REQUEST_BUFFER,
round_info->split_factor * sizeof(IceTCommRequest));
piece_offsets = icetGetStateBuffer(
RADIXKR_SPLIT_OFFSET_ARRAY_BUFFER,
round_info->split_factor * sizeof(IceTInt));
image_pieces = icetGetStateBuffer(
RADIXKR_SPLIT_IMAGE_ARRAY_BUFFER,
round_info->split_factor * sizeof(IceTSparseImage));
for (i = 0; i < round_info->split_factor; i++) {
image_pieces[i] = p_group.partners[i].sendImage;
}
icetSparseImageSplit(image,
start_offset,
round_info->split_factor,
remaining_partitions,
image_pieces,
piece_offsets);
/* The pivot for loop arranges the sends to happen in an order such that
those to be composited first in their destinations will be sent
first. This serves little purpose other than to try to stagger the
order of sending images so that no everyone sends to the same process
first. */
BEGIN_PIVOT_FOR(i,
0,
round_info->partition_index,
round_info->split_factor) {
radixkrPartnerInfo *p = &p_group.partners[i];
p->offset = piece_offsets[i];
if (i != round_info->partition_index) {
IceTVoid *package_buffer;
IceTSizeType package_size;
icetSparseImagePackageForSend(image_pieces[i],
&package_buffer, &package_size);
send_requests[i] = icetCommIsend(package_buffer,