diff --git a/benchmark/benchmark.cc b/benchmark/benchmark.cc index 6717b033c9d9f06868839f4b775dea46772416d2..f4bc04cdafb1ac723cfef0271de693aee04ecd4f 100644 --- a/benchmark/benchmark.cc +++ b/benchmark/benchmark.cc @@ -19,6 +19,7 @@ #include "environ.h" #include "format_units.h" #include "format_print.h" +#include "output_sion.h" #include <cstdlib> #include <cstdio> #include <cstring> @@ -107,10 +108,8 @@ inline unsigned int Benchmark::getNumSteps() const { const int INVALID_RANK = -8; // Random negative Number const int INVALID_STEP = -9; // Random negative Number -void Benchmark::computeRankStepMappings() { +void Benchmark::permuteSteps(){ stepPermutation.resize(getNumSteps()); - getRankFromStep.resize(getNumSteps()); - getStepFromRank.resize(size()); std::unique_ptr<StopwatchI> rootWatch = Stopwatchfactory::getRootWatch(cl->rank()); @@ -128,7 +127,27 @@ void Benchmark::computeRankStepMappings() { } rootWatch->stop(); printTimingIfRoot(cl->rank(), "[randvec]", rootWatch->getDuration()); +} + +void Benchmark::printCommunicationScheme(){ + #ifdef DEBUG_PRINT_COMMUNICATION_SCHEME + for(std::int i = 0; i < size(); i++) { + barrier(); + if( rank() == i) { + std::cout << rank() << ": getRankFromStep: " << getRankFromStep << "\n"; + std::cout << rank() << ": getStepFromRank: " << getStepFromRank << "\n"; + std::cout << rank() << ": stepPermutation: " << stepPermutation << "\n"; + } + } + #endif +} + +void Benchmark::computeRankStepMappings() { + getRankFromStep.resize(getNumSteps()); + getStepFromRank.resize(size()); + + std::unique_ptr<StopwatchI> rootWatch = Stopwatchfactory::getRootWatch(cl->rank()); rootWatch->start(); std::fill(getRankFromStep.begin(), getRankFromStep.end(), INVALID_RANK); std::fill(getStepFromRank.begin(), getStepFromRank.end(), INVALID_STEP); @@ -137,20 +156,229 @@ void Benchmark::computeRankStepMappings() { } else { mapWith1FactorAlgorithm(args->num_randomize_tasks > 0); } + + //Permute Steps + stepPermutation.resize(getNumSteps()); + permuteSteps(); rootWatch->stop(); printTimingIfRoot(cl->rank(), "[getpart]", rootWatch->getDuration()); + //DEBUG: Print Communication Scheme #ifdef DEBUG_PRINT_COMMUNICATION_SCHEME - for(int i = 0; i < size(); i++) { - barrier(); - if( rank() == i) { - std::cout << rank() << ": getRankFromStep: " << getRankFromStep << "\n"; - std::cout << rank() << ": getStepFromRank: " << getStepFromRank << "\n"; - std::cout << rank() << ": stepPermutation: " << stepPermutation << "\n"; + printCommunicationScheme(); + #endif +} + +int Benchmark::checkRankStepMappings(std::vector<uint64_t> com){ + const std::size_t num_steps=com.size(); + int err=0; + std::size_t i, j, k; + std::unique_ptr<StopwatchI> rootWatch = Stopwatchfactory::getRootWatch(cl->rank()); + + /* Bad Connection Vector */ + std::vector<std::unique_ptr<std::uint64_t[]>> bc; + + /* Check Bounds */ + rootWatch->start(); + k=0; + for(i=0;i<num_steps;i++){ + if((int)(com[i])>=size()){ //TODO: Currently only using 1st + std::unique_ptr<std::uint64_t[]> tmp(new std::uint64_t[3]{(std::uint64_t)rank(),com[i],(std::uint64_t)i}); //TODO: Currently only using 1st + bc.push_back(std::move(tmp)); + k++; } } - barrier(); - #endif + rootWatch->stop(); + printTimingIfRoot(cl->rank(), "[checkcombounds]", rootWatch->getDuration()); + + /* Print Out-Of-Bounds Connections */ + if(rank()==0){ + if(k>0){ + fprintf(stderr,"Task %d identified the following %zu out-of-bounds connections:",rank(),k); + for(j=0;j<k;j++){ + fprintf(stderr," %" PRIu64 "->%" PRIu64 "(%" PRIu64 ")",bc[j][0],bc[j][1],bc[j][2]); + } + fprintf(stderr,"\n"); + err=1; + } + for(i=1;i<(size_t)size();i++){ + cl->recv(i,&k,1); + if(k>0){ + fprintf(stderr,"Task %zu identified the following %zu out-of-bounds connections:",i,k); + for(j=0;j<k;j++){ + std::uint64_t tmp[3]; + cl->recv(i,tmp,3); + fprintf(stderr," %" PRIu64 "->%" PRIu64 "(%" PRIu64 ")",tmp[0],tmp[1],tmp[2]); + } + fprintf(stderr,"\n"); + err=1; + } + } + }else{ + cl->send(0,&k,1); + for(j=0;j<k;j++){ + cl->send(0,bc[j].get(),3); + } + } + + /* Bin Communication Matrix According To Partner */ + rootWatch->start(); + std::vector<std::uint64_t>* con=new std::vector<std::uint64_t>[size()]; + for(i=0;i<(size_t)size();i++){ + for(j=0;j<(num_steps);j++){ + if((size_t)(com[j])==i) con[i].push_back(j+1); //TODO: Currently only using 1st + } + } + rootWatch->stop(); + printTimingIfRoot(cl->rank(), "[bincom]", rootWatch->getDuration()); + + /* Check For Duplicate Connections */ + bc.resize(0); + rootWatch->start(); + k=0; + for(i=0;i<(size_t)size();i++){ + if(con[i].size()>1){ + for(j=1;j<con[i].size();j++){ + std::unique_ptr<std::uint64_t[]> tmp(new std::uint64_t[3]{(std::uint64_t)rank(),i,con[i][j]}); + bc.push_back(std::move(tmp)); + k++; + } + } + } + rootWatch->stop(); + printTimingIfRoot(cl->rank(), "[checkcomduplicates]", rootWatch->getDuration()); + + /* Print Duplicate Connections */ + if(rank()==0){ + if(k>0){ + fprintf(stderr,"Task %d identified the following %zu duplicate connections:",rank(),k); + for(j=0;j<k;j++){ + fprintf(stderr," %" PRIu64 "->%" PRIu64 "(%" PRIu64 ")",bc[j][0],bc[j][1],bc[j][2]); + } + fprintf(stderr,"\n"); + err=1; + } + for(i=1;i<(std::size_t)size();i++){ + cl->recv(i,&k,1); + if(k>0){ + fprintf(stderr,"Task %zu identified the following %zu duplicate connections:",i,k); + for(j=0;j<k;j++){ + std::uint64_t tmp[3]; + cl->recv(i,tmp,3); + fprintf(stderr," %" PRIu64 "->%" PRIu64 "(%" PRIu64 ")",tmp[0],tmp[1],tmp[2]); + } + fprintf(stderr,"\n"); + err=1; + } + } + }else{ + cl->send(0,&k,1); + for(j=0;j<k;j++){ + cl->send(0,bc[j].get(),3); + } + } + + /* Check Connections With Partners */ + bc.resize(0); + rootWatch->start(); + k=0; + for(std::size_t i=0;i<(std::size_t)size()-1;i++){ + int partner; + std::uint64_t step; + const int idle=(i*size()/2)%(size()-1); + if(rank()==(size()-1)) partner=idle; + else if(rank()==idle) partner=size()-1; + else partner=(i-rank()+size()-1)%(size()-1); + if(rank()<partner){ + cl->send(partner,con[partner].data(),1); + cl->recv(partner,&step ,1); + }else{ + cl->recv(partner,&step ,1); + cl->send(partner,con[partner].data(),1); + } + if(con[partner][0]!=step){ + std::unique_ptr<std::uint64_t[]> tmp(new std::uint64_t[3]{(std::uint64_t)partner,(std::uint64_t)rank(),step}); + bc.push_back(std::move(tmp)); + k++; + } + } + rootWatch->stop(); + printTimingIfRoot(cl->rank(), "[checkcom]", rootWatch->getDuration()); + delete[] con; + + /* Print Bad Connections */ + if(rank()==0){ + if(k>0){ + fprintf(stderr,"Task %d identified the following %zu bad connections:",rank(),k); + for(j=0;j<k;j++){ + fprintf(stderr," %" PRIu64 "->%" PRIu64 "(%" PRIu64 ")",bc[j][0],bc[j][1],bc[j][2]); + } + fprintf(stderr,"\n"); + err=1; + } + for(i=1;i<(std::size_t)size();i++){ + cl->recv(i,&k,1); + if(k>0){ + fprintf(stderr,"Task %zu identified the following %zu bad connections:",i,k); + for(j=0;j<k;j++){ + std::uint64_t tmp[3]; + cl->recv(i,tmp,3); + fprintf(stderr," %" PRIu64 "->%" PRIu64 "(%" PRIu64 ")",tmp[0],tmp[1],tmp[2]); + } + fprintf(stderr,"\n"); + err=1; + } + } + }else{ + cl->send(0,&k,1); + for(j=0;j<k;j++){ + cl->send(0,bc[j].get(),3); + } + } + + /* Broadcast Error To All Ranks */ + cl->bcast(0,&err,1); + + return(err); +} + +void Benchmark::loadRankStepMappings(const std::size_t num_iterations, const std::size_t iteration){ + std::unique_ptr<StopwatchI> rootWatch = Stopwatchfactory::getRootWatch(cl->rank()); + + //Load Communication Matrix + rootWatch->start(); + std::vector<uint64_t> com=cl->read_COM_parallel(args,num_iterations,iteration); + if(com.size()==0){ + fatal("Failed to read communication pattern."); + } + rootWatch->stop(); + printTimingIfRoot(cl->rank(), "[loadcom]", rootWatch->getDuration()); + + //TODO: Currently only extracting 1 iteration + const std::size_t num_steps=com.size(); + + //Test Loaded Communication Matrix + if(checkRankStepMappings(com)){ + fatal("Errors detected in loaded communication pattern!"); + } + + //Configure Communication Pattern + rootWatch->start(); + getRankFromStep.resize(num_steps); + getStepFromRank.resize(size()); + getRankFromStep.assign(com.begin(),com.end()); + std::fill(getStepFromRank.begin(), getStepFromRank.end(), INVALID_STEP); + for(std::size_t step=0;step<num_steps;step++){ + getStepFromRank.at(getRankFromStep.at(step))=step; + } + rootWatch->stop(); + printTimingIfRoot(cl->rank(), "[applycom]", rootWatch->getDuration()); + + //Permute Steps + permuteSteps(); + + //DEBUG: Print Communication Scheme + printCommunicationScheme(); } std::vector<int> Benchmark::oneFactorAlgorithm(const int size, const int rank) { @@ -819,8 +1047,18 @@ void Benchmark::run_Stresstest() { } } -void Benchmark::run_Measuringtest() { - const std::size_t NUM_ITERATIONS = randomizeTasks() ? args->num_randomize_tasks : 1; +void Benchmark::run_Measuringtest(){ + std::uint64_t tmp=0; + if(args->sion_com[0]!='\0'){ + if(cl->rank()==0){ + tmp=get_num_iterations_from_pattern_sion(args,size()); + cl->bcast(0,&tmp,1); + }else{ + cl->bcast(0,&tmp,1); + } + } + cl->barrier(); + const std::size_t NUM_ITERATIONS = (tmp!=0) ? tmp : (randomizeTasks() ? args->num_randomize_tasks : 1); newStatsVec.resize(NUM_ITERATIONS); double timeWork=0.0, timeTotal=0.0; // Total time = work time + all-to-all time (+ overhead) @@ -829,7 +1067,11 @@ void Benchmark::run_Measuringtest() { std::printf("\n\nIteration:%4ld of %4ld\n", iteration, NUM_ITERATIONS); std::printf("Preparing\n"); } - computeRankStepMappings(); + if(args->sion_com[0]!='\0'){ + loadRankStepMappings(NUM_ITERATIONS,iteration); + }else{ + computeRankStepMappings(); + } stats = &newStatsVec.at( iteration - 1 ); stats->initialize(cl.get(), args); if (0 == cl->rank()) { diff --git a/benchmark/benchmark.h b/benchmark/benchmark.h index e6990278a1a38abbc110fbfec593939dc9899845..fe55717a7eb9822129094574e4b5603101be63b8 100644 --- a/benchmark/benchmark.h +++ b/benchmark/benchmark.h @@ -70,6 +70,10 @@ namespace linktest{ std::vector<int> oneFactorAlgorithm(const int size, const int rank); void mapWith1FactorAlgorithm(bool randomizeTasks); void mapWithBisection(); + void permuteSteps(); + void printCommunicationScheme(); + void loadRankStepMappings(const std::size_t num_iterations, const std::size_t iteration); + int checkRankStepMappings(std::vector<uint64_t> com); int work_alltoall(); /** * Executes semi-, bi- or unidirectional tests diff --git a/benchmark/cconfig.h b/benchmark/cconfig.h index 325ef6a991ff71a8ce6a7eaa52eea7bc3cf2e9d7..622e39734eed5beb567a71732ce54ea8d4c92759 100644 --- a/benchmark/cconfig.h +++ b/benchmark/cconfig.h @@ -49,6 +49,10 @@ /* Linktest alignment string for output files. Marks the end of the Binary Header */ #define END_HEADER "END_HEADER" +/* LinkTest identification for input communication SION files (offset: 0x00) */ +#define LINKTEST_COM_ID "LKTST_COM" + + /* Linktest alignment string for output files. Marks end of thread output */ #define END_BLOCK "END_BLOCK" diff --git a/benchmark/cmdline.cc b/benchmark/cmdline.cc index 6adab296a1df33b5c804875938bd12d24af3f6a0..3084bad942cb08f4bd409d49def6925bb6620b25 100644 --- a/benchmark/cmdline.cc +++ b/benchmark/cmdline.cc @@ -319,6 +319,16 @@ auto linktest_all_cmdline_argdefs = { .required = false, .help = "Seed for task randomization", .p = &cmdline_args.seed_randomize_tasks + }, + Argument{ + .type = Argument::kString, + .nargs = 1, + .optshort = "", + .optlong = "sion-communication-pattern", + .defaultval = "", + .required = false, + .help = "SION file containing communication patterns", + .p = &cmdline_args.sion_com } }; @@ -709,8 +719,16 @@ const struct linktest_args *bcast_cmdline_args(VirtualCluster* cl, cl->bcast(root,&tmp->buf_mt_seed ,1); cl->bcast(root,&tmp->check_buffers ,1); - /* Broadcast Output Filename */ + /* Broadcast Input Communication Pattern Filename */ std::uint64_t buf_size; + if(cl->rank()==root) buf_size=tmp->sion_com.size()+1; + cl->bcast(root,&buf_size,1); + char buf_sion_com[buf_size]; + if(cl->rank()==root) std::snprintf(buf_sion_com, sizeof(buf_sion_com), "%s", tmp->sion_com.c_str()); + cl->bcast(root, buf_sion_com, buf_size); + tmp->sion_com = buf_sion_com; + + /* Broadcast Output Filename */ if(cl->rank()==root) buf_size=tmp->output.size()+1; cl->bcast(root,&buf_size,1); char buf_output[buf_size]; @@ -846,8 +864,11 @@ void print_cmdline_args(const struct linktest_args* args){ std::printf("Seed for random comm. schemes: %-" PRIu64 "\n" ,args->seed_randomize_tasks); } std::printf("Write output SION file? %-s\n" ,getProtocol()); + if(args->sion_com[0]!='\0'){ + std::printf("Input Com. Pat. SION filename: %-s\n" ,args->sion_com.c_str()); + } if(args->do_nosion==0) { - std::printf("Output SION filename: %-s\n" ,args->output.c_str()); + std::printf("Output SION filename: %-s\n" ,args->output.c_str()); } std::printf(HLINE); } diff --git a/benchmark/cmdline.h b/benchmark/cmdline.h index 46644b1b5504ef9cdb8c41c0d1d2fa6d3037e392..fe9e6da2d1ed443506fc90a527a42d52b0641741 100644 --- a/benchmark/cmdline.h +++ b/benchmark/cmdline.h @@ -47,6 +47,7 @@ struct linktest_args { AllocatorType alloc_typ; std::string virtual_cluster_implementation; std::string memory_buffer_allocator; + std::string sion_com; std::string output; }; diff --git a/benchmark/config.h b/benchmark/config.h index d97ec1f7b8909cf0a1ae3dbdc5afd44bc5ae1136..117d6ca37845c47d66c5c97091ad689fc5d367fd 100644 --- a/benchmark/config.h +++ b/benchmark/config.h @@ -22,6 +22,7 @@ constexpr std::int32_t GIT_HASH_LEN_ = GIT_HASH_LEN; // sizeo constexpr char GIT_HASH_SHORT_[] = GIT_HASH; constexpr std::int32_t GIT_HASH_SHORT_LEN_ = GIT_HASH_SHORT_LEN; // sizeof(GIT_HASH_SHORT_); constexpr char LINKTEST_ENVIRON_PREFIX_[] = LINKTEST_ENVIRON_PREFIX; +constexpr char LINKTEST_COM_ID_[] = LINKTEST_COM_ID; constexpr char LINKTEST_ID_[] = LINKTEST_ID; constexpr char END_HEADER_[] = END_HEADER; constexpr char END_BLOCK_[] = END_BLOCK; diff --git a/benchmark/output_sion.cc b/benchmark/output_sion.cc index ac8c8d0fff94cd1e8bb92fab82b1fec8c8716855..d1a8ba77c657479e19481d827247f7d6664c2fd1 100644 --- a/benchmark/output_sion.cc +++ b/benchmark/output_sion.cc @@ -21,17 +21,25 @@ #include <cstdlib> #include <cstdio> #include <cstring> +#include <cinttypes> #include <type_traits> +#include <algorithm> #ifdef USE_SION #include <mpi.h> #ifdef __cplusplus - extern "C" { + extern "C" { #endif #include "sion.h" #ifdef __cplusplus - } + } #endif + enum DataType{ + PatternFormatInvalid=0, //Invalid Data Type + PatternFormatCom =1, //COM matrix : Rank-by-Step (Value: Partner) + PatternFormatMat =2, //Communication Matrix: Rank-by-Partner (Value: Step ) + PatternFormatPer =3, //Permutation Vector for Permuting 1-Factor Output - CURRENTLY NOT USED + }; #endif #ifdef USE_SION @@ -62,7 +70,7 @@ static Serializer getSerializer(VirtualCluster* cl, const struct linktest_args * s.addDataSource("args->use_multi_buf" ,&args->use_multi_buf ); s.addDataSource("args->randomize_buffers" ,&args->randomize_buffers ); s.addDataSource("args->check_buffers" ,&args->check_buffers ); - s.addDataSource("args->alloc_typ" ,&args->alloc_typ ); // enum aka int + s.addDataSource("args->alloc_typ" ,&args->alloc_typ ); // uint64_t data s.addDataSource("args->num_msg" ,&args->num_msg ); s.addDataSource("args->len_msg" ,&args->len_msg ); @@ -114,6 +122,27 @@ static Serializer getSerializer(VirtualCluster* cl, const struct linktest_args * return s; } + +static Serializer getSerializedCommunicationPatternHead(const char* const com_id, const uint32_t* const version, const uint64_t* const num_iter){ + /* Note although this function promises not to change its input, + * when reading to these pointers data will be overwritten. + */ + Serializer s; + s.addDataSource(LINKTEST_COM_ID_, com_id , std::strlen(LINKTEST_COM_ID_)+1); + s.addDataSource("version" , version , 3 ); + s.addDataSource("num_iter" , num_iter ); + return s; +} + +static Serializer getSerializedCommunicationPatternBlockHead(const uint8_t* const dtype, const uint64_t* const numel){ + /* Note although this function promises not to change its input, + * when reading to these pointers data will be overwritten. + */ + Serializer s; + s.addDataSource("dtype", dtype); + s.addDataSource("numel", numel); + return s; +} #endif int linktest_output_sion_collect_local_data(VirtualCluster* cl, @@ -389,3 +418,444 @@ int linktest_output_sion_parallel(VirtualCluster* cl, return ERROR; #endif } + +/*************************/ +/* Determine Chunk Sizes */ +/*************************/ +//Should only be executed as root! +#ifdef USE_SION + std::vector<sion_int64> getChunkSizes(char* filename, const int vcl_size){ //BUG: Should be "const char* const filename", but SIONlib 1.7.7 requires "char* filename" + std::unique_ptr<StopwatchI> rootWatch = Stopwatchfactory::getRootWatch(0); //Safe since this should only be executed by root. + auto printTiming = [&rootWatch](std::string step_name){ + printTimingIfRoot(0, step_name, rootWatch->getDuration()); + }; + + /*************/ + /* Open File */ + /*************/ + FILE* fp ; + int ntasks ; + int nfiles ; + std::int32_t fsblksz ; + rootWatch->start(); + auto file_handle = sion_open(filename, + "rb" , + &ntasks , + &nfiles , + NULL , + &fsblksz, + NULL , + &fp ); + rootWatch->stop(); + printTiming("[sioncomopenroot]"); + + /****************************************************/ + /* Check Number Of Tasks File Should Be Opened With */ + /****************************************************/ + if(ntasks!=vcl_size){ + sion_close(file_handle); + fatal("To read %s %d tasks are required, while LinkTest is currently running with %d tasks.",filename,ntasks,vcl_size); + } + + /******************/ + /* Read Meta Data */ + /******************/ + int maxchunks; + sion_int64 globalskip; + sion_int64 start_of_varheader; + sion_int64 *chunksizes; + sion_int64 *globalranks; + sion_int64 *blockcount; + sion_int64 *blocksizes; + sion_get_locations(file_handle, + &ntasks, + &maxchunks, + &globalskip, + &start_of_varheader, + &chunksizes, + &globalranks, + &blockcount, + &blocksizes); + + /*************************/ + /* Determine Chunk Sizes */ + /*************************/ + std::vector<sion_int64> chunk_sizes; + chunk_sizes.resize(ntasks); + memcpy(chunk_sizes.data(),chunksizes,ntasks*sizeof(sion_int64)); + + /**************/ + /* Close File */ + /**************/ + rootWatch->start(); + sion_close(file_handle); + rootWatch->stop(); + printTiming("[sioncomcloseroot]"); + + return(chunk_sizes); + } +#endif + +/*********************************************/ +/* Get Number Of Iterations & Test SION File */ +/*********************************************/ +// Should only be executed with root (0) task! +// If return 0 then error! */ +uint64_t get_num_iterations_from_pattern_sion(const struct linktest_args* const args, const int vcl_size){ + #ifdef USE_SION + /*********************/ + /* Start a Stopwatch */ + /*********************/ + std::unique_ptr<StopwatchI> rootWatch = Stopwatchfactory::getRootWatch(0); //Safe since this should only be executed by root. + auto printTiming = [&rootWatch](std::string step_name){ + printTimingIfRoot(0, step_name, rootWatch->getDuration()); + }; + + /************************/ + /* Determine Chunk Size */ + /************************/ + char filename[args->sion_com.size()+1]; + /* ToDo: Below is a super awkward work around for SIONlib which + * does not require a const filename. This is not inline with + * our const command-line arguments, hence we need to create a + * new temporary string to hold the filename to open. This is to + * be fixed when a new version of SIONlib that rectifies this + * bug is available. + */ + std::snprintf(filename, sizeof(filename), "%s", args->sion_com.c_str()); + std::vector<sion_int64> chunk_sizes=getChunkSizes(filename,vcl_size); + sion_int64* chunksz=chunk_sizes.data(); + + /*************/ + /* Open File */ + /*************/ + FILE* fp ; + int ntasks ; + int nfiles ; + std::int32_t fsblksz ; + int* globalranks=NULL; + rootWatch->start(); + auto file_handle = sion_open(filename, + "rb,posix" , + &ntasks , + &nfiles , + &chunksz , + &fsblksz , + &globalranks, + &fp ); + rootWatch->stop(); + printTiming("[sioncomrootopen]"); + #ifdef DEBUG_COM_INPUT + printf("%s->ntasks: %d\n",args->sion_com.c_str(),ntasks);fflush(stdout); + #endif + + /****************************************************/ + /* Check Number Of Tasks File Should Be Opened With */ + /****************************************************/ + if(ntasks!=vcl_size){ + sion_close(file_handle); + fatal("To read %s %d tasks are required, while LinkTest is currently running with %d tasks.",args->sion_com.c_str(),ntasks,vcl_size); + } + + /*************/ + /* Read Head */ + /*************/ + // Construct Buffer + std::string com_id;com_id.resize(strlen(LINKTEST_COM_ID_)+1); + std::uint32_t version[3]; + std::uint64_t num_iter; + Serializer s=getSerializedCommunicationPatternHead(com_id.c_str(),version,&num_iter); + const size_t buf_size=s.getSize(); + void* const buffer=::operator new(buf_size); + // Read Into Buffer + rootWatch->start(); + if(sion_fread(buffer,buf_size,1,file_handle)!=1){ + error("Failed to read communication-pattern header from SION file."); + } + rootWatch->stop(); + printTiming("[sioncomrootread]"); + //Distribute Data In Buffer + s.read((char*)buffer,0); + com_id[com_id.size()-1]='\0'; //Ensure string is null terminated + #ifdef DEBUG_COM_INPUT + printf("rank=0, com_id=%s, version=%" PRIu32 ".%" PRIu32 ".%" PRIu32 ", num_iterations=%" PRIu64 "\n",com_id.c_str(),version[0],version[1],version[2],num_iter); + fflush(stdout); + #endif + ::operator delete(buffer); + + /**************/ + /* Close File */ + /**************/ + rootWatch->start(); + sion_close(file_handle); + rootWatch->stop(); + printTiming("[sioncomrootclose]"); + + /**************/ + /* Check Head */ + /**************/ + if(std::strcmp(LINKTEST_COM_ID_,com_id.c_str())!=0){ + fatal("%s string not read. Read: %s.",LINKTEST_COM_ID_,com_id.c_str()); + } + if(version[0]!=LINKTEST_VERSION ){ + fatal("LinkTest major version mismatch when reading communication pattern! %" PRId32 "!=%" PRId32,version[0],VERSION_MAJOR); + } + if(version[1]!=LINKTEST_VERSION_SUB ){ + fatal("LinkTest major version mismatch when reading communication pattern! %" PRId32 "!=%" PRId32,version[1],VERSION_MINOR); + } + if(version[2]!=LINKTEST_VERSION_PATCHLEVEL){ + fatal("LinkTest patch version mismatch when reading communication pattern! %" PRId32 "!=%" PRId32,version[2],VERSION_PATCH); + } + + /*******************************/ + /* Return Number Of Iterations */ + /*******************************/ + return num_iter; + #else + error("This version of the benchmark was compiled without SIONlib support."); + return 0; + #endif +} + +/*****************/ +/* SION Read COM */ +/*****************/ +std::vector<uint64_t> sion_read_communication_pattern_for_iteration_parallel([[maybe_unused]] VirtualCluster* cl, [[maybe_unused]] const struct linktest_args* const args, [[maybe_unused]] const std::size_t num_iterations, [[maybe_unused]] const std::size_t iteration){ + // It should not be possible to reach this state without SION, but just in case. + #ifdef USE_SION + /*********************/ + /* Start a Stopwatch */ + /*********************/ + std::unique_ptr<StopwatchI> rootWatch = Stopwatchfactory::getRootWatch(cl->rank()); + auto printTiming = [rank=cl->rank(), &rootWatch](std::string step_name){ + printTimingIfRoot(rank, step_name, rootWatch->getDuration()); + }; + + /************************/ + /* Determine Chunk Size */ + /************************/ + char filename[args->sion_com.size()+1]; + /* ToDo: Below is a super awkward work around for SIONlib which + * does not require a const filename. This is not inline with + * our const command-line arguments, hence we need to create a + * new temporary string to hold the filename to open. This is to + * be fixed when a new version of SIONlib that rectifies this + * bug is available. + */ + std::snprintf(filename, sizeof(filename), "%s", args->sion_com.c_str()); + sion_int64 chunk_size; + if(cl->rank()==0){ + std::vector<sion_int64> chunk_sizes=getChunkSizes(filename,cl->size()); + chunk_size=chunk_sizes[0]; + for(int i=1;i<cl->size();i++) cl->send(i,&chunk_sizes[i],1); + }else{ + cl->recv(0,&chunk_size,1); + } + cl->barrier(); + + /*******************************/ + /* Parameters for Opening File */ + /*******************************/ + FILE* fp; + int filenumber=0; + int nfiles =1; + int fsblksize =0; + char* newfname; + int grank=VirtualClusterSionGenericAdapter::global_rank(*cl); + int lrank=VirtualClusterSionGenericAdapter::local_rank(*cl); + int gsize=VirtualClusterSionGenericAdapter::global_size(*cl); + int lsize=VirtualClusterSionGenericAdapter::local_size(*cl); + + /**************/ + /* Create API */ + /**************/ + auto sion_api=create_and_register_api(args->virtual_cluster_implementation); + + /*************/ + /* Open File */ + /*************/ + rootWatch->start(); + auto file_handle = sion_generic_paropen( + sion_api, + filename, + "rb", + &chunk_size, + &fsblksize, + cl, //gcommgroup + grank, //global rank + gsize, //global comm size + &filenumber, //filenumber + &nfiles, //numfiles + &lrank, //local rank + &lsize, //lsoze + &fp, //fileptr + &newfname //newfname + ); + cl->barrier(); + rootWatch->stop(); + printTiming("[sioncomopenpar]"); + + /*************/ + /* Read Head */ + /*************/ + // Construct Buffer + std::size_t seek_offset=0; + std::string com_id;com_id.resize(strlen(LINKTEST_COM_ID_)+1); + std::uint32_t version[3]; + std::uint64_t num_iter; + Serializer shead=getSerializedCommunicationPatternHead(com_id.c_str(),version,&num_iter); + const size_t head_size=shead.getSize(); + void* const head_buffer=::operator new(head_size); + // Read Into Buffer + rootWatch->start(); + if(sion_fread(head_buffer,head_size,1,file_handle)!=1){ + ::operator delete(head_buffer); + error("Failed to read communication-pattern header from SION file."); + } + rootWatch->stop(); + printTiming("[sioncomreadheadpar]"); + seek_offset+=head_size; + //Distribute Data In Buffer + shead.read((char*)head_buffer,cl->rank()); + #ifdef DEBUG_COM_INPUT + for(int k=0;k<cl->size();k++){ + if(cl->rank()==k){ + printf("rank=%d, com_id=%s, version=%" PRIu32 ".%" PRIu32 ".%" PRIu32 ", num_iterations=%" PRIu64 "\n",cl->rank(),com_id.c_str(),version[0],version[1],version[2],num_iter); + fflush(stdout); + } + cl->barrier(); + } + #endif + ::operator delete(head_buffer); + com_id[com_id.size()-1]='\0'; //Ensure string is null terminated + + /**************/ + /* Check Head */ + /**************/ + if(std::strcmp(LINKTEST_COM_ID_,com_id.c_str())!=0){ + fatal("%s string not read. Read: %s.",LINKTEST_COM_ID_,com_id.c_str()); + } + if(version[0]!=LINKTEST_VERSION ){ + fatal("LinkTest major version mismatch when reading communication pattern! %" PRId32 "!=%" PRId32,version[0],VERSION_MAJOR); + } + if(version[1]!=LINKTEST_VERSION_SUB ){ + fatal("LinkTest major version mismatch when reading communication pattern! %" PRId32 "!=%" PRId32,version[1],VERSION_MINOR); + } + if(version[2]!=LINKTEST_VERSION_PATCHLEVEL){ + fatal("LinkTest patch version mismatch when reading communication pattern! %" PRId32 "!=%" PRId32,version[2],VERSION_PATCH); + } + if(num_iter!=num_iterations){ + fatal("Number of iterations mismatch, expected %" PRId32 " but found " PRId32,num_iterations,num_iter); + } + + /************************/ + /* Skip Past Iterations */ + /************************/ + //TODO: Keep offset from one iteration to the next + // so we can skip past the seek step. + std::uint8_t dtype; + std::uint64_t numel=0; + Serializer sblockhead=getSerializedCommunicationPatternBlockHead(&dtype,&numel); + const std::size_t block_head_size=sblockhead.getSize(); + void* const block_head_buffer=::operator new(block_head_size); + rootWatch->start(); + for(size_t i=1;i<iteration;i++){ + if(sion_fread(block_head_buffer,block_head_size,1,file_handle)!=1){ + ::operator delete(block_head_buffer); + error("Failed to read communication-pattern block header for iteration %zu from SION file.",i); + } + sblockhead.read((char*)block_head_buffer,cl->rank()); + seek_offset+=block_head_size+numel*sizeof(std::uint64_t); + sion_seek(file_handle,SION_CURRENT_RANK,0,seek_offset); + } + rootWatch->stop(); + printTiming("[sioncomseekpar]"); + + /**************************/ + /* Read Current Iteration */ + /**************************/ + rootWatch->start(); + if(sion_fread(block_head_buffer,block_head_size,1,file_handle)!=1){ + ::operator delete(block_head_buffer); + error("Failed to read communication-pattern block header for iteration %zu from SION file.",iteration); + } + sblockhead.read((char*)block_head_buffer,cl->rank()); + #ifdef DEBUG_COM_INPUT + for(int k=0;k<cl->size();k++){ + if(cl->rank()==k){ + printf("rank=%d, dtype=%" PRIu8 ", numel=%" PRIu64 "\n",cl->rank(),dtype,numel); + fflush(stdout); + } + cl->barrier(); + } + #endif + std::vector<uint64_t> com; + switch(dtype){ + case PatternFormatInvalid: + error("Detected invalid pattern format for communication patter %zu!",iteration); + break; + case PatternFormatCom: + com.resize(numel); + if(sion_fread(com.data(),sizeof(std::uint64_t),numel,file_handle)!=numel){ + error("Failed to read communication-pattern for iteration %zu from SION file.",iteration); + } + break; + case PatternFormatMat: + /* We convert here to a com matrix (see case above), only to convert back at a later stage. + * This currently works better though with the program flow. + * TODO: Optimize this! + */ + for(size_t i=0;i<num_iter;i++){ + //Check if numel is equal to number of tasks + if(numel!=(std::uint64_t)cl->size()){ + error("Communication-pattern matrix from SION file is %s!",numel>(uint64_t)cl->size()?"too large":"too small"); + goto r; + } + //Load data + std::uint64_t mat[numel]; + if(sion_fread(mat,sizeof(std::uint64_t),numel,file_handle)!=numel){ + error("Failed to read communication-pattern for iteration %zu from SION file.",iteration); + break; + } + //Determine number of steps + std::uint64_t global_max_step=*std::max_element(&mat[0],&mat[numel-1]); + if(cl->rank()==0){ + std::uint64_t local__max_step; + for(int j=1;j<cl->size();j++){ + cl->recv(j,&local__max_step,1); + global_max_step=(global_max_step>local__max_step)?global_max_step:local__max_step; + } + cl->bcast(0,&global_max_step,1); + }else{ + cl->send( 0,&global_max_step,1); + cl->bcast(0,&global_max_step,1); + } + //Convert to com + com.resize(global_max_step,0); + for(std::uint64_t j=0;j<(std::uint64_t)cl->size();j++){ + const size_t partner=(std::size_t)mat[(std::size_t)j]; + if(partner==0) continue; + com[partner-1]=j; + } + } + break; + default: + error("Detected unknown communication-pattern format for iteration %zu!",iteration); + break; + } + if(com.empty()!=false){ + rootWatch->stop(); + printTiming("[sioncomreadpar]"); + } + +r: rootWatch->start(); + sion_generic_parclose(file_handle); + rootWatch->stop(); + printTiming("[sioncomclosepar]"); + ::operator delete(block_head_buffer); + return com; + #else + error("This version of the benchmark was compiled without SIONlib support."); + return ERROR; + #endif +} diff --git a/benchmark/output_sion.h b/benchmark/output_sion.h index bf9f287697c9ec4e4c743881c5fecfc797516980..b88de55aa47adf08ed676cade422d3a933718c53 100644 --- a/benchmark/output_sion.h +++ b/benchmark/output_sion.h @@ -33,11 +33,22 @@ int linktest_output_sion_funnelled(VirtualCluster* cl, const struct linktest_args *args, const std::vector<LinktestStats>& statsVec); -/* Write in parallel using SIONlib generic API with the same transport layer as the VirtualCluster +/* Write in parallel using SIONlib generic API with the same transport layer + as the VirtualCluster. */ int linktest_output_sion_parallel(VirtualCluster* cl, const struct linktest_args *args, const std::vector<LinktestStats>& statsVec); +/* Determine number of iterations to run based on input SION file + */ +uint64_t get_num_iterations_from_pattern_sion(const struct linktest_args* const args, const int vcl_size); + +/* Read in parallel using SIONlib the communication pattern for a given iteration + */ +std::vector<uint64_t> sion_read_communication_pattern_for_iteration_parallel([[maybe_unused]] VirtualCluster* cl, + [[maybe_unused]] const struct linktest_args* const args, + [[maybe_unused]] const std::size_t num_iterations, + [[maybe_unused]] const std::size_t iteration); #endif diff --git a/benchmark/vcluster.cc b/benchmark/vcluster.cc index 7e435218a52b078d083063118ab110a02ec5f649..4dbb1448c9eb2b8c816686c713d9118389e7fa0d 100644 --- a/benchmark/vcluster.cc +++ b/benchmark/vcluster.cc @@ -528,3 +528,8 @@ int VirtualCluster::write_funnelled(const linktest_args* args, const std::vector { return linktest_output_sion_funnelled(this, args, statsVec); } + +std::vector<std::uint64_t> VirtualCluster::read_COM_parallel(const struct linktest_args* const args, const std::size_t num_iterations, const std::size_t iteration){ + return sion_read_communication_pattern_for_iteration_parallel(this, args, num_iterations, iteration); +} + diff --git a/benchmark/vcluster.h b/benchmark/vcluster.h index 9da08970212e617c301e5d86b9d2991fe796358e..e013395251f10783ba34dff4aad4de3490963cd3 100644 --- a/benchmark/vcluster.h +++ b/benchmark/vcluster.h @@ -256,7 +256,7 @@ public: const struct linktest_args* args, double* time); - /* I/O operations. + /* I/O operations: Part I * * - write_parallel: Write in parallel from all processing elements. * - write_funnelled: Funnel the I/O through the first processing element. @@ -267,6 +267,13 @@ public: virtual int write_parallel(const linktest_args* args, const std::vector<LinktestStats>& statsVec); virtual int write_funnelled(const linktest_args* args, const std::vector<LinktestStats>& statsVec); + /* I/O operations: Part II + * + * - read_COM_parallel: Reads in parallel the communication matrix for the current step. + */ +public: + virtual std::vector<std::uint64_t> read_COM_parallel(const struct linktest_args* const args, const std::size_t num_iterations, const std::size_t iteration); + /* Given the name of the vcluster implementation create an instance. This * function accesses an internal database to map the name of the implementation * to a function that creates the instance. In order for this to work, the diff --git a/misc/MPI_COM_Check.cc b/misc/MPI_COM_Check.cc new file mode 100644 index 0000000000000000000000000000000000000000..9b9e567dd8ea8a8c351447d2c29806874ce741c0 --- /dev/null +++ b/misc/MPI_COM_Check.cc @@ -0,0 +1,275 @@ +#include <stdio.h> +#include <memory> +#include <vector> +#include <mpi.h> + +int main(int argc, char *argv[]){ + int i, j, k, world_size, world_rank; + MPI_Status mpi_status ; + MPI_Request mpi_request; + + /* Initialize MPI */ + int ret=MPI_Init(NULL, NULL); + if (ret)return(ret); + ret=MPI_Comm_size(MPI_COMM_WORLD,&world_size); + if (ret)return(ret); + MPI_Comm_rank(MPI_COMM_WORLD,&world_rank); + if (ret)return(ret); + if(world_size%2){ + if(world_rank==0){printf("This example requires an even number of tasks!\n");fflush(stdout);} + goto FIN_MPI; + } + if(world_size<3){ + if(world_rank==0){printf("This example requires at least 4 tasks!\n");fflush(stdout);} + goto FIN_MPI; + } + + { + /* Generate Communication Matrix */ + // Would normally load each row for each task from disk in parallel + const int num_steps=(world_size==6)?3:((world_size==8)?22:(world_size-1)); + if(world_rank==0){printf("num_steps=%d\n\n",num_steps);fflush(stdout);} + int* const com=(int*)malloc((num_steps)*sizeof(int)); + if(world_size==6){ //Custom version for n=6 to show off custom com matrix and errors. + switch(world_rank){ + case(0): + com[0]=1;com[1]=2;com[2]=3; + break; + case(1): + com[0]=0;com[1]=3;com[2]=2; + break; + case(2): + com[0]=3;com[1]=0;com[2]=1; + break; + case(3): + com[0]=2;com[1]=1;com[2]=0; + break; + case(4): + com[0]=5;com[1]=5;com[2]=4; + break; + case(5): + com[0]=4;com[1]=4;com[2]=5; + break; + } + /* Include Errors */ + // Turn on to see effect + if(world_rank==4)com[1]=2; + if(world_rank==5)com[2]=666; + }else if(world_size==8){ + switch(world_rank){ //Custom version for n=8 to show off faster option for serial 2-CPU node testing. + case(0): + com[ 0]=1;com[ 1]=2;com[ 2]=3;com[ 3]=0;com[ 4]=0;com[ 5]=0;com[ 6]=4;com[ 7]=5; + com[ 8]=6;com[ 9]=7;com[10]=0;com[11]=0;com[12]=0;com[13]=0;com[14]=0;com[15]=0; + com[16]=0;com[17]=0;com[18]=0;com[19]=0;com[20]=0;com[21]=0; + break; + case(1): + com[ 0]=0;com[ 1]=1;com[ 2]=1;com[ 3]=2;com[ 4]=3;com[ 5]=1;com[ 6]=1;com[ 7]=1; + com[ 8]=1;com[ 9]=1;com[10]=4;com[11]=5;com[12]=6;com[13]=7;com[14]=1;com[15]=1; + com[16]=1;com[17]=1;com[18]=1;com[19]=1;com[20]=1;com[21]=1; + break; + case(2): + com[ 0]=2;com[ 1]=0;com[ 2]=2;com[ 3]=1;com[ 4]=2;com[ 5]=3;com[ 6]=2;com[ 7]=2; + com[ 8]=2;com[ 9]=2;com[10]=2;com[11]=2;com[12]=2;com[13]=2;com[14]=4;com[15]=5; + com[16]=6;com[17]=7;com[18]=2;com[19]=2;com[20]=2;com[21]=2; + break; + case(3): + com[ 0]=3;com[ 1]=3;com[ 2]=0;com[ 3]=3;com[ 4]=1;com[ 5]=2;com[ 6]=3;com[ 7]=3; + com[ 8]=3;com[ 9]=3;com[10]=3;com[11]=3;com[12]=3;com[13]=3;com[14]=3;com[15]=3; + com[16]=3;com[17]=3;com[18]=4;com[19]=5;com[20]=6;com[21]=7; + break; + case(4): + com[ 0]=5;com[ 1]=6;com[ 2]=7;com[ 3]=4;com[ 4]=4;com[ 5]=4;com[ 6]=0;com[ 7]=4; + com[ 8]=4;com[ 9]=4;com[10]=1;com[11]=4;com[12]=4;com[13]=4;com[14]=2;com[15]=4; + com[16]=4;com[17]=4;com[18]=3;com[19]=4;com[20]=4;com[21]=4; + break; + case(5): + com[ 0]=4;com[ 1]=5;com[ 2]=5;com[ 3]=6;com[ 4]=7;com[ 5]=5;com[ 6]=5;com[ 7]=0; + com[ 8]=5;com[ 9]=5;com[10]=5;com[11]=1;com[12]=5;com[13]=5;com[14]=5;com[15]=2; + com[16]=5;com[17]=5;com[18]=5;com[19]=3;com[20]=5;com[21]=5; + break; + case(6): + com[ 0]=6;com[ 1]=4;com[ 2]=6;com[ 3]=5;com[ 4]=6;com[ 5]=7;com[ 6]=6;com[ 7]=6; + com[ 8]=0;com[ 9]=6;com[10]=6;com[11]=6;com[12]=1;com[13]=6;com[14]=6;com[15]=6; + com[16]=2;com[17]=6;com[18]=6;com[19]=6;com[20]=3;com[21]=6; + break; + case(7): + com[ 0]=7;com[ 1]=7;com[ 2]=4;com[ 3]=7;com[ 4]=5;com[ 5]=6;com[ 6]=7;com[ 7]=7; + com[ 8]=7;com[ 9]=0;com[10]=7;com[11]=7;com[12]=7;com[13]=1;com[14]=7;com[15]=7; + com[16]=7;com[17]=2;com[18]=7;com[19]=7;com[20]=7;com[21]=3; + break; + } + /* Include Errors */ + // Turn on to see effect + if(world_rank==4)com[1]=2; + if(world_rank==5)com[2]=666; + }else{ + //Default 1 Factor algorithm + for(i=0;i<(num_steps);i++){ + const int idle=(i*world_size/2)%(num_steps); + if(world_rank==(num_steps)) com[i]=idle; + else if(world_rank==idle) com[i]=num_steps; + else com[i]=(i-world_rank+num_steps)%(num_steps); + } + /* Include Errors */ + // Turn on to see effect + if(world_rank==0) com[2]=1; + if(world_rank==1) com[0]=666; + } + + /* Print Communication Matrix */ + for(i=0;i<world_size;i++){ + if(i==world_rank){ + printf("%d: %d",world_rank,com[0]); + for(j=1;j<(num_steps);j++) printf(", %d",com[j]); + printf("\n"); + fflush(stdout); + } + MPI_Barrier(MPI_COMM_WORLD); + } + if(world_rank==0){ + printf("\n"); + fflush(stdout); + } + + /* Bin Communication Matrix According To Partner */ + std::vector<int>* con=new std::vector<int>[world_size]; + for(i=0;i<world_size;i++){ + for(j=0;j<(num_steps);j++){ + if(com[j]==i) con[i].push_back(j); + } + } + + /* Print Connections Per Pair For Each Rank */ + for(i=0;i<world_size;i++){ + if(i==world_rank){ + for(j=0;j<world_size;j++){ + const int tmp=con[j].size(); + if(j==i){ + printf("%d does not communicate in %d steps",world_rank,tmp); + if(tmp){ + printf(": %d",con[j][0]); + for(k=1;k<tmp;k++){ + printf(", %d",con[j][k]); + } + }else printf("."); + }else{ + printf("%d communicates with %d in %d steps",world_rank,j,tmp); + if(tmp){ + printf(": %d",con[j][0]); + for(k=1;k<tmp;k++){ + printf(", %d",con[j][k]); + } + }else printf("."); + } + printf("\n"); + } + printf("\n"); + fflush(stdout); + } + MPI_Barrier(MPI_COMM_WORLD); + } + if(world_rank==0){ + printf("\n"); + fflush(stdout); + } + + /* Test Pairs */ + std::vector<std::unique_ptr<int[]>> bc; + // Check Bounds + k=0; + for(i=0;i<num_steps;i++){ + if(com[i]>=world_size){ + std::unique_ptr<int[]> tmp(new int[3]{world_rank,com[i],i}); + bc.push_back(std::move(tmp)); + k++; + } + } + // Check Partners + std::vector<int> steps; + for(i=1;i<world_size;i++){ + const int recv_partner=(world_size+world_rank-i)%world_size; + const int send_partner=(world_rank+i)%world_size; + int recv_size; + const int send_size=con[send_partner].size(); + int sync_send; + const int sync_recv=0; + //Exchange Sizes + MPI_Isend(&send_size,1,MPI::INT,send_partner,send_partner,MPI_COMM_WORLD,&mpi_request); + MPI_Recv(&recv_size,1,MPI::INT,recv_partner,world_rank,MPI_COMM_WORLD,&mpi_status); + MPI_Isend(&sync_recv,1,MPI::INT,recv_partner,recv_partner,MPI_COMM_WORLD,&mpi_request); + steps.reserve(recv_size); + MPI_Recv(&sync_send,1,MPI::INT,send_partner,world_rank,MPI_COMM_WORLD,&mpi_status); + //Send and receive data + MPI_Isend(con[send_partner].data(),send_size,MPI::INT,send_partner,send_partner,MPI_COMM_WORLD,&mpi_request); + MPI_Recv(steps.data(),recv_size,MPI::INT,recv_partner,world_rank,MPI_COMM_WORLD,&mpi_status); + //Test Data + for(j=0;j<recv_size;j++){ + if(com[steps[j]]!=recv_partner){ + std::unique_ptr<int[]> tmp(new int[3]{recv_partner,world_rank,steps[j]}); + bc.push_back(std::move(tmp)); + k++; + } + } + } + MPI_Barrier(MPI_COMM_WORLD); + + /* Print Bad Connections */ + for(i=0;i<world_size;i++){ + if(i==world_rank){ + printf("%d identified the following %d bad connections:",world_rank,k); + for(j=0;j<k;j++){ + printf(" %d->%d(%d)",bc[j][0],bc[j][1],bc[j][2]); + } + printf("\n"); + } + } + MPI_Barrier(MPI_COMM_WORLD); + if(world_rank==0){ + printf("\n"); + fflush(stdout); + } + + /* Collect Bad Connections */ + if(world_rank==0){ + int recv_size; + for(i=1;i<world_size;i++){ + const int sync_recv=0; + MPI_Recv(&recv_size,1,MPI::INT,i,i,MPI_COMM_WORLD,&mpi_status); + MPI_Isend(&sync_recv,1,MPI::INT,i,i,MPI_COMM_WORLD,&mpi_request); + for(j=0;j<recv_size;j++){ + std::unique_ptr<int[]> buf(new int[3]); + MPI_Recv(buf.get(),3,MPI::INT,i,i,MPI_COMM_WORLD,&mpi_status); + bc.push_back(std::move(buf)); + k++; + } + } + }else{ + int sync_recv; + for(i=1;i<world_size;i++){ + if(i==world_rank){ + MPI_Isend(&k,1,MPI::INT,0,i,MPI_COMM_WORLD,&mpi_request); + MPI_Recv(&sync_recv,1,MPI::INT,0,i,MPI_COMM_WORLD,&mpi_status); + for(j=0;j<k;j++){ + MPI_Send(bc[j].get(),3,MPI::INT,0,i,MPI_COMM_WORLD); + } + } + } + } + MPI_Barrier(MPI_COMM_WORLD); + + /* Print Bad Connections Again */ + if(world_rank==0){ + printf("Bad Connections:\n"); + for(i=0;i<k;i++){ + printf("%d->%d (step: %d)\n",bc[i][0],bc[i][1],bc[i][2]); + } + } + + /* Free Data */ + free(com); + delete[] con; + } + + /* Finalize MPI Environment. */ + FIN_MPI: MPI_Finalize(); +} \ No newline at end of file diff --git a/misc/build.bash b/misc/build.bash new file mode 100755 index 0000000000000000000000000000000000000000..cb1e09d75cd28f33578979e180e76fcb0afd348e --- /dev/null +++ b/misc/build.bash @@ -0,0 +1,14 @@ +#!/bin/bash + +# The following function will generate communication pattern that you can load into LinkTest and use. +mpicc -Wall -O2 -D__USE_POSIX -D_FILE_OFFSET_BITS=64 -I<<<PATH TO SIONlib INSTALL>>>/sionlib_linux_gomp/include -D_SION_LINUX -DSION_MPI generateLinkTestComPattern.c -L<<<PATH TO SIONlib INSTALL>>>/sionlib_linux_gomp/lib -lsionmpi_64 -lsiongen_64 -lsionser_64 -lsioncom_64 -L/usr/local/lib -lsionfwd-client -pthread /usr/lib/x86_64-linux-gnu/openmpi/lib/libmpi.so -lsioncom_64_lock_none -o generateLinkTestComPattern.exe +# execute: mpirun -n <<<NUMBER OF RANKS>>> ./generateLinkTestComPattern.exe + +# The following function is an example of how to check LinkTest communication patterns +mpicxx MPI_COM_Check.cc -o MPI_COM_Check.exe +# execute: mpirun -n <<<NUMBER OF RANKS>>> ./MPI_COM_Check.exe + +# The foll +gcc -O2 permutationTester.c -o permutationTester.exe +# execute: ./permutationTester.exe + diff --git a/misc/generateLinkTestComPattern.c b/misc/generateLinkTestComPattern.c new file mode 100644 index 0000000000000000000000000000000000000000..91e06acb011e2cb093d2bb2f9959680d6f11ea42 --- /dev/null +++ b/misc/generateLinkTestComPattern.c @@ -0,0 +1,188 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <inttypes.h> +#include <mpi.h> +#include "sion.h" + +#define LINKTEST_COM_ID "LKTST_COM" +#define END_BLOCK "End_Block" + +/* This is an example script how to generate communication matrices so that they can + * be loaded into LinkTest. + * + * To use this script call it using the number of tasks that you wish to use in + * LinkTest. This will the create a communication pattern for that amount of tasks. + * + * You can choose to save the communication pattern as either a rank-by-step matrix + * where the value indicates the communication partner in said step or as a rank-by- + * rank matrix in which the value indicates the step in which the communication + * between the two is tested. You can set this a the beginning of the main function + * by setting "dType" to either "DataTypeCom" or "DataTypeMat". "DataTypePer" is + * currently not supported by LinkTest. + * + * TODO: Implement DataTypePer both here and in LinkTest. + */ + + +enum DataType{ + DataTypeInvalid=0, //Invalid Data Type + DataTypeCom =1, //COM matrix : Rank-by-Step (Value: Partner) + DataTypeMat =2, //Communication Matrix: Rank-by-Partner (Value: Step ) + DataTypePer =3, //Permutation Vector for Permuting 1-Factor Output TODO: CURRENTLY NOT SUPPORTED IN LINKTEST +}; + +struct Item{ + const void* data; + size_t size; + struct Item *next; +}; + +struct ItemList_s{ + size_t num_items; + size_t total_size; + struct Item* first_item; + struct Item* last_item; +}; +typedef struct ItemList_s ItemList; + +int append(ItemList* const itemList,const void* const data,const size_t size){ + if(itemList->num_items==0){ + itemList->first_item=(struct Item*)malloc(sizeof(struct Item)); + if(itemList->first_item==NULL) return -1; + itemList->last_item=itemList->first_item; + }else{ + itemList->last_item->next=(struct Item*)malloc(sizeof(struct Item)); + if(itemList->last_item->next==NULL) return -1; + itemList->last_item=itemList->last_item->next; + } + itemList->last_item->data=data; + itemList->last_item->size=size; + itemList->num_items++; + itemList->total_size+=size; + return(0); +} + +void* createBuffer(const ItemList* const itemList){ + struct Item* item=itemList->first_item; + void* const buf=(void*)malloc(itemList->total_size); + void* ptr=buf; + + for(size_t i=0;i<itemList->num_items;i++){ + ptr=memcpy(ptr,item->data,item->size)+item->size; + item=item->next; + } + + return(buf); +} + +void freeList(const ItemList* const itemList){ + struct Item* item=itemList->first_item; + struct Item* next; + for(size_t i=0;i<itemList->num_items;i++){ + next=item->next; + free(item); + item=next; + } +} + +int main(int argc, char** argv){ + const uint8_t dType =DataTypeMat; + int world_size, world_rank; + + /* Initialize MPI */ + int ret=MPI_Init(NULL, NULL); + if (ret)return(ret); + ret=MPI_Comm_size(MPI_COMM_WORLD,&world_size); + if (ret)return(ret); + MPI_Comm_rank(MPI_COMM_WORLD,&world_rank); + if (ret)return(ret); + if(world_size%2){ + if(world_rank==0){printf("This example requires an even number of tasks!\n");fflush(stdout);} + goto FIN_MPI; + } + + /* Generate Default COM */ + const uint64_t num_ranks=(uint64_t)world_size; + const uint64_t num_steps=num_ranks-1; + uint64_t* com=(uint64_t*)malloc(num_steps*sizeof(uint64_t)); + for(size_t i=0;i<num_steps;i++){ + const uint64_t idle=(i*world_size/2)%num_steps; + if(world_rank==num_steps) com[i]=idle; + else if((uint64_t)world_rank==idle) com[i]=num_steps; + else com[i]=(i-world_rank+num_steps)%num_steps; + } + + uint8_t tmp; + MPI_Status mpi_status; + if(world_rank==0) printf("COM Matrix:\n"); + else MPI_Recv(&tmp,0,MPI_UINT8_T,world_rank-1,0,MPI_COMM_WORLD,&mpi_status); + printf("COM %d: ",world_rank); + for(size_t i=0;i<num_steps;i++) printf("%" PRIu64 ",",com[i]); + printf("\n"); + fflush(stdout); + if(world_rank<world_size-1) MPI_Send(&tmp,0,MPI_UINT8_T,world_rank+1,0,MPI_COMM_WORLD); + MPI_Barrier(MPI_COMM_WORLD); + + uint64_t* mat=(dType==DataTypeMat)?(uint64_t*)calloc((size_t)world_size,sizeof(uint64_t)):NULL; + if(dType==DataTypeMat){ + for(size_t i=0;i<num_steps;i++) mat[com[i]]=i+1; + + if(world_rank==0) printf("Communication Matrix:\n"); + else MPI_Recv(&tmp,0,MPI_UINT8_T,world_rank-1,0,MPI_COMM_WORLD,&mpi_status); + printf("MAT %d: ",world_rank); + for(size_t i=0;i<num_ranks;i++) printf("%" PRIu64 ",",mat[i]); + printf("\n"); + fflush(stdout); + if(world_rank<world_size-1) MPI_Send(&tmp,0,MPI_UINT8_T,world_rank+1,0,MPI_COMM_WORLD); + MPI_Barrier(MPI_COMM_WORLD); + } + + /* Prepare Buffer */ + const char LinkTest_Com_ID[]=LINKTEST_COM_ID; + const uint32_t version[3] ={2, 1, 17}; + const uint64_t num_iter =1; + const char End_Block[] =END_BLOCK; + ItemList itemList={.num_items=0,.total_size=0,.first_item=NULL,.last_item=NULL}; + append(&itemList, LinkTest_Com_ID,(strlen(LinkTest_Com_ID)+1)*sizeof(char )); + append(&itemList, version , 3*sizeof(uint32_t)); + append(&itemList,&num_iter , sizeof(uint64_t)); + append(&itemList,&dType , sizeof(uint8_t )); + if(dType==DataTypeMat){ + append(&itemList,&num_ranks, sizeof(uint64_t)); + append(&itemList, mat ,num_ranks*sizeof(uint64_t)); + }else{ + append(&itemList,&num_steps, sizeof(uint64_t)); + append(&itemList, com ,num_steps*sizeof(uint64_t)); + } + append(&itemList, End_Block ,(strlen(End_Block )+1)*sizeof(char )); + + /* Create Buffer */ + void* const buffer=createBuffer(&itemList); + + /* for SIONlib open call */ + char fname[] ="COM.sion"; + int numfiles =1; + const MPI_Comm gcomm =MPI_COMM_WORLD; + const MPI_Comm lcomm =MPI_COMM_WORLD; + sion_int64 chunksize =itemList.total_size; + sion_int32 fsblksize =-1; + FILE* fileptr =NULL; + char* newfname =NULL; + + // Write Data; + int sid=sion_paropen_mpi(fname,"bw",&numfiles,gcomm,&lcomm,&chunksize,&fsblksize,&world_rank,&fileptr,&newfname); + if(sion_fwrite(buffer,itemList.total_size,1,sid)!=1){ + fprintf(stderr,"ERROR: Rank %d failed to write buffer.\n",world_rank); + } + sion_parclose_mpi(sid); + + //Free Data + free(buffer); + free(com); + if(mat!=NULL) free(mat); + freeList(&itemList); + + /* Finalize MPI Environment. */ + FIN_MPI: MPI_Finalize(); +} diff --git a/misc/permutationTester.c b/misc/permutationTester.c new file mode 100644 index 0000000000000000000000000000000000000000..8307f84eb3c268aa1d1284473e2935f6f968ca2b --- /dev/null +++ b/misc/permutationTester.c @@ -0,0 +1,53 @@ +#include <stdio.h> +#include <stdlib.h> + +void permute(const int n,int* const vec){ + int i,help,pos; + for(i=0;i<n;i++)vec[i]=i; + for(i=0;i<n;i++){ + pos=(int)((double)rand()*(double)(n)/(double)RAND_MAX); + help=vec[i]; + vec[i]=vec[pos]; + vec[pos]=help; + } +} + +void test(const int niter, const int n, int** const vecs){ + int i,j,k,l; + l=0; + for(i=0;i<niter;i++){ + for(j=i+1;j<niter;j++){ + int tst=1; + for(k=0;k<n;k++){ + if(vecs[i][k]!=vecs[j][k]){tst=0;break;} + } + if(tst){ + printf("Permutations %d and %d are equal!\n",i,j); + l++; + } + } + } + printf("%d repeated permutations detected!\n",l); +} + +int main(){ + int i,j; + const int n=100000; //Should not be zero! + const int niter=30000; + int** const vecs=(int**)malloc(niter*sizeof(int*)); + + //Set SEED + srand(1337); + + //Generate Permutations + for(i=0;i<niter;i++){ + vecs[i]=(int*)malloc(n*sizeof(int)); + permute(n,vecs[i]); + } + + // Test Permutations + test(niter,n,vecs); + + for(i=0;i<niter;i++) free(vecs[i]); + free(vecs); +} \ No newline at end of file