/////////////////////////////////////////////////////////////////////////////////////// // File : convol.c // Date : june 2014 // author : Alain Greiner /////////////////////////////////////////////////////////////////////////////////////// // This multi-threaded application implements a 2D convolution product. // It can run on a multi-cores, multi-clusters architecture, with one thread // per core, and uses the POSIX threads API. // // The input image is read from a file and the output image is saved to another file. // // - number of clusters containing processors must be power of 2 no larger than 256. // - number of processors per cluster must be power of 2 no larger than 4. // - number of working threads is the number of cores availables in the hardware // architecture : nthreads = nclusters * ncores. // // The convolution kernel is defined in the execute() function. // It can be factored in two independant line and column convolution products. // // The main() function can be launched on any processor. // - It checks software requirements versus the hardware resources. // - It open & maps the input file to a global buffer. // - it open & maps the output file to another global buffer. // - it open the instrumentation file. // - it creates & activates two FBF windows to display input & output images. // - it launches other threads to run in parallel the execute() function. // - it saves the instrumentation results on disk. // - it closes the input, output, & instrumentation files. // - it deletes the FBF input & output windows. // // The execute() function is executed in parallel by all threads. These threads are // working on 5 arrays of distributed buffers, indexed by the cluster index [cid]. // - A[cid]: contain the distributed initial image (NL/NCLUSTERS lines per cluster). // - B[cid]: is the result of horizontal filter, then transpose B <= Trsp(HF(A) // - C[cid]: is the result of vertical image, then transpose : c <= Trsp(VF(B) // - D[cid]: is the the difference between A and FH(A) : D <= A - FH(A) // - Z[cid]: contain the distributed final image Z <= C + D // // It can be split in four phases separated by synchronisation barriers: // 1. Initialisation: // Allocates the 5 A[cid],B[cid],C[cid],D[cid],Z[cid] buffers, initialise A[cid] // from the buffer, and display the initial image on FBF if rquired. // 2. Horizontal Filter: // Set B[cid] and D[cid] from A[cid]. Read data accesses are local, write data // accesses are remote, to implement the transpose. // 3. Vertical Filter: // Set C[cid] from B[cid]. Read data accesses are local, write data accesses // are remote, to implement the transpose. // 4. Save results: // Set the Z[cid] from C[cid] and D[cid]. All read and write access are local. // Move the final image (Z[cid] buffer) to the buffer. // // This application supports three placement modes, implemented in the main() function. // In all modes, the working threads are identified by the [tid] continuous index // in range [0, NTHREADS-1], and defines how the lines are shared amongst the threads. // This continuous index can always be decomposed in two continuous sub-indexes: // tid == cid * NCORES + lid, where cid is in [0,NCLUSTERS-1] and lid in [0,NCORES-1]. // // - NO_PLACEMENT: the main thread is itsef a working thread. The (N_1) other working // threads are created by the main thread, but the placement is done by the OS, using // the DQDT for load balancing, and two working threads can be placed on the same core. // The [cid,lid] are only abstract identifiers, and cannot be associated to a physical // cluster or a physical core. In this mode, the main thread run on any cluster, // but has tid = 0 (i.e. cid = 0 & tid = 0). // // - EXPLICIT_PLACEMENT: the main thread is again a working thread, but the placement // of the threads on the cores is explicitely controled by the main thread to have // exactly one working thread per core, and the [cxy][lpid] core coordinates for a given // thread[tid] can be directly derived from the [tid] value: [cid] is an alias for the // physical cluster identifier, and [lid] is the local core index. // // - PARALLEL_PLACEMENT: the main thread is not anymore a working thread, and uses the // non standard pthread_parallel_create() function to avoid the costly sequencial // loops for pthread_create() and pthread_join(). It garanties one working thread // per core, and the same relation between the thread[tid] and the core[cxy][lpid]. // // The [tid] continuous index defines how the work is shared amongst the threads: // - each thread handles NL/nthreads lines for the horizontal filter. // - each thread handles NP/nthreads columns for the vertical filter. /////////////////////////////////////////////////////////////////////////////////////// #include #include #include #include #include #include #include #include #include #define VERBOSE_MAIN 1 #define VERBOSE_EXEC 1 #define SUPER_VERBOSE 0 #define X_MAX 16 #define Y_MAX 16 #define CORES_MAX 4 #define CLUSTERS_MAX (X_MAX * Y_MAX) #define THREADS_MAX (X_MAX * Y_MAX * CORES_MAX) #define IMAGE_TYPE 420 // pixel encoding type #define INPUT_IMAGE_PATH "misc/couple_512.raw" // default image_in #define OUTPUT_IMAGE_PATH "misc/couple_conv_512.raw" // default image_out #define NL 512 // default nlines #define NP 512 // default npixels #define NO_PLACEMENT 0 #define EXPLICIT_PLACEMENT 0 #define PARALLEL_PLACEMENT 1 #define INTERACTIVE_MODE 0 #define USE_DQT_BARRIER 1 #define INITIAL_DISPLAY_ENABLE 1 #define FINAL_DISPLAY_ENABLE 1 #define TA(c,l,p) (A[c][((NP) * (l)) + (p)]) #define TB(c,p,l) (B[c][((NL) * (p)) + (l)]) #define TC(c,l,p) (C[c][((NP) * (l)) + (p)]) #define TD(c,l,p) (D[c][((NP) * (l)) + (p)]) #define TZ(c,l,p) (Z[c][((NP) * (l)) + (p)]) #define max(x,y) ((x) > (y) ? (x) : (y)) #define min(x,y) ((x) < (y) ? (x) : (y)) ////////////////////////////////////////////////////////// // global variables ////////////////////////////////////////////////////////// // global instrumentation counters for the main thread unsigned int SEQUENCIAL_TIME = 0; unsigned int PARALLEL_TIME = 0; // instrumentation counters for thread[tid] in cluster[cid] unsigned int START[CLUSTERS_MAX][CORES_MAX] = {{ 0 }}; unsigned int H_BEG[CLUSTERS_MAX][CORES_MAX] = {{ 0 }}; unsigned int H_END[CLUSTERS_MAX][CORES_MAX] = {{ 0 }}; unsigned int V_BEG[CLUSTERS_MAX][CORES_MAX] = {{ 0 }}; unsigned int V_END[CLUSTERS_MAX][CORES_MAX] = {{ 0 }}; unsigned int F_BEG[CLUSTERS_MAX][CORES_MAX] = {{ 0 }}; unsigned int F_END[CLUSTERS_MAX][CORES_MAX] = {{ 0 }}; // pointer on buffer containing the input image, maped by the main to the input file unsigned char * image_in; // pointer on buffer containing the output image, maped by the main to the output file unsigned char * image_out; // return values at thread exit unsigned int THREAD_EXIT_SUCCESS = 0; unsigned int THREAD_EXIT_FAILURE = 1; // pointer and identifier for FBF windows void * in_win_buf; int in_wid; void * out_win_buf; int out_wid; // synchronization barrier pthread_barrier_t barrier; // platform parameters unsigned int x_size; // number of clusters in a row unsigned int y_size; // number of clusters in a column unsigned int ncores; // number of processors per cluster // main thread continuous index unsigned int tid_main; // arrays of pointers on distributed buffers in all clusters unsigned char * GA[CLUSTERS_MAX]; int * GB[CLUSTERS_MAX]; int * GC[CLUSTERS_MAX]; int * GD[CLUSTERS_MAX]; unsigned char * GZ[CLUSTERS_MAX]; // array of threads kernel identifiers / indexed by [tid] pthread_t exec_trdid[THREADS_MAX]; // array of threads attributes / indexed bi [tid] pthread_attr_t exec_attr[THREADS_MAX]; // array of execute() function arguments / indexed by [tid] pthread_parallel_work_args_t exec_args[THREADS_MAX]; // image features unsigned int image_nl; unsigned int image_np; char input_image_path[128]; char output_image_path[128]; ///////////////////////////////////////////////////////////////////////////////////// // functions declaration ///////////////////////////////////////////////////////////////////////////////////// void * execute( void * args ); void instrument( FILE * f , char * filename ); ///////////////// void main( void ) ///////////////// { unsigned long long start_cycle; unsigned long long end_sequencial_cycle; unsigned long long end_parallel_cycle; int error; char instru_name[32]; // instrumentation file name char instru_path[64]; // instrumentation path name ///////////////////////////////////////////////////////////////////////////////// get_cycle( &start_cycle ); ///////////////////////////////////////////////////////////////////////////////// if( (NO_PLACEMENT + EXPLICIT_PLACEMENT + PARALLEL_PLACEMENT) != 1 ) { printf("\n[convol error] illegal placement\n"); exit( 0 ); } // get & check platform parameters hard_config_t config; get_config( &config ); x_size = config.x_size; y_size = config.y_size; ncores = config.ncores; if((ncores != 1) && (ncores != 2) && (ncores != 4)) { printf("\n[convol error] number of cores per cluster must be 1/2/4\n"); exit( 0 ); } if( (x_size != 1) && (x_size != 2) && (x_size != 4) && (x_size != 8) && (x_size != 16) ) { printf("\n[convol error] x_size must be 1/2/4/8/16\n"); exit( 0 ); } if( (y_size != 1) && (y_size != 2) && (y_size != 4) && (y_size != 8) && (y_size != 16) ) { printf("\n[convol error] y_size must be 1/2/4/8/16\n"); exit( 0 ); } // main thread get identifiers for core executing main unsigned int cxy_main; unsigned int lid_main; get_core_id( &cxy_main , &lid_main ); // compute nthreads and nclusters unsigned int nclusters = x_size * y_size; unsigned int nthreads = nclusters * ncores; // get input and output images pathnames and size if( INTERACTIVE_MODE ) { // get image size printf("\n[convol] image nlines : "); get_uint32( &image_nl ); printf("\n[convol] image npixels : "); get_uint32( &image_np ); printf("\n[convol] input image path : "); get_string( input_image_path , 128 ); printf("[convol] output image path : "); get_string( output_image_path , 128 ); } else { image_nl = NL; image_np = NP; strcpy( input_image_path , INPUT_IMAGE_PATH ); strcpy( output_image_path , OUTPUT_IMAGE_PATH ); } // main thread get FBF size and type int fbf_width; int fbf_height; int fbf_type; fbf_get_config( &fbf_width , &fbf_height , &fbf_type ); if( ((unsigned int)fbf_width < image_np) || ((unsigned int)fbf_height < image_nl) || (fbf_type != IMAGE_TYPE) ) { printf("\n[convol error] image not acceptable\n" "FBF width = %d / npixels = %d\n" "FBF height = %d / nlines = %d\n" "FBF type = %d / expected = %d\n", fbf_width, image_np, fbf_height, image_nl, fbf_type, IMAGE_TYPE ); exit( 0 ); } if( nthreads > image_nl ) { printf("\n[convol error] nthreads (%d] larger than nlines (%d)\n", nthreads , image_nl ); exit( 0 ); } // define instrumentation file name if( NO_PLACEMENT ) { printf("\n[convol] %d cluster(s) / %d core(s) / FBF[%d*%d] / PID %x / NO_PLACE\n", nclusters, ncores, fbf_width, fbf_height, getpid() ); // build instrumentation file name if( USE_DQT_BARRIER ) snprintf( instru_name , 32 , "dqt_no_place_%d_%d", x_size * y_size , ncores ); else snprintf( instru_name , 32 , "smp_no_place_%d_%d", x_size * y_size , ncores ); } if( EXPLICIT_PLACEMENT ) { printf("\n[convol] %d cluster(s) / %d core(s) / FBF[%d*%d] / PID %x / EXPLICIT\n", nclusters, ncores, fbf_width, fbf_height, getpid() ); // build instrumentation file name if( USE_DQT_BARRIER ) snprintf( instru_name , 32 , "dqt_explicit_%d_%d", x_size * y_size , ncores ); else snprintf( instru_name , 32 , "smp_explicit_%d_%d", x_size * y_size , ncores ); } if( PARALLEL_PLACEMENT ) { printf("\n[convol] %d cluster(s) / %d core(s) / FBF[%d*%d] / PID %x / PARALLEL\n", nclusters, ncores, fbf_width, fbf_height, getpid() ); // build instrumentation file name if( USE_DQT_BARRIER ) snprintf( instru_name , 32 , "dqt_parallel_%d_%d", x_size * y_size , ncores ); else snprintf( instru_name , 32 , "smp_parallel_%d_%d", x_size * y_size , ncores ); } // open instrumentation file snprintf( instru_path , 64 , "/home/convol/%s", instru_name ); FILE * f_instru = fopen( instru_path , NULL ); if ( f_instru == NULL ) { printf("\n[convol error] cannot open instrumentation file %s\n", instru_path ); exit( 0 ); } #if VERBOSE_MAIN printf("\n[convol] main on core[%x,%d] open instrumentation file %s\n", cxy_main, lid_main, instru_path ); #endif // main create an FBF window for input image in_wid = fbf_create_window( 0, // l_zero 0, // p_zero image_nl, // lines image_np, // pixels &in_win_buf ); if( in_wid < 0 ) { printf("\n[transpose error] cannot open FBF window for %s\n", input_image_path); exit( 0 ); } // activate window error = fbf_active_window( in_wid , 1 ); if( error ) { printf("\n[transpose error] cannot activate window for %s\n", input_image_path ); exit( 0 ); } #if VERBOSE_MAIN printf("\n[convol] main on core[%x,%d] created FBF window (wid %d) for <%s>\n", cxy_main, lid_main, in_wid, input_image_path ); #endif // main create an FBF window for output image out_wid = fbf_create_window( 0, // l_zero image_np, // p_zero image_nl, // lines image_np, // pixels &out_win_buf ); if( out_wid < 0 ) { printf("\n[transpose error] cannot create FBF window for %s\n", output_image_path); exit( 0 ); } // activate window error = fbf_active_window( out_wid , 1 ); if( error ) { printf("\n[transpose error] cannot activate window for %s\n", output_image_path ); exit( 0 ); } #if VERBOSE_MAIN printf("\n[convol] main on core[%x,%d] created FBF window (wid %d) for <%s>\n", cxy_main, lid_main, out_wid, output_image_path ); #endif // main initialise barrier if( USE_DQT_BARRIER ) { pthread_barrierattr_t attr; attr.x_size = x_size; attr.y_size = y_size; attr.nthreads = ncores; error = pthread_barrier_init( &barrier, &attr , nthreads ); } else { error = pthread_barrier_init( &barrier, NULL , nthreads ); } if( error ) { printf("\n[convol error] cannot initialize barrier\n"); exit( 0 ); } #if VERBOSE_MAIN printf("\n[convol] main on core[%x,%d] completed barrier init\n", cxy_main, lid_main ); #endif // main open input file int fd_in = open( input_image_path , O_RDONLY , 0 ); if ( fd_in < 0 ) { printf("\n[convol error] cannot open input file <%s>\n", input_image_path ); exit( 0 ); } // main thread map input file to image_in buffer image_in = (unsigned char *)mmap( NULL, image_np * image_nl, PROT_READ, MAP_FILE | MAP_SHARED, fd_in, 0 ); // offset if ( image_in == NULL ) { printf("\n[convol error] main cannot map buffer to file %s\n", input_image_path ); exit( 0 ); } #if VERBOSE_MAIN printf("\n[convol] main on core[%x,%x] map buffer to file <%s>\n", cxy_main, lid_main, input_image_path ); #endif // main thread open output file int fd_out = open( output_image_path , O_CREAT , 0 ); if ( fd_out < 0 ) { printf("\n[convol error] main cannot open file %s\n", output_image_path ); exit( 0 ); } // main thread map image_out buffer to output file image_out = (unsigned char *)mmap( NULL, image_np * image_nl, PROT_WRITE, MAP_FILE | MAP_SHARED, fd_out, 0 ); // offset if ( image_out == NULL ) { printf("\n[convol error] main cannot map buffer to file %s\n", output_image_path ); exit( 0 ); } #if VERBOSE_MAIN printf("\n[convol] main on core[%x,%x] map buffer to file <%s>\n", cxy_main, lid_main, output_image_path ); #endif ///////////////////////////////////////////////////////////////////////////////////// get_cycle( &end_sequencial_cycle ); SEQUENCIAL_TIME = (unsigned int)(end_sequencial_cycle - start_cycle); ///////////////////////////////////////////////////////////////////////////////////// ////////////////// #if NO_PLACEMENT { // the tid value for the main thread is always 0 // main thread creates other threads with tid in [1,nthreads-1] unsigned int tid; for ( tid = 0 ; tid < nthreads ; tid++ ) { // register tid value in exec_args[tid] array exec_args[tid].tid = tid; // create other threads if( tid > 0 ) { if ( pthread_create( &exec_trdid[tid], NULL, // no attribute &execute, &exec_args[tid] ) ) { printf("\n[convol error] cannot create thread %d\n", tid ); exit( 0 ); } #if VERBOSE_MAIN printf("\n[convol] main created thread %d\n", tid ); #endif } else { tid_main = 0; } } // end for tid // main thread calls itself the execute() function execute( &exec_args[0] ); // main thread wait other threads completion for ( tid = 1 ; tid < nthreads ; tid++ ) { unsigned int * status; // main wait thread[tid] status if ( pthread_join( exec_trdid[tid], (void*)(&status)) ) { printf("\n[convol error] main cannot join thread %d\n", tid ); exit( 0 ); } // check status if( *status != THREAD_EXIT_SUCCESS ) { printf("\n[convol error] thread %x returned failure\n", tid ); exit( 0 ); } #if VERBOSE_MAIN printf("\n[convol] main successfully joined thread %x\n", tid ); #endif } // end for tid } #endif // end no_placement ////////////////////// #if EXPLICIT_PLACEMENT { // main thread places each other threads on a specific core[cxy][lid] // but the actual thread creation is sequencial unsigned int x; unsigned int y; unsigned int l; unsigned int cxy; // cluster identifier unsigned int tid; // thread continuous index for( x = 0 ; x < x_size ; x++ ) { for( y = 0 ; y < y_size ; y++ ) { cxy = HAL_CXY_FROM_XY( x , y ); for( l = 0 ; l < ncores ; l++ ) { // compute thread continuous index tid = (((x * y_size) + y) * ncores) + l; // register tid value in exec_args[tid] array exec_args[tid].tid = tid; // no thread created on the core running the main if( (cxy != cxy_main) || (l != lid_main) ) { // define thread attributes exec_attr[tid].attributes = PT_ATTR_CLUSTER_DEFINED | PT_ATTR_CORE_DEFINED; exec_attr[tid].cxy = cxy; exec_attr[tid].lid = l; // create thread[tid] on core[cxy][l] if ( pthread_create( &exec_trdid[tid], &exec_attr[tid], &execute, &exec_args[tid] ) ) { printf("\n[convol error] cannot create thread %d\n", tid ); exit( 0 ); } #if VERBOSE_MAIN printf("\n[convol] main created thread[%d] on core[%x,%d]\n", tid, cxy, l ); #endif } else { tid_main = tid; } } } } // main thread calls itself the execute() function execute( &exec_args[tid_main] ); // main thread wait other threads completion for( tid = 0 ; tid < nthreads ; tid++ ) { // no other thread on the core running the main if( tid != tid_main ) { unsigned int * status; // wait thread[tid] if( pthread_join( exec_trdid[tid] , (void*)(&status) ) ) { printf("\n[convol error] main cannot join thread %d\n", tid ); exit( 0 ); } // check status if( *status != THREAD_EXIT_SUCCESS ) { printf("\n[convol error] thread %d returned failure\n", tid ); exit( 0 ); } #if VERBOSE_MAIN printf("\n[convol] main joined thread %d on core[%x,%d]\n", tid , cxy , l ); #endif } } } #endif // end explicit_placement ////////////////////// #if PARALLEL_PLACEMENT { // compute covering DQT size an level unsigned int z = (x_size > y_size) ? x_size : y_size; unsigned int root_level = ((z == 1) ? 0 : ((z == 2) ? 1 : ((z == 4) ? 2 : ((z == 8) ? 3 : 4)))); // create & execute the working threads if( pthread_parallel_create( root_level , &execute ) ) { printf("\n[convol error] in %s\n", __FUNCTION__ ); exit( 0 ); } } #endif // end parallel_placement ///////////////////////////////////////////////////////////////////////////// get_cycle( &end_parallel_cycle ); PARALLEL_TIME = (unsigned int)(end_parallel_cycle - end_sequencial_cycle); ///////////////////////////////////////////////////////////////////////////// // main thread register instrumentation results instrument( f_instru , instru_name ); #if VERBOSE_MAIN printf("\n[convol] main registered instrumentation info\n" ); #endif // main thread close input file close( fd_in ); #if VERBOSE_MAIN printf("\n[convol] main closed input file\n" ); #endif // main thread close output file close( fd_out ); #if VERBOSE_MAIN printf("\n[convol] main closed output file\n" ); #endif // main thread close instrumentation file fclose( f_instru ); #if VERBOSE_MAIN printf("\n[convol] main closed instrumentation file\n" ); #endif // ask confirm for exit if( INTERACTIVE_MODE ) { char byte; printf("\n[convol] press any key to to delete FBF windows and exit\n"); getc( &byte ); } // main thread delete FBF windows fbf_delete_window( in_wid ); fbf_delete_window( out_wid ); #if VERBOSE_MAIN printf("\n[convol] main deleted FBF windows\n" ); #endif // main thread suicide exit( 0 ); } // end main() ////////////////////////////////// void * execute( void * arguments ) ////////////////////////////////// { unsigned long long date; pthread_parallel_work_args_t * args = (pthread_parallel_work_args_t *)arguments; // Each thread initialises the convolution kernel parameters in local stack. // The values defined in the next 12 lines are Philips proprietary information. int vnorm = 115; int vf[35] = { 1, 1, 2, 2, 2, 2, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 4, 4, 4, 4, 3, 3, 3, 2, 2, 2, 2, 1, 1 }; unsigned int hrange = 100; unsigned int hnorm = 201; // WARNING //A thread is identified by the tid index, defined in the "args" structure. // This index being in range [0,nclusters*ncores-1] we can always write // tid == cid * ncores + lid // with cid in [0,nclusters-1] and lid in [0,ncores-1]. // if NO_PLACEMENT, there is no relation between these // thread [cid][lid] indexes, and the core coordinates [cxy][lpid] // get thread abstract identifiers[cid,lid] from tid unsigned int tid = args->tid; unsigned int cid = tid / ncores; unsigned int lid = tid % ncores; #if VERBOSE_EXEC unsigned int cxy; // core cluster identifier unsigned int lpid; // core local identifier get_cycle( &date ); get_core_id( &cxy , &lpid ); printf("\n[convol] exec[%d] on core[%x,%d] enters parallel exec / cycle %d\n", tid , cxy , lpid , (unsigned int)date ); #endif // compute nthreads and nclusters from global variables unsigned int nclusters = x_size * y_size; unsigned int nthreads = nclusters * ncores; // indexes for loops unsigned int c; // cluster index unsigned int l; // line index unsigned int p; // pixel index unsigned int z; // vertical filter index unsigned int lines_per_thread = image_nl / nthreads; unsigned int lines_per_cluster = image_nl / nclusters; unsigned int pixels_per_thread = image_np / nthreads; unsigned int pixels_per_cluster = image_np / nclusters; // compute number of pixels stored in one cluster unsigned int local_pixels = image_nl * image_np / nclusters; get_cycle( &date ); START[cid][lid] = (unsigned int)date; // Each thread[cid][0] allocates 5 buffers local cluster cid // and registers these 5 pointers in the global arrays if ( lid == 0 ) { GA[cid] = malloc( local_pixels * sizeof( unsigned char ) ); GB[cid] = malloc( local_pixels * sizeof( int ) ); GC[cid] = malloc( local_pixels * sizeof( int ) ); GD[cid] = malloc( local_pixels * sizeof( int ) ); GZ[cid] = malloc( local_pixels * sizeof( unsigned char ) ); if( (GA[cid] == NULL) || (GB[cid] == NULL) || (GC[cid] == NULL) || (GD[cid] == NULL) || (GZ[cid] == NULL) ) { printf("\n[convol error] thread[%d] cannot allocate buf_in\n", tid ); pthread_exit( &THREAD_EXIT_FAILURE ); } #if VERBOSE_EXEC get_cycle( &date ); printf("\n[convol] exec[%d] on core[%x,%d] allocated shared buffers / cycle %d\n" " GA %x / GB %x / GC %x / GD %x / GZ %x\n", tid, cxy , lpid, (unsigned int)date, GA[cid], GB[cid], GC[cid], GD[cid], GZ[cid] ); #endif } //////////////////////////////// pthread_barrier_wait( &barrier ); // Each thread[tid] allocates and initialises in its private stack // a copy of the arrays of pointers on the distributed buffers. unsigned char * A[CLUSTERS_MAX]; int * B[CLUSTERS_MAX]; int * C[CLUSTERS_MAX]; int * D[CLUSTERS_MAX]; unsigned char * Z[CLUSTERS_MAX]; for( c = 0 ; c < nclusters ; c++ ) { A[c] = GA[c]; B[c] = GB[c]; C[c] = GC[c]; D[c] = GD[c]; Z[c] = GZ[c]; } unsigned int npixels = image_np * lines_per_thread; // pixels moved by any thread unsigned int g_offset = npixels * tid; // offset in global buffer for tid unsigned int l_offset = npixels * lid; // offset in local buffer for tid // min and max line indexes handled by thread[tid] for a global buffer unsigned int global_lmin = tid * lines_per_thread; unsigned int global_lmax = global_lmin + lines_per_thread; // min and max line indexes handled by thread[tid] for a local buffer unsigned int local_lmin = lid * lines_per_thread; unsigned int local_lmax = local_lmin + lines_per_thread; // pmin and pmax pixel indexes handled by thread[tid] in a column unsigned int column_pmin = tid * pixels_per_thread; unsigned int column_pmax = column_pmin + pixels_per_thread; // Each thread[tid] copy npixels from image_in buffer to local A[cid] buffer memcpy( A[cid] + l_offset, image_in + g_offset, npixels ); #if VERBOSE_EXEC get_cycle( &date ); printf( "\n[convol] exec[%d] on core[%x,%d] loaded input file in A[%d] / cycle %d\n", tid , cxy , lpid , cid , (unsigned int)date); #endif // Optionnal parallel display for the initial image if ( INITIAL_DISPLAY_ENABLE ) { // each thread[tid] copy npixels from A[cid] to in_win_buf buffer memcpy( in_win_buf + g_offset, A[cid] + l_offset, npixels ); // refresh the FBF window if( fbf_refresh_window( in_wid , global_lmin , global_lmax ) ) { printf("\n[convol error] in %s : thread[%d] cannot access FBF\n", __FUNCTION__ , tid ); pthread_exit( &THREAD_EXIT_FAILURE ); } #if VERBOSE_EXEC get_cycle( &date ); printf( "\n[convol] exec[%d] on core[%x,%d] completed initial display / cycle %d\n", tid , cxy , lpid , (unsigned int)date ); #endif //////////////////////////////// pthread_barrier_wait( &barrier ); } //////////////////////////////////////////////////////////// // parallel horizontal filter : // B <= Transpose(FH(A)) // D <= A - FH(A) // Each thread computes (image_nl/nthreads) lines. // The image must be extended : // if (z<0) TA(cid,l,z) == TA(cid,l,0) // if (z>image_np-1) TA(cid,l,z) == TA(cid,l,image_np-1) //////////////////////////////////////////////////////////// get_cycle( &date ); H_BEG[cid][lid] = (unsigned int)date; // l = global line index / p = absolute pixel index for (l = global_lmin; l < global_lmax; l++) { // src_c and src_l are the cluster index and the line index for A & D int src_c = l / lines_per_cluster; int src_l = l % lines_per_cluster; // We use the specific values of the horizontal ep-filter for optimisation: // sum(p) = sum(p-1) + TA[p+hrange] - TA[p-hrange-1] // To minimize the number of tests, the loop on pixels is split in three domains int sum_p = (hrange + 2) * TA(src_c, src_l, 0); for (z = 1; z < hrange; z++) { sum_p = sum_p + TA(src_c, src_l, z); } // first domain : from 0 to hrange for (p = 0; p < hrange + 1; p++) { // dst_c and dst_p are the cluster index and the pixel index for B int dst_c = p / pixels_per_cluster; int dst_p = p % pixels_per_cluster; sum_p = sum_p + (int) TA(src_c, src_l, p + hrange) - (int) TA(src_c, src_l, 0); TB(dst_c, dst_p, l) = sum_p / hnorm; TD(src_c, src_l, p) = (int) TA(src_c, src_l, p) - sum_p / hnorm; } // second domain : from (hrange+1) to (image_np-hrange-1) for (p = hrange + 1; p < image_np - hrange; p++) { // dst_c and dst_p are the cluster index and the pixel index for B int dst_c = p / pixels_per_cluster; int dst_p = p % pixels_per_cluster; sum_p = sum_p + (int) TA(src_c, src_l, p + hrange) - (int) TA(src_c, src_l, p - hrange - 1); TB(dst_c, dst_p, l) = sum_p / hnorm; TD(src_c, src_l, p) = (int) TA(src_c, src_l, p) - sum_p / hnorm; } // third domain : from (image_np-hrange) to (image_np-1) for (p = image_np - hrange; p < image_np; p++) { // dst_c and dst_p are the cluster index and the pixel index for B int dst_c = p / pixels_per_cluster; int dst_p = p % pixels_per_cluster; sum_p = sum_p + (int) TA(src_c, src_l, image_np - 1) - (int) TA(src_c, src_l, p - hrange - 1); TB(dst_c, dst_p, l) = sum_p / hnorm; TD(src_c, src_l, p) = (int) TA(src_c, src_l, p) - sum_p / hnorm; } #if SUPER_VERBOSE get_cycle( &date ); printf(" - line %d computed at cycle %d\n", l, (unsigned int)date ); #endif } get_cycle( &date ); H_END[cid][lid] = (unsigned int)date; #if VERBOSE_EXEC get_cycle( &date ); printf( "\n[convol] exec[%d] on core[%x,%d] completed horizontal filter / cycle %d\n", tid , cxy , lpid , (unsigned int)date ); #endif //////////////////////////////// pthread_barrier_wait( &barrier ); /////////////////////////////////////////////////////////////// // parallel vertical filter : // C <= Transpose(FV(B)) // Each thread computes (image_np/nthreads) columns // The image must be extended : // if (l<0) TB(cid,p,l) == TB(cid,p,0) // if (l>image_nl-1) TB(cid,p,l) == TB(cid,p,image_nl-1) /////////////////////////////////////////////////////////////// get_cycle( &date ); V_BEG[cid][lid] = (unsigned int)date; // l = global line index / p = pixel index in column for (p = column_pmin; p < column_pmax ; p++) { // src_c and src_p are the cluster index and the pixel index for B int src_c = p / pixels_per_cluster; int src_p = p % pixels_per_cluster; int sum_l; // We use the specific values of the vertical ep-filter // To minimize the number of tests, the image_nl lines are split in three domains // first domain : explicit computation for the first 18 values for (l = 0; l < 18; l++) { // dst_c and dst_l are the cluster index and the line index for C int dst_c = l / lines_per_cluster; int dst_l = l % lines_per_cluster; for (z = 0, sum_l = 0; z < 35; z++) { sum_l = sum_l + vf[z] * TB(src_c, src_p, max(l - 17 + z,0) ); } TC(dst_c, dst_l, p) = sum_l / vnorm; } // second domain for (l = 18; l < image_nl - 17; l++) { // dst_c and dst_l are the cluster index and the line index for C int dst_c = l / lines_per_cluster; int dst_l = l % lines_per_cluster; sum_l = sum_l + TB(src_c, src_p, l + 4) + TB(src_c, src_p, l + 8) + TB(src_c, src_p, l + 11) + TB(src_c, src_p, l + 15) + TB(src_c, src_p, l + 17) - TB(src_c, src_p, l - 5) - TB(src_c, src_p, l - 9) - TB(src_c, src_p, l - 12) - TB(src_c, src_p, l - 16) - TB(src_c, src_p, l - 18); TC(dst_c, dst_l, p) = sum_l / vnorm; } // third domain for (l = image_nl - 17; l < image_nl; l++) { // dst_c and dst_l are the cluster index and the line index for C int dst_c = l / lines_per_cluster; int dst_l = l % lines_per_cluster; sum_l = sum_l + TB(src_c, src_p, min(l + 4, image_nl - 1)) + TB(src_c, src_p, min(l + 8, image_nl - 1)) + TB(src_c, src_p, min(l + 11, image_nl - 1)) + TB(src_c, src_p, min(l + 15, image_nl - 1)) + TB(src_c, src_p, min(l + 17, image_nl - 1)) - TB(src_c, src_p, l - 5) - TB(src_c, src_p, l - 9) - TB(src_c, src_p, l - 12) - TB(src_c, src_p, l - 16) - TB(src_c, src_p, l - 18); TC(dst_c, dst_l, p) = sum_l / vnorm; } #if SUPER_VERBOSE get_cycle( &date ); printf(" - column %d computed at cycle %d\n", p, (unsigned int)date ); #endif } get_cycle( &date ); V_END[cid][lid] = (unsigned int)date; #if VERBOSE_EXEC get_cycle( &date ); printf( "\n[convol] exec[%d] on core[%x,%d] completed vertical filter / cycle %d\n", tid , cxy , lid , (unsigned int)date ); #endif //////////////////////////////// pthread_barrier_wait( &barrier ); /////////////////////////////////////////////////////////////// // build final image in local Z buffer from C & D local buffers // store it in output image file, and display it on FBF. // Z <= C + D /////////////////////////////////////////////////////////////// get_cycle( &date ); F_BEG[cid][lid] = (unsigned int)date; // Each thread[tid] set local buffer Z[cid] from local buffers C[cid] & D[cid] for( l = local_lmin ; l < local_lmax ; l++ ) { for( p = 0 ; p < image_np ; p++ ) { TZ(cid,l,p) = TC(cid,l,p) + TD(cid,l,p); } } // Each thread[tid] copy npixels from Z[cid] buffer to image_out buffer memcpy( image_out + g_offset, Z[cid] + l_offset, npixels ); // Optional parallel display of the final image if ( FINAL_DISPLAY_ENABLE ) { // each thread[tid] copy npixels from Z[cid] to out_win_buf buffer memcpy( out_win_buf + g_offset, Z[cid] + l_offset, npixels ); // refresh the FBF window if( fbf_refresh_window( out_wid , global_lmin , global_lmax ) ) { printf("\n[convol error] in %s : thread[%d] cannot access FBF\n", __FUNCTION__ , tid ); pthread_exit( &THREAD_EXIT_FAILURE ); } #if VERBOSE_EXEC get_cycle( &date ); printf( "\n[convol] exec[%d] on core[%x,%d] completed final display / cycle %d\n", tid , cxy , lpid , (unsigned int)date ); #endif } // Each thread[cid,0] releases the 5 local buffers if( lid == 0 ) { free( A[cid] ); free( B[cid] ); free( C[cid] ); free( D[cid] ); free( Z[cid] ); } get_cycle( &date ); F_END[cid][lid] = (unsigned int)date; // thread termination depends on the placement policy if( PARALLEL_PLACEMENT ) { // threads are runing in detached mode, and // each thread must signal completion by calling barrier // passed in arguments before exit pthread_barrier_wait( args->barrier ); pthread_exit( &THREAD_EXIT_SUCCESS ); } else { // threads are running in attached mode // all threads (but the one executing main) exit if ( tid != tid_main ) pthread_exit( &THREAD_EXIT_SUCCESS ); } return NULL; } // end execute() ////////////////////////// void instrument( FILE * f, char * filename ) { unsigned int nclusters = x_size * y_size; unsigned int cc, pp; unsigned int min_start = 0xFFFFFFFF; unsigned int max_start = 0; unsigned int min_h_beg = 0xFFFFFFFF; unsigned int max_h_beg = 0; unsigned int min_h_end = 0xFFFFFFFF; unsigned int max_h_end = 0; unsigned int min_v_beg = 0xFFFFFFFF; unsigned int max_v_beg = 0; unsigned int min_v_end = 0xFFFFFFFF; unsigned int max_v_end = 0; unsigned int min_f_beg = 0xFFFFFFFF; unsigned int max_f_beg = 0; unsigned int min_f_end = 0xFFFFFFFF; unsigned int max_f_end = 0; for (cc = 0; cc < nclusters; cc++) { for (pp = 0; pp < ncores; pp++ ) { if (START[cc][pp] < min_start) min_start = START[cc][pp]; if (START[cc][pp] > max_start) max_start = START[cc][pp]; if (H_BEG[cc][pp] < min_h_beg) min_h_beg = H_BEG[cc][pp]; if (H_BEG[cc][pp] > max_h_beg) max_h_beg = H_BEG[cc][pp]; if (H_END[cc][pp] < min_h_end) min_h_end = H_END[cc][pp]; if (H_END[cc][pp] > max_h_end) max_h_end = H_END[cc][pp]; if (V_BEG[cc][pp] < min_v_beg) min_v_beg = V_BEG[cc][pp]; if (V_BEG[cc][pp] > max_v_beg) max_v_beg = V_BEG[cc][pp]; if (V_END[cc][pp] < min_v_end) min_v_end = V_END[cc][pp]; if (V_END[cc][pp] > max_v_end) max_v_end = V_END[cc][pp]; if (F_BEG[cc][pp] < min_f_beg) min_f_beg = F_BEG[cc][pp]; if (F_BEG[cc][pp] > max_f_beg) max_f_beg = F_BEG[cc][pp]; if (F_END[cc][pp] < min_f_end) min_f_end = F_END[cc][pp]; if (F_END[cc][pp] > max_f_end) max_f_end = F_END[cc][pp]; } } // display on terminal printf( "\n ------ %s ------\n" , filename ); printf(" - START : min = %d / max = %d / med = %d / delta = %d\n", min_start, max_start, (min_start+max_start)/2, max_start-min_start); printf(" - H_BEG : min = %d / max = %d / med = %d / delta = %d\n", min_h_beg, max_h_beg, (min_h_beg+max_h_beg)/2, max_h_beg-min_h_beg); printf(" - H_END : min = %d / max = %d / med = %d / delta = %d\n", min_h_end, max_h_end, (min_h_end+max_h_end)/2, max_h_end-min_h_end); printf(" - V_BEG : min = %d / max = %d / med = %d / delta = %d\n", min_v_beg, max_v_beg, (min_v_beg+max_v_beg)/2, max_v_beg-min_v_beg); printf(" - V_END : min = %d / max = %d / med = %d / delta = %d\n", min_v_end, max_v_end, (min_v_end+max_v_end)/2, max_v_end-min_v_end); printf(" - D_BEG : min = %d / max = %d / med = %d / delta = %d\n", min_f_beg, max_f_beg, (min_f_beg+max_f_beg)/2, max_f_beg-min_f_beg); printf(" - D_END : min = %d / max = %d / med = %d / delta = %d\n", min_f_end, max_f_end, (min_f_end+max_f_end)/2, max_f_end-min_f_end); printf( "\n General Scenario (Kcycles)\n" ); printf( " - LOAD IMAGE = %d\n", (min_h_beg - min_start)/1000 ); printf( " - H_FILTER = %d\n", (max_h_end - min_h_beg)/1000 ); printf( " - BARRIER HORI/VERT = %d\n", (min_v_beg - max_h_end)/1000 ); printf( " - V_FILTER = %d\n", (max_v_end - min_v_beg)/1000 ); printf( " - BARRIER VERT/DISP = %d\n", (min_f_beg - max_v_end)/1000 ); printf( " - DISPLAY = %d\n", (max_f_end - min_f_beg)/1000 ); printf( " \nSEQUENCIAL = %d / PARALLEL = %d\n", SEQUENCIAL_TIME/1000, PARALLEL_TIME/1000 ); // save on disk fprintf( f , "\n ------ %s ------\n" , filename ); fprintf( f , " - START : min = %d / max = %d / med = %d / delta = %d\n", min_start, max_start, (min_start+max_start)/2, max_start-min_start); fprintf( f , " - H_BEG : min = %d / max = %d / med = %d / delta = %d\n", min_h_beg, max_h_beg, (min_h_beg+max_h_beg)/2, max_h_beg-min_h_beg); fprintf( f , " - H_END : min = %d / max = %d / med = %d / delta = %d\n", min_h_end, max_h_end, (min_h_end+max_h_end)/2, max_h_end-min_h_end); fprintf( f , " - V_BEG : min = %d / max = %d / med = %d / delta = %d\n", min_v_beg, max_v_beg, (min_v_beg+max_v_beg)/2, max_v_beg-min_v_beg); fprintf( f , " - V_END : min = %d / max = %d / med = %d / delta = %d\n", min_v_end, max_v_end, (min_v_end+max_v_end)/2, max_v_end-min_v_end); fprintf( f , " - D_BEG : min = %d / max = %d / med = %d / delta = %d\n", min_f_beg, max_f_beg, (min_f_beg+max_f_beg)/2, max_f_beg-min_f_beg); fprintf( f , " - D_END : min = %d / max = %d / med = %d / delta = %d\n", min_f_end, max_f_end, (min_f_end+max_f_end)/2, max_f_end-min_f_end); fprintf( f , "\n General Scenario (Kcycles)\n" ); fprintf( f , " - LOAD IMAGE = %d\n", (min_h_beg - min_start)/1000 ); fprintf( f , " - H_FILTER = %d\n", (max_h_end - min_h_beg)/1000 ); fprintf( f , " - BARRIER HORI/VERT = %d\n", (min_v_beg - max_h_end)/1000 ); fprintf( f , " - V_FILTER = %d\n", (max_v_end - min_v_beg)/1000 ); fprintf( f , " - BARRIER VERT/DISP = %d\n", (min_f_beg - max_v_end)/1000 ); fprintf( f , " - SAVE = %d\n", (max_f_end - min_f_beg)/1000 ); fprintf( f , " \nSEQUENCIAL = %d / PARALLEL = %d\n", SEQUENCIAL_TIME/1000, PARALLEL_TIME/1000 ); } // end instrument() // Local Variables: // tab-width: 3 // c-basic-offset: 3 // c-file-offsets:((innamespace . 0)(inline-open . 0)) // indent-tabs-mode: nil // End: // vim: filetype=cpp:expandtab:shiftwidth=3:tabstop=3:softtabstop=3