/**CFile**************************************************************** FileName [cmdAuto.c] SystemName [ABC: Logic synthesis and verification system.] PackageName [Command processing package.] Synopsis [Autotuner.] Author [Alan Mishchenko, Bruno Schmitt] Affiliation [UC Berkeley] Date [Ver. 1.0. Started - June 20, 2005.] Revision [$Id: cmdAuto.c,v 1.00 2005/06/20 00:00:00 alanmi Exp $] ***********************************************************************/ #include #include #include #include #include "misc/util/abc_global.h" #include "misc/extra/extra.h" #include "aig/gia/gia.h" #include "sat/satoko/satoko.h" #ifdef ABC_USE_PTHREADS #ifdef _WIN32 #include "../lib/pthread.h" #else #include #include #endif #endif ABC_NAMESPACE_IMPL_START //////////////////////////////////////////////////////////////////////// /// DECLARATIONS /// //////////////////////////////////////////////////////////////////////// #define CMD_AUTO_LINE_MAX 1000 // max number of chars in the string #define CMD_AUTO_ARG_MAX 100 // max number of arguments in the call extern int Gia_ManSatokoCallOne( Gia_Man_t * p, satoko_opts_t * opts, int iOutput ); //////////////////////////////////////////////////////////////////////// /// FUNCTION DEFINITIONS /// //////////////////////////////////////////////////////////////////////// /**Function************************************************************* Synopsis [Printing option structure.] Description [] SideEffects [] SeeAlso [] ***********************************************************************/ void Cmd_RunAutoTunerPrintOptions( satoko_opts_t * pOpts ) { printf( "-C %d ", (int)pOpts->conf_limit ); printf( "-V %.3f ", (float)pOpts->var_decay ); printf( "-W %.3f ", (float)pOpts->clause_decay ); if ( pOpts->verbose ) printf( "-v" ); printf( "\n" ); } /**Function************************************************************* Synopsis [The main evaluation procedure for an array of AIGs.] Description [] SideEffects [] SeeAlso [] ***********************************************************************/ int Cmd_RunAutoTunerEvalSimple( Vec_Ptr_t * vAigs, satoko_opts_t * pOpts ) { Gia_Man_t * pGia; int i, TotalCost = 0; //printf( "Tuning with options: " ); //Cmd_RunAutoTunerPrintOptions( pOpts ); Vec_PtrForEachEntry( Gia_Man_t *, vAigs, pGia, i ) TotalCost += Gia_ManSatokoCallOne( pGia, pOpts, -1 ); return TotalCost; } /**Function************************************************************* Synopsis [The main evaluation procedure for the set of AIGs.] Description [] SideEffects [] SeeAlso [] ***********************************************************************/ #ifndef ABC_USE_PTHREADS int Cmd_RunAutoTunerEval( Vec_Ptr_t * vAigs, satoko_opts_t * pOpts, int nCores ) { return Cmd_RunAutoTunerEvalSimple( vAigs, pOpts ); } #else // pthreads are used #define CMD_THR_MAX 100 typedef struct Cmd_AutoData_t_ { Gia_Man_t * pGia; satoko_opts_t * pOpts; int iThread; int nTimeOut; int fWorking; int Result; } Cmd_AutoData_t; void * Cmd_RunAutoTunerEvalWorkerThread( void * pArg ) { Cmd_AutoData_t * pThData = (Cmd_AutoData_t *)pArg; volatile int * pPlace = &pThData->fWorking; while ( 1 ) { while ( *pPlace == 0 ); assert( pThData->fWorking ); if ( pThData->pGia == NULL ) // kills itself when there is nothing to do { pthread_exit( NULL ); assert( 0 ); return NULL; } pThData->Result = Gia_ManSatokoCallOne( pThData->pGia, pThData->pOpts, -1 ); pThData->fWorking = 0; } assert( 0 ); return NULL; } int Cmd_RunAutoTunerEval( Vec_Ptr_t * vAigs, satoko_opts_t * pOpts, int nProcs ) { Cmd_AutoData_t ThData[CMD_THR_MAX]; pthread_t WorkerThread[CMD_THR_MAX]; int i, status, fWorkToDo = 1, TotalCost = 0; Vec_Ptr_t * vStack; if ( nProcs == 1 ) return Cmd_RunAutoTunerEvalSimple( vAigs, pOpts ); // subtract manager thread nProcs--; assert( nProcs >= 1 && nProcs <= CMD_THR_MAX ); // start threads for ( i = 0; i < nProcs; i++ ) { ThData[i].pGia = NULL; ThData[i].pOpts = pOpts; ThData[i].iThread = i; ThData[i].nTimeOut = -1; ThData[i].fWorking = 0; ThData[i].Result = -1; status = pthread_create( WorkerThread + i, NULL,Cmd_RunAutoTunerEvalWorkerThread, (void *)(ThData + i) ); assert( status == 0 ); } // look at the threads vStack = Vec_PtrDup(vAigs); while ( fWorkToDo ) { fWorkToDo = (int)(Vec_PtrSize(vStack) > 0); for ( i = 0; i < nProcs; i++ ) { // check if this thread is working if ( ThData[i].fWorking ) { fWorkToDo = 1; continue; } // check if this thread has recently finished if ( ThData[i].pGia != NULL ) { assert( ThData[i].Result >= 0 ); TotalCost += ThData[i].Result; ThData[i].pGia = NULL; } if ( Vec_PtrSize(vStack) == 0 ) continue; // give this thread a new job assert( ThData[i].pGia == NULL ); ThData[i].pGia = (Gia_Man_t *)Vec_PtrPop( vStack ); ThData[i].fWorking = 1; } } Vec_PtrFree( vStack ); // stop threads for ( i = 0; i < nProcs; i++ ) { assert( !ThData[i].fWorking ); // stop ThData[i].pGia = NULL; ThData[i].fWorking = 1; } return TotalCost; } #endif // pthreads are used /**Function************************************************************* Synopsis [Derives all possible param stucts according to the config file.] Description [] SideEffects [] SeeAlso [] ***********************************************************************/ char * Cmd_DeriveConvertIntoString( int argc, char ** argv ) { char pBuffer[CMD_AUTO_LINE_MAX] = {0}; int i; for ( i = 0; i < argc; i++ ) { strcat( pBuffer, argv[i] ); strcat( pBuffer, " " ); } return Abc_UtilStrsav(pBuffer); } satoko_opts_t * Cmd_DeriveOptionFromSettings( int argc, char ** argv ) { int c; satoko_opts_t opts, * pOpts; satoko_default_opts(&opts); Extra_UtilGetoptReset(); #ifdef SATOKO_ACT_VAR_FIXED while ( ( c = Extra_UtilGetopt( argc, argv, "CPDEFGHIJKLMNOQRSTUhv" ) ) != EOF ) #else while ( ( c = Extra_UtilGetopt( argc, argv, "CPDEFGHIJKLMNOQRShv" ) ) != EOF ) #endif { switch ( c ) { case 'C': if ( globalUtilOptind >= argc ) { Abc_Print( -1, "Command line switch \"-C\" should be followed by an integer.\n" ); return NULL; } opts.conf_limit = atoi(argv[globalUtilOptind]); globalUtilOptind++; if ( opts.conf_limit < 0 ) return NULL; break; case 'P': if ( globalUtilOptind >= argc ) { Abc_Print( -1, "Command line switch \"-P\" should be followed by an integer.\n" ); return NULL; } opts.prop_limit = atoi(argv[globalUtilOptind]); globalUtilOptind++; if ( opts.prop_limit < 0 ) return NULL; break; case 'D': if ( globalUtilOptind >= argc ) { Abc_Print( -1, "Command line switch \"-D\" should be followed by an float.\n" ); return NULL; } opts.f_rst = atof(argv[globalUtilOptind]); globalUtilOptind++; if ( opts.f_rst < 0 ) return NULL; break; case 'E': if ( globalUtilOptind >= argc ) { Abc_Print( -1, "Command line switch \"-E\" should be followed by an float.\n" ); return NULL; } opts.b_rst = atof(argv[globalUtilOptind]); globalUtilOptind++; if ( opts.b_rst < 0 ) return NULL; break; case 'F': if ( globalUtilOptind >= argc ) { Abc_Print( -1, "Command line switch \"-F\" should be followed by an integer.\n" ); return NULL; } opts.fst_block_rst = (unsigned)atoi(argv[globalUtilOptind]); globalUtilOptind++; break; case 'G': if ( globalUtilOptind >= argc ) { Abc_Print( -1, "Command line switch \"-G\" should be followed by an integer.\n" ); return NULL; } opts.sz_lbd_bqueue = (unsigned)atoi(argv[globalUtilOptind]); globalUtilOptind++; break; case 'H': if ( globalUtilOptind >= argc ) { Abc_Print( -1, "Command line switch \"-H\" should be followed by an integer.\n" ); return NULL; } opts.sz_trail_bqueue = (unsigned)atoi(argv[globalUtilOptind]); globalUtilOptind++; break; case 'I': if ( globalUtilOptind >= argc ) { Abc_Print( -1, "Command line switch \"-I\" should be followed by an integer.\n" ); return NULL; } opts.n_conf_fst_reduce = (unsigned)atoi(argv[globalUtilOptind]); globalUtilOptind++; break; case 'J': if ( globalUtilOptind >= argc ) { Abc_Print( -1, "Command line switch \"-J\" should be followed by an integer.\n" ); return NULL; } opts.inc_reduce = (unsigned)atoi(argv[globalUtilOptind]); globalUtilOptind++; break; case 'K': if ( globalUtilOptind >= argc ) { Abc_Print( -1, "Command line switch \"-K\" should be followed by an integer.\n" ); return NULL; } opts.inc_special_reduce = (unsigned)atoi(argv[globalUtilOptind]); globalUtilOptind++; break; case 'L': if ( globalUtilOptind >= argc ) { Abc_Print( -1, "Command line switch \"-L\" should be followed by an integer.\n" ); return NULL; } opts.lbd_freeze_clause = (unsigned)atoi(argv[globalUtilOptind]); globalUtilOptind++; break; case 'M': if ( globalUtilOptind >= argc ) { Abc_Print( -1, "Command line switch \"-M\" should be followed by an integer.\n" ); return NULL; } opts.learnt_ratio = atof(argv[globalUtilOptind]) / 100; globalUtilOptind++; if ( opts.learnt_ratio < 0 ) return NULL; break; case 'N': if ( globalUtilOptind >= argc ) { Abc_Print( -1, "Command line switch \"-M\" should be followed by an integer.\n" ); return NULL; } opts.garbage_max_ratio = atof(argv[globalUtilOptind]) / 100; globalUtilOptind++; if ( opts.garbage_max_ratio < 0 ) return NULL; break; case 'O': if ( globalUtilOptind >= argc ) { Abc_Print( -1, "Command line switch \"-O\" should be followed by an integer.\n" ); return NULL; } opts.clause_max_sz_bin_resol = (unsigned)atoi(argv[globalUtilOptind]); globalUtilOptind++; break; case 'Q': if ( globalUtilOptind >= argc ) { Abc_Print( -1, "Command line switch \"-O\" should be followed by an integer.\n" ); return NULL; } opts.clause_min_lbd_bin_resol = (unsigned)atoi(argv[globalUtilOptind]); globalUtilOptind++; break; case 'R': if ( globalUtilOptind >= argc ) { Abc_Print( -1, "Command line switch \"-R\" should be followed by an float.\n" ); return NULL; } opts.clause_decay = atof(argv[globalUtilOptind]); globalUtilOptind++; if ( opts.clause_decay < 0 ) return NULL; break; case 'S': if ( globalUtilOptind >= argc ) { Abc_Print( -1, "Command line switch \"-S\" should be followed by an float.\n" ); return NULL; } opts.var_decay = atof(argv[globalUtilOptind]); globalUtilOptind++; if ( opts.var_decay < 0 ) return NULL; break; #ifdef SATOKO_ACT_VAR_FIXED case 'T': if ( globalUtilOptind >= argc ) { Abc_Print( -1, "Command line switch \"-T\" should be followed by an float.\n" ); return NULL; } opts.var_act_limit = (unsigned)strtol(argv[globalUtilOptind], NULL, 16); globalUtilOptind++; break; case 'U': if ( globalUtilOptind >= argc ) { Abc_Print( -1, "Command line switch \"-U\" should be followed by an float.\n" ); return NULL; } opts.var_act_rescale = (unsigned)strtol(argv[globalUtilOptind], NULL, 16); globalUtilOptind++; break; #endif case 'h': return NULL; case 'v': opts.verbose ^= 1; break; default: return NULL; } } // return a copy of this parameter structure pOpts = ABC_ALLOC( satoko_opts_t, 1 ); memcpy( pOpts, &opts, sizeof(satoko_opts_t) ); return pOpts; } void Cmf_CreateOptions_rec( Vec_Wec_t * vPars, int iPar, char Argv[CMD_AUTO_ARG_MAX][20], int Argc, Vec_Ptr_t * vOpts ) { Vec_Int_t * vLine; int Symb, Num, i; assert( Argc <= CMD_AUTO_ARG_MAX ); if ( Vec_WecSize(vPars) == iPar ) { satoko_opts_t * pOpts; char * pArgv[CMD_AUTO_ARG_MAX]; for ( i = 0; i < Argc; i++ ) pArgv[i] = Argv[i]; pOpts = Cmd_DeriveOptionFromSettings( Argc, pArgv ); if ( pOpts == NULL ) printf( "Cannot parse command line options...\n" ); else { Vec_PtrPush( vOpts, pOpts ); Vec_PtrPush( vOpts, Cmd_DeriveConvertIntoString(Argc, pArgv) ); printf( "Adding settings %s\n", (char *)Vec_PtrEntryLast(vOpts) ); } return; } vLine = Vec_WecEntry( vPars, iPar ); // consider binary option if ( Vec_IntSize(vLine) == 2 ) { Symb = Vec_IntEntry( vLine, 0 ); Num = Vec_IntEntry( vLine, 1 ); assert( Abc_Int2Float(Num) == -1.0 ); // create one setting without this option Cmf_CreateOptions_rec( vPars, iPar+1, Argv, Argc, vOpts ); // create another setting with this option sprintf( Argv[Argc], "-%c", Symb ); Cmf_CreateOptions_rec( vPars, iPar+1, Argv, Argc+1, vOpts ); return; } // consider numeric option Vec_IntForEachEntryDouble( vLine, Symb, Num, i ) { float NumF = Abc_Int2Float(Num); // create setting with this option assert( NumF >= 0 ); sprintf( Argv[Argc], "-%c", Symb ); if ( NumF == (float)(int)NumF ) sprintf( Argv[Argc+1], "%d", (int)NumF ); else sprintf( Argv[Argc+1], "%.3f", NumF ); Cmf_CreateOptions_rec( vPars, iPar+1, Argv, Argc+2, vOpts ); } } Vec_Ptr_t * Cmf_CreateOptions( Vec_Wec_t * vPars ) { char Argv[CMD_AUTO_ARG_MAX][20]; int Symb, Num, i, Argc = 0; Vec_Ptr_t * vOpts = Vec_PtrAlloc( 100 ); Vec_Int_t * vLine = Vec_WecEntry( vPars, 0 ); printf( "Creating all possible settings to be used by the autotuner:\n" ); sprintf( Argv[Argc++], "autotuner" ); Vec_IntForEachEntryDouble( vLine, Symb, Num, i ) { float NumF = Abc_Int2Float(Num); sprintf( Argv[Argc++], "-%c", Symb ); if ( NumF < 0.0 ) continue; if ( NumF == (float)(int)NumF ) sprintf( Argv[Argc++], "%d", (int)NumF ); else sprintf( Argv[Argc++], "%.3f", NumF ); } Cmf_CreateOptions_rec( vPars, 1, Argv, Argc, vOpts ); printf( "Finished creating %d settings.\n\n", Vec_PtrSize(vOpts)/2 ); return vOpts; } /**Function************************************************************* Synopsis [Parses config file and derives AIGs listed in file list.] Description [] SideEffects [] SeeAlso [] ***********************************************************************/ static inline int Cmf_IsSpace( char p ) { return p == ' ' || p == '\t' || p == '\n' || p == '\r'; } static inline int Cmf_IsLowerCaseChar( char p ) { return p >= 'a' && p <= 'z'; } static inline int Cmf_IsUpperCaseChar( char p ) { return p >= 'A' && p <= 'Z'; } static inline int Cmf_IsDigit( char p ) { return (p >= '0' && p <= '9') || p == '.'; } Vec_Wec_t * Cmd_ReadParamChoices( char * pConfig ) { Vec_Wec_t * vPars; Vec_Int_t * vLine; char * pThis, pBuffer[CMD_AUTO_LINE_MAX]; FILE * pFile = fopen( pConfig, "rb" ); if ( pFile == NULL ) { printf( "File containing list of files \"%s\" cannot be opened.\n", pConfig ); return NULL; } vPars = Vec_WecAlloc( 100 ); while ( fgets( pBuffer, CMD_AUTO_LINE_MAX, pFile ) != NULL ) { // get the command from the file if ( Cmf_IsSpace(pBuffer[0]) || pBuffer[0] == '#') continue; // skip trailing spaces while ( Cmf_IsSpace(pBuffer[strlen(pBuffer)-1]) ) pBuffer[strlen(pBuffer)-1] = 0; // read the line vLine = Vec_WecPushLevel( vPars ); for ( pThis = pBuffer; *pThis; ) { if ( Cmf_IsLowerCaseChar(*pThis) ) { Vec_IntPushTwo( vLine, (int)*pThis, Abc_Float2Int((float)-1.0) ); pThis++; while ( Cmf_IsSpace(*pThis) ) pThis++; continue; } if ( Cmf_IsUpperCaseChar(*pThis) ) { char Param = *pThis++; if ( !Cmf_IsDigit(*pThis) ) { printf( "Upper-case character (%c) should be followed by a number without space in line \"%s\".\n", Param, pBuffer ); return NULL; } Vec_IntPushTwo( vLine, (int)Param, Abc_Float2Int(atof(pThis)) ); while ( Cmf_IsDigit(*pThis) ) pThis++; while ( Cmf_IsSpace(*pThis) ) pThis++; continue; } printf( "Expecting a leading lower-case or upper-case digit in line \"%s\".\n", pBuffer ); return NULL; } } fclose( pFile ); return vPars; } Vec_Ptr_t * Cmd_ReadFiles( char * pFileList ) { Gia_Man_t * pGia; Vec_Ptr_t * vAigs; char pBuffer[CMD_AUTO_LINE_MAX]; FILE * pFile = fopen( pFileList, "rb" ); if ( pFile == NULL ) { printf( "File containing list of files \"%s\" cannot be opened.\n", pFileList ); return NULL; } vAigs = Vec_PtrAlloc( 100 ); while ( fgets( pBuffer, CMD_AUTO_LINE_MAX, pFile ) != NULL ) { // get the command from the file if ( Cmf_IsSpace(pBuffer[0]) || pBuffer[0] == '#') continue; // skip trailing spaces while ( Cmf_IsSpace(pBuffer[strlen(pBuffer)-1]) ) pBuffer[strlen(pBuffer)-1] = 0; // read the file pGia = Gia_AigerRead( pBuffer, 0, 0, 0 ); if ( pGia == NULL ) { printf( "Cannot read AIG from file \"%s\".\n", pBuffer ); continue; } Vec_PtrPush( vAigs, pGia ); } fclose( pFile ); return vAigs; } /**Function************************************************************* Synopsis [Autotuner for SAT solver "satoko".] Description [] SideEffects [] SeeAlso [] ***********************************************************************/ void Cmd_RunAutoTuner( char * pConfig, char * pFileList, int nCores ) { abctime clk = Abc_Clock(); Vec_Wec_t * vPars = Cmd_ReadParamChoices( pConfig ); Vec_Ptr_t * vAigs = Cmd_ReadFiles( pFileList ); Vec_Ptr_t * vOpts = vPars ? Cmf_CreateOptions( vPars ) : NULL; int i; char * pString, * pStringBest = NULL; satoko_opts_t * pOpts, * pOptsBest = NULL; int Result, ResultBest = 0x7FFFFFFF; Gia_Man_t * pGia; // iterate through all possible option settings if ( vAigs && vOpts ) { Vec_PtrForEachEntryDouble( satoko_opts_t *, char *, vOpts, pOpts, pString, i ) { abctime clk = Abc_Clock(); printf( "Evaluating settings: %20s... \n", pString ); Result = Cmd_RunAutoTunerEval( vAigs, pOpts, nCores ); printf( "Cost = %6d. ", Result ); Abc_PrintTime( 1, "Time", Abc_Clock() - clk ); if ( ResultBest > Result ) { ResultBest = Result; pStringBest = pString; pOptsBest = pOpts; } } printf( "The best settings are: %20s \n", pStringBest ); printf( "Best cost = %6d. ", ResultBest ); Abc_PrintTime( 1, "Total time", Abc_Clock() - clk ); } // cleanup if ( vPars ) Vec_WecFree( vPars ); if ( vOpts ) Vec_PtrFreeFree( vOpts ); if ( vAigs ) { Vec_PtrForEachEntry( Gia_Man_t *, vAigs, pGia, i ) Gia_ManStop( pGia ); Vec_PtrFree( vAigs ); } } //////////////////////////////////////////////////////////////////////// /// END OF FILE /// //////////////////////////////////////////////////////////////////////// ABC_NAMESPACE_IMPL_END