-
Mathieu Faverge authoredMathieu Faverge authored
parameters.c 23.00 KiB
/**
*
* @file parameters.c
*
* @copyright 2019-2024 Bordeaux INP, CNRS (LaBRI UMR 5800), Inria,
* Univ. Bordeaux. All rights reserved.
***
*
* @brief Chameleon auxiliary routines for testing structures
*
* @version 1.3.0
* @author Lucas Barros de Assis
* @author Mathieu Faverge
* @author Alycia Lisito
* @author Lionel Eyraud-Dubois
* @date 2023-07-05
*
*/
#include "testings.h"
extern parameter_t parameters[];
void print_usage( const char* prog_name )
{
parameter_t *param = parameters;
#if !defined(CHAMELEON_TESTINGS_VENDOR)
if ( CHAMELEON_Initialized() ) {
int rank = CHAMELEON_Comm_rank();
if (rank != 0) {
return;
}
}
#endif
printf( "Usage:\n"
" %s -o|--op operation_name [options]\n"
" %s -f|--file input_file [options]\n",
prog_name, prog_name );
while( param->helper != NULL )
{
char str[STR_MAX_LENGTH];
/* This is not an option, we skip it */
if ( !(param->flags & PARAM_OPTION) ) {
param++;
continue;
}
/* This is an option header */
if ( param->name == NULL ) {
printf( "\n %s:\n", param->helper );
param++;
continue;
}
if ( param->shname > 0 ) {
snprintf( str, STR_MAX_LENGTH, "-%c, --%s",
param->shname, param->name );
}
else {
snprintf( str, STR_MAX_LENGTH, " --%s",
param->name );
}
/* If an argument is needed, add " x" */
if ( param->has_arg > 0 ) {
int len = strlen(str);
assert( len < (STR_MAX_LENGTH-3) );
str[ len ] = ' ';
str[ len+1 ] = 'x';
str[ len+2 ] = '\0';
}
printf( " %-23s %s\n",
str, param->helper );
param++;
}
printf( "\n"
"For example: %s -H -o gemm -t 2 -m 2000 -n 2000 -k 2000 -b 200\n"
" will run one gemm with three matrices of size 2000x2000 each and a tile size of 200.\n"
" The output will be in the human readable format\n"
"\n", prog_name );
#if !defined(CHAMELEON_TESTINGS_VENDOR)
printf( "Remarks about timing:\n"
" Timings are reported respectively as 'tsub' for the graph submission time, and 'time'\n"
" for the execution time.\n"
" By default the synchronous tile interface is used to perform the timings. 'tsub' is null.\n"
" If the --async option is enabled, then the asynchronous interface is called. 'tsub' reports\n"
" the task submission time, and 'time' the execution time that includes 'tsub'.\n"
" If the --splitsub option is enabled, then the asynchronous interface is called and task\n"
" submission is fully performed before starting the computation. 'tsub' reports the\n"
" task submission time, and 'time' the execution time excluding 'tsub'.\n"
" Note that the 'gflops' field is always computed with 'time'\n" );
#endif
}
/**
********************************************************************************
*
* @brief Get the list of values associated to a given parameter
*
*******************************************************************************
*
* @param[in] name
* The name of the parameter we are interested in.
*
* @return NULL if no parameter exists with this name, otherwise the pointer to
* the list of values associated to this parameter.
*
*******************************************************************************
*/
vallist_t *
parameters_getlist( const char *name )
{
parameter_t *param = parameters_getbyname( name );
if ( param == NULL ) {
return NULL;
}
else {
return param->vallist;
}
}
/**
********************************************************************************
*
* @brief Parses a list in form A1, A2, ..., An and insert the values in an
* argument list.
*
*******************************************************************************
*
* @param[inout] param
* The parameter associated to the list.
* On exit, the list of values are added to the parameter list of
* possible values.
*
* @param[in] liststr
* The string that holds the list
*
*******************************************************************************
*/
void
parameters_read_list( parameter_t *param,
const char *liststr )
{
const char *delim = ", \n";
char *str = strdup( liststr );
char *token, *saveptr;
vallist_t *previous, *current;
/* Initialize the list items */
previous = NULL;
current = param->vallist;
/* Move to the end of the list if some parameters have already been registered */
while( current != NULL ) {
previous = current;
current = current->next;
}
assert( current == NULL );
token = strtok_r( str, delim, &saveptr );
while ( token != NULL ) {
current = calloc( 1, sizeof(vallist_t) );
/* Read the value */
current->value = param->read( token );
/* Insert at the end of the list */
if ( previous != NULL ) {
previous->next = current;
}
else {
/* Nothing was in the list */
param->vallist = current;
}
previous = current;
/* Move to the next token */
token = strtok_r( NULL, delim, &saveptr );
}
free( str );
}
/**
********************************************************************************
*
* @brief Parses a list in form start:end[:step] and inserts the values in an
* argument list.
*
*******************************************************************************
*
* @param[inout] param
* The parameter associated to the list.
* On exit, the range of values are added to the parameter list of
* possible values.
*
* @param[in] rangestr
* The string that holds the range
*
* @param[in] min
* The minimum value available
*
* @param[in] max
* The maximum value available
*
*******************************************************************************
*/
void
parameters_read_intrange( parameter_t *param,
const char *rangestr,
int min, int max )
{
int start, end, step, count;
vallist_t *previous, *current;
max = (max == -1) ? INT32_MAX : max;
count = sscanf( rangestr, "%d:%d:%d", &start, &end, &step );
if ( count < 2 ) {
fprintf(stderr, "Incorrect range syntax (%s): data skipped\n", rangestr );
return;
}
else if (count == 2) {
step = 1;
}
/* Check the range */
if ( (start < min) || (start > max) ||
(end < min) || (end > max) )
{
/* Try to shift to 0 to see if now we fit */
start += min;
end += min;
if ( (start < min) || (start > max) ||
(end < min) || (end > max) )
{
fprintf( stderr, "Incorrect range values outside the possible ranges [%d:%d]",
min, max );
if ( min > 0 ) {
fprintf( stderr, " or [%d:%d]\n", 0, max-min );
}
else {
fprintf( stderr, "\n" );
}
}
}
/* Initialize the list items */
previous = NULL;
current = param->vallist;
/* Move to the end of the list if some parameters have already been registered */
while( current != NULL ) {
previous = current;
current = current->next;
}
assert( current == NULL );
while ( start <= end ) {
current = calloc( 1, sizeof(vallist_t) );
/* Read the value */
current->value.ival = start;
/* Insert at the end of the list */
if ( previous != NULL ) {
previous->next = current;
}
else {
/* Nothing was in the list */
param->vallist = current;
}
previous = current;
start += step;
}
}
/**
********************************************************************************
*
* @brief Wrapper to parse a list or range of values associated to a parameter.
*
*******************************************************************************
*
* @param[inout] param
* The parameter associated to the list.
* On exit, the range of values are added to the parameter list of
* possible values.
*
* @param[in] values
* The string that holds the range of list of values
*
*******************************************************************************
*/
void
parameters_read( parameter_t *param,
const char *values )
{
int range;
if ( values == NULL ) {
fprintf( stderr, "Error passing NULL parameter to read\n" );
exit(EXIT_FAILURE);
}
range = (values != NULL) && ( strchr( values, ':' ) != NULL );
/* If we have a ranged of integer values */
if ( range )
{
switch ( param->valtype ) {
case TestValInt:
parameters_read_intrange( param, values, 0, -1 );
break;
case TestTrans:
parameters_read_intrange( param, values, ChamNoTrans, ChamConjTrans );
break;
case TestUplo:
parameters_read_intrange( param, values, ChamUpper, ChamUpperLower );
break;
case TestDiag:
parameters_read_intrange( param, values, ChamNonUnit, ChamUnit );
break;
case TestSide:
parameters_read_intrange( param, values, ChamLeft, ChamRight );
break;
case TestNormtype:
parameters_read_intrange( param, values, ChamOneNorm, ChamMaxNorm );
break;
default:
fprintf( stderr, "parameters_read: range is not available for this datatype (%d)\n",
param->valtype );
}
return;
}
parameters_read_list( param, values );
}
/**
********************************************************************************
*
* @brief Generic function to add value(s) to a given parameter
*
*******************************************************************************
*
* @param[inout] param
* The parameter that will receive the value
* On exit, the value(s) (switch, list, range, ...) is/are added to the
* parameter list of possible values
*
* @param[in] values
* The string that holds the values (list, range, NULL if switch)
*
*******************************************************************************
*/
void
parameters_addvalues( parameter_t *param,
const char *values )
{
if ( param->has_arg == 0 ) {
param->value.ival = 1;
}
else if ( param->has_arg == 1 ) {
param->value = param->read( values );
}
else {
parameters_read( param, values );
}
}
/**
********************************************************************************
*
* @brief Parses an input test file.
*
*******************************************************************************
*
* @param[in] filename
* The name of the input file.
*
*******************************************************************************
*/
void
parameters_read_file( const char *filename )
{
FILE *fp;
const char *delim = " =";
char *saveptr;
char *line_read, *line;
char *name, *values;
size_t len = 256;
parameter_t *param;
fp = fopen( filename, "r" );
if ( fp == NULL ) {
fprintf( stderr, "Error reading input file %s\n", filename );
perror("fopen");
exit(1);
}
len = 256;
line_read = malloc( len * sizeof( char ) );
while ( getline( &line_read, &len, fp ) != -1 )
{
line = line_read;
/* Ignores comments and empty lines */
if ( (line[0] == '#' ) ||
(line[0] == '\n') )
{
continue;
}
/* Removes possible extra spaces */
while ( line[0] == ' ' ) {
line++;
}
/* Reads the parameter name and values */
name = strtok_r( line, delim, &saveptr );
values = strtok_r( NULL, "", &saveptr );
/* Goes for the listed values */
while ( (values[0] == ' ') ||
(values[0] == '=') )
{
values++;
}
param = parameters_getbyname( name );
if ( param == NULL ) {
fprintf( stderr, "Parameter %s is not know. We skip it\n", name );
continue;
}
parameters_addvalues( param, values );
}
free(line_read);
fclose(fp);
}
#if !defined(CHAMELEON_TESTINGS_VENDOR)
int
parameters_compute_q( int p )
{
parameter_t *param;
int np = CHAMELEON_Comm_size();
if ( (np % p) != 0 ) {
fprintf( stderr, "ERROR: The number of processes (%d) must be a multiple of P (%d)\n", np, p );
exit(1);
}
param = parameters_get( 'Q' );
param->value.ival = np / p;
return param->value.ival;
}
/**
********************************************************************************
*
* @brief Helper function to generate the testing descriptors with the right
* data distrbution.
*
*******************************************************************************
*
* @param[in] filename
* The name of the input file.
*
*******************************************************************************
*/
int
parameters_desc_create( const char *id, CHAM_desc_t **descptr, cham_flttype_t dtyp,
int mb, int nb, int lm, int ln, int m, int n )
{
custom_dist_t *custom_args = NULL;
const char *custom = parameters_getvalue_str( "custom" );
intptr_t mtxfmt = parameters_getvalue_int( "mtxfmt" );
int rc;
mtxfmt = -mtxfmt; /* Inverse sign to get the defined values */
if ( !custom ) {
int P = parameters_getvalue_int( "P" );
int Q = parameters_compute_q( P );
rc = CHAMELEON_Desc_Create(
descptr, (void*)mtxfmt, dtyp, mb, nb, mb * nb, lm, ln, 0, 0, m, n, P, Q );
(*descptr)->name = id;
return rc;
}
if ( ((void*)mtxfmt) == CHAMELEON_MAT_ALLOC_GLOBAL ) {
fprintf( stderr, "In parameters_desc_create, cannot use custom distributions with global matrix allocation (Use --mtxfmt=1)\n" );
return CHAMELEON_ERR_ILLEGAL_VALUE;
}
rc = chameleon_getrankof_custom_init( &custom_args, custom );
if ( rc != CHAMELEON_SUCCESS ) {
return rc;
}
rc = CHAMELEON_Desc_Create_User(
descptr, (void*)mtxfmt, dtyp, mb, nb, mb * nb, lm, ln, 0, 0, m, n, CHAMELEON_Comm_size(), 1,
NULL, NULL, chameleon_getrankof_custom, custom_args );
(*descptr)->name = id;
return rc;
}
/**
*******************************************************************************
*
* @brief Helper function to destroy the testing descriptors.
*
*******************************************************************************
*
* @param[inout] descptr
* The descriptor to destroy. On exit the descriptor can no longer be used.
*
*******************************************************************************
*/
int
parameters_desc_destroy(CHAM_desc_t **descptr)
{
CHAM_desc_t *desc;
if ( descptr == NULL ) {
return CHAMELEON_ERR_ILLEGAL_VALUE;
}
desc = *descptr;
if ( desc == NULL ) {
return CHAMELEON_ERR_ILLEGAL_VALUE;
}
if ( desc->get_rankof_init_arg ) {
if ( desc->get_rankof_init == chameleon_getrankof_custom ) {
chameleon_getrankof_custom_destroy( (custom_dist_t**)&(desc->get_rankof_init_arg) );
}
}
return CHAMELEON_Desc_Destroy( descptr );
}
#endif
void
parameters_getopt_init( char *optstring,
struct option **longopts )
{
parameter_t *param = parameters;
int i;
int nboptions = 0;
int strpos = 0;
while( param->helper != NULL )
{
/* This is not an option, we skip it */
if ( !(param->flags & PARAM_OPTION) ||
(param->name == NULL) )
{
param++;
continue;
}
nboptions++;
if ( param->shname < 0 ) {
param++;
continue;
}
optstring[strpos] = param->shname;
strpos++;
assert( strpos < STR_MAX_LENGTH );
if ( param->has_arg > 0 ) {
optstring[strpos] = ':';
strpos++;
assert( strpos < STR_MAX_LENGTH );
}
param++;
}
optstring[strpos] = '\0';
/* Now, let's generate the long opt if needed */
#if defined(CHAMELEON_HAVE_GETOPT_LONG)
if ( longopts != NULL ) {
struct option *opt;
*longopts = calloc( nboptions+1, sizeof( struct option ) );
opt = *longopts;
param = parameters;
for ( i=0; i<nboptions; i++, opt++, param++ ) {
/* Look for a valid option */
while ( !(param->flags & PARAM_OPTION) ||
(param->name == NULL) )
{
param++;
}
opt->name = param->name;
opt->has_arg = ( param->has_arg > 0 ) ? 1 : 0;
opt->flag = NULL;
opt->val = param->shname;
}
}
#endif
}
parameter_t *
parameters_get( int shname )
{
parameter_t *param = parameters;
while( param->helper != NULL )
{
/* This is not an option, we skip it */
if ( param->name == NULL ) {
param++;
continue;
}
if ( shname == param->shname ) {
return param;
}
param++;
}
fprintf( stderr, "parameters_get could not find parameter %d(%c)\n", shname, shname );
return NULL;
}
int
parameters_getvalue_int( const char *name )
{
parameter_t *param = parameters;
while( param->helper != NULL )
{
/* This is not an option, we skip it */
if ( param->name == NULL ) {
param++;
continue;
}
if ( strcasecmp( name, param->name ) != 0 ) {
param++;
continue;
}
if ( param->has_arg > 1 ) {
fprintf( stderr, "parameters_getvalue_int should not be called with parameter %s\n", name );
return -1;
}
if ( param->valtype != TestValInt ) {
fprintf( stderr, "parameters_getvalue_int has been called with a non integer parameter (%s)\n", name );
return -1;
}
return param->value.ival;
}
fprintf( stderr, "parameters_getvalue_int could not find parameter %s\n", name );
return -1;
}
double
parameters_getvalue_fixdbl( const char *name )
{
parameter_t *param = parameters;
while( param->helper != NULL )
{
/* This is not an option, we skip it */
if ( param->name == NULL ) {
param++;
continue;
}
if ( strcasecmp( name, param->name ) != 0 ) {
param++;
continue;
}
if ( param->has_arg > 1 ) {
fprintf( stderr, "parameters_getvalue_double should not be called with parameter %s\n", name );
return -1;
}
if ( param->valtype != TestValDouble ) {
fprintf( stderr, "parameters_getvalue_double has been called with a non float parameter (%s)\n", name );
return -1;
}
return param->value.dval;
}
fprintf( stderr, "parameters_getvalue_int could not find parameter %s\n", name );
return -1;
}
char *
parameters_getvalue_str( const char *name )
{
parameter_t *param = parameters;
while( param->helper != NULL )
{
/* This is not an option, we skip it */
if ( param->name == NULL ) {
param++;
continue;
}
if ( strcasecmp( name, param->name ) != 0 ) {
param++;
continue;
}
if ( param->has_arg > 1 ) {
fprintf( stderr, "parameters_getvalue_str should not be called with parameter %s\n", name );
return NULL;
}
if ( param->valtype != TestString ) {
fprintf( stderr, "parameters_getvalue_str has been called with a non string parameter (%s)\n", name );
return NULL;
}
return param->value.str;
}
fprintf( stderr, "parameters_getvalue_str could not find parameter %s\n", name );
return NULL;
}
parameter_t *
parameters_getbyname( const char *name )
{
parameter_t *param = parameters;
while( param->helper != NULL )
{
/* This is not an option, we skip it */
if ( param->name == NULL ) {
param++;
continue;
}
if ( strcasecmp( name, param->name ) != 0 ) {
param++;
continue;
}
/* if ( param->has_arg < 2 ) { */
/* fprintf( stderr, "parameters_getbyname should not be called with parameter %s\n", name ); */
/* return NULL; */
/* } */
return param;
}
fprintf( stderr, "parameters_getbyname could not find parameter %s\n", name );
return NULL;
}
void parameters_parser( int argc, char **argv )
{
int opt;
char optstring[STR_MAX_LENGTH];
struct option *longopts = NULL;
parameter_t *param;
parameters_getopt_init( optstring, &longopts );
#if defined(CHAMELEON_HAVE_GETOPT_LONG)
while ((opt = getopt_long(argc, argv, optstring, longopts, NULL)) != -1)
#else
while ((opt = getopt(argc, argv, optstring)) != -1)
#endif
{
switch(opt) {
case 'h':
print_usage(argv[0]);
exit(0);
case '?': /* error from getopt[_long] */
exit(1);
break;
default:
param = parameters_get( opt );
if ( param == NULL ) {
print_usage(argv[0]);
exit(1);
}
parameters_addvalues( param, optarg );
}
}
if ( longopts != NULL ) {
free( longopts );
}
#if !defined(CHAMELEON_TESTINGS_VENDOR)
/* Force Async if splitsub is enabled */
{
int splitsub = parameters_getvalue_int( "splitsub" );
if ( splitsub ) {
param = parameters_get( 's' );
if ( param == NULL ) {
print_usage(argv[0]);
exit(1);
}
parameters_addvalues( param, NULL );
#if defined(CHAMELEON_RUNTIME_SYNC)
fprintf( stderr, "Spliting the submission and the execution stages is not possible when the option CHAMELEON_RUNTIME_SYNC is enabled\n" );
exit(0);
#endif
}
}
#endif
}
void
parameters_destroy()
{
parameter_t *param = parameters;
vallist_t *current, *next;
while( param->helper != NULL )
{
/* This is not an option, we skip it */
if ( param->has_arg < 2 ) {
param++;
continue;
}
current = param->vallist;
while ( current != NULL )
{
next = current->next;
free( current );
current = next;
}
param++;
}
return;
}