Shadowrun: Awakened 29 September 2011 - Build 871
square.cpp
Go to the documentation of this file.
00001 /*
00002     Copyright 2005-2010 Intel Corporation.  All Rights Reserved.
00003 
00004     This file is part of Threading Building Blocks.
00005 
00006     Threading Building Blocks is free software; you can redistribute it
00007     and/or modify it under the terms of the GNU General Public License
00008     version 2 as published by the Free Software Foundation.
00009 
00010     Threading Building Blocks is distributed in the hope that it will be
00011     useful, but WITHOUT ANY WARRANTY; without even the implied warranty
00012     of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00013     GNU General Public License for more details.
00014 
00015     You should have received a copy of the GNU General Public License
00016     along with Threading Building Blocks; if not, write to the Free Software
00017     Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
00018 
00019     As a special exception, you may use this file as part of a free software
00020     library without restriction.  Specifically, if other files instantiate
00021     templates or use macros or inline functions from this file, or you compile
00022     this file and link it with other files to produce an executable, this
00023     file does not by itself cause the resulting executable to be covered by
00024     the GNU General Public License.  This exception does not however
00025     invalidate any other reasons why the executable file might be covered by
00026     the GNU General Public License.
00027 */
00028 
00029 //
00030 // Example program that reads a file of decimal integers in text format
00031 // and changes each to its square.
00032 // 
00033 #include "tbb/pipeline.h"
00034 #include "tbb/tick_count.h"
00035 #include "tbb/task_scheduler_init.h"
00036 #include "tbb/tbb_allocator.h"
00037 #include <cstring>
00038 #include <cstdlib>
00039 #include <cstdio>
00040 #include <cctype>
00041 
00042 using namespace std;
00043 
00045 
00047 class TextSlice {
00049     char* logical_end;
00051     char* physical_end;
00052 public:
00054     static TextSlice* allocate( size_t max_size ) {
00055         // +1 leaves room for a terminating null character.
00056         TextSlice* t = (TextSlice*)tbb::tbb_allocator<char>().allocate( sizeof(TextSlice)+max_size+1 );
00057         t->logical_end = t->begin();
00058         t->physical_end = t->begin()+max_size;
00059         return t;
00060     }
00062     void free() {
00063         tbb::tbb_allocator<char>().deallocate((char*)this,sizeof(TextSlice)+(physical_end-begin())+1);
00064     } 
00066     char* begin() {return (char*)(this+1);}
00068     char* end() {return logical_end;}
00070     size_t size() const {return logical_end-(char*)(this+1);}
00072     size_t avail() const {return physical_end-logical_end;}
00074     void append( char* first, char* last ) {
00075         memcpy( logical_end, first, last-first );
00076         logical_end += last-first;
00077     }
00079     void set_end( char* p ) {logical_end=p;}
00080 };
00081 
00082 const size_t MAX_CHAR_PER_INPUT_SLICE = 4000;
00083 static const char* InputFileName = "input.txt";
00084 static const char* OutputFileName = "output.txt";
00085 
00086 class MyInputFilter: public tbb::filter {
00087 public:
00088     MyInputFilter( FILE* input_file_ );
00089     ~MyInputFilter();
00090 private:
00091     FILE* input_file;
00092     TextSlice* next_slice;
00093     /*override*/ void* operator()(void*);
00094 };
00095 
00096 MyInputFilter::MyInputFilter( FILE* input_file_ ) : 
00097     filter(serial_in_order),
00098     input_file(input_file_),
00099     next_slice( TextSlice::allocate( MAX_CHAR_PER_INPUT_SLICE ) )
00100 { 
00101 }
00102 
00103 MyInputFilter::~MyInputFilter() {
00104     next_slice->free();
00105 }
00106  
00107 void* MyInputFilter::operator()(void*) {
00108     // Read characters into space that is available in the next slice.
00109     size_t m = next_slice->avail();
00110     size_t n = fread( next_slice->end(), 1, m, input_file );
00111     if( !n && next_slice->size()==0 ) {
00112         // No more characters to process
00113         return NULL;
00114     } else {
00115         // Have more characters to process.
00116         TextSlice& t = *next_slice;
00117         next_slice = TextSlice::allocate( MAX_CHAR_PER_INPUT_SLICE );
00118         char* p = t.end()+n;
00119         if( n==m ) {
00120             // Might have read partial number.  If so, transfer characters of partial number to next slice.
00121             while( p>t.begin() && isdigit(p[-1]) ) 
00122                 --p;
00123             next_slice->append( p, t.end()+n );
00124         }
00125         t.set_end(p);
00126         return &t;
00127     }
00128 }
00129     
00131 class MyTransformFilter: public tbb::filter {
00132 public:
00133     MyTransformFilter();
00134     /*override*/void* operator()( void* item );
00135 };
00136 
00137 MyTransformFilter::MyTransformFilter() : 
00138     tbb::filter(parallel) 
00139 {}  
00140 
00141 /*override*/void* MyTransformFilter::operator()( void* item ) {
00142     TextSlice& input = *static_cast<TextSlice*>(item);
00143     // Add terminating null so that strtol works right even if number is at end of the input.
00144     *input.end() = '\0';
00145     char* p = input.begin();
00146     TextSlice& out = *TextSlice::allocate( 2*MAX_CHAR_PER_INPUT_SLICE );
00147     char* q = out.begin();
00148     for(;;) {
00149         while( p<input.end() && !isdigit(*p) ) 
00150             *q++ = *p++; 
00151         if( p==input.end() ) 
00152             break;
00153         long x = strtol( p, &p, 10 );
00154         // Note: no overflow checking is needed here, as we have twice the 
00155         // input string length, but the square of a non-negative integer n 
00156         // cannot have more than twice as many digits as n.
00157         long y = x*x; 
00158         sprintf(q,"%ld",y);
00159         q = strchr(q,0);
00160     }
00161     out.set_end(q);
00162     input.free();
00163     return &out;
00164 }
00165          
00167 class MyOutputFilter: public tbb::filter {
00168     FILE* my_output_file;
00169 public:
00170     MyOutputFilter( FILE* output_file );
00171     /*override*/void* operator()( void* item );
00172 };
00173 
00174 MyOutputFilter::MyOutputFilter( FILE* output_file ) : 
00175     tbb::filter(serial_in_order),
00176     my_output_file(output_file)
00177 {
00178 }
00179 
00180 void* MyOutputFilter::operator()( void* item ) {
00181     TextSlice& out = *static_cast<TextSlice*>(item);
00182     size_t n = fwrite( out.begin(), 1, out.size(), my_output_file );
00183     if( n!=out.size() ) {
00184         fprintf(stderr,"Can't write into file '%s'\n", OutputFileName);
00185         exit(1);
00186     }
00187     out.free();
00188     return NULL;
00189 }
00190 
00191 static int NThread = tbb::task_scheduler_init::automatic;
00192 static bool is_number_of_threads_set = false;
00193 
00194 void Usage()
00195 {
00196     fprintf( stderr, "Usage:\tsquare [input-file [output-file [nthread]]]\n");
00197 }
00198 
00199 int ParseCommandLine(  int argc, char* argv[] ) {
00200     // Parse command line
00201     if( argc> 4 ){
00202         Usage();
00203         return 0;
00204     }
00205     if( argc>=2 ) InputFileName = argv[1];
00206     if( argc>=3 ) OutputFileName = argv[2];
00207     if( argc>=4 ) {
00208         NThread = strtol(argv[3],0,0);
00209         if( NThread<1 ) {
00210             fprintf(stderr,"nthread set to %d, but must be at least 1\n",NThread);
00211             return 0;
00212         }
00213         is_number_of_threads_set = true; //Number of threads is set explicitly
00214     }
00215     return 1;
00216 }
00217 
00218 int run_pipeline( int nthreads )
00219 {
00220     FILE* input_file = fopen(InputFileName,"r");
00221     if( !input_file ) {
00222         perror( InputFileName );
00223         Usage();
00224         return 0;
00225     }
00226     FILE* output_file = fopen(OutputFileName,"w");
00227     if( !output_file ) {
00228         perror( OutputFileName );
00229         return 0;
00230     }
00231 
00232     // Create the pipeline
00233     tbb::pipeline pipeline;
00234 
00235     // Create file-reading writing stage and add it to the pipeline
00236     MyInputFilter input_filter( input_file );
00237     pipeline.add_filter( input_filter );
00238 
00239     // Create capitalization stage and add it to the pipeline
00240     MyTransformFilter transform_filter; 
00241     pipeline.add_filter( transform_filter );
00242 
00243     // Create file-writing stage and add it to the pipeline
00244     MyOutputFilter output_filter( output_file );
00245     pipeline.add_filter( output_filter );
00246 
00247     // Run the pipeline
00248     tbb::tick_count t0 = tbb::tick_count::now();
00249     // Need more than one token in flight per thread to keep all threads 
00250     // busy; 2-4 works
00251     pipeline.run( nthreads*4 );
00252     tbb::tick_count t1 = tbb::tick_count::now();
00253 
00254     fclose( output_file );
00255     fclose( input_file );
00256 
00257     if (is_number_of_threads_set) {
00258         printf("threads = %d time = %g\n", nthreads, (t1-t0).seconds());
00259     } else {
00260         if ( nthreads == 1 ){
00261             printf("serial run   time = %g\n", (t1-t0).seconds());
00262         } else {
00263             printf("parallel run time = %g\n", (t1-t0).seconds());
00264         }
00265     }
00266     return 1;
00267 }
00268 
00269 int main( int argc, char* argv[] ) {
00270     if(!ParseCommandLine( argc, argv ))
00271         return 1;
00272     if (is_number_of_threads_set) {
00273         // Start task scheduler
00274         tbb::task_scheduler_init init( NThread );
00275         if(!run_pipeline (NThread))
00276             return 1;
00277     } else { // Number of threads wasn't set explicitly. Run serial and parallel version
00278         { // serial run
00279             tbb::task_scheduler_init init_serial(1);
00280             if(!run_pipeline (1))
00281                 return 1;
00282         }
00283         { // parallel run (number of threads is selected automatically)
00284             tbb::task_scheduler_init init_parallel;
00285             if(!run_pipeline (init_parallel.default_num_threads()))
00286                 return 1;
00287         }
00288     }
00289     return 0;
00290 }

Copyright © 2007-2010 by The Shadowrun: Awakened Team. This work is licensed under the GNU Lesser General Public License 3.

GNU Lesser General Public License 3 Sourceforge.net