| // RUN: %s Max=15000 Matrix=200 Complex=20000 Iterator=20000 | grep DONE |
| |
| //============================================================================= |
| // |
| // OOPACK - a benchmark for comparing OOP vs. C-style programming. |
| // Copyright (C) 1995 Arch D. Robison |
| // |
| // This program is free software; you can redistribute it and/or modify |
| // it under the terms of the GNU General Public License as published by |
| // the Free Software Foundation; either version 2 of the License, or |
| // (at your option) any later version. |
| // |
| // This program is distributed in the hope that it will be useful, |
| // but WITHOUT ANY WARRANTY; without even the implied warranty of |
| // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| // GNU General Public License for more details. |
| // |
| // For a copy of the GNU General Public License, write to the Free Software |
| // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. |
| // |
| //============================================================================= |
| // |
| // OOPACK: a benchmark for comparing OOP vs. C-style programming. |
| // |
| // Version: 1.7 |
| // |
| // Author: Arch D. Robison (robison@kai.com) |
| // Kuck & Associates |
| // 1906 Fox Dr. |
| // Champaign IL 61820 |
| // |
| // Web Info: http://www.kai.com/oopack/oopack.html |
| // |
| // Code last revised: November 21, 1995 |
| // Comments last revised: March 23, 1998 |
| // |
| // This benchmark program contains a suite of tests that measure the relative |
| // performance of object-oriented-programming (OOP) in C++ versus just writing |
| // plain C-style code in C++. All of the tests are written so that a |
| // compiler can in principle transform the OOP code into the C-style code. |
| // After you run this benchmark and discover just how much you are paying to |
| // use object-oriented programming, you will probably say: OOP? ACK! |
| // (Unless, of course, you have Kuck & Associates' C++ compiler.) |
| // |
| // TO COMPILE |
| // |
| // Compile with your favorite C++ compiler. E.g. ``CC -O2 oopack.C''. |
| // On most machines, no special command-line options are required. |
| // For Suns only, you need to define the symbol ``sun4''. |
| // E.g. ``g++ -O -Dsun4 oopack.C''. |
| // |
| // TO RUN |
| // |
| // To run the benchmark, run |
| // |
| // a.out Max=50000 Matrix=500 Complex=20000 Iterator=50000 |
| // |
| // This runs the four tests for the specified number of iterations. |
| // E.g., the Max test is run for 50000 iterations. You may want to |
| // adjust the number of iterations to be small enough to get |
| // an answer in reasonable time, but large enough to get a reasonably |
| // accurate answer. |
| // |
| // INTERPRETING THE RESULTS |
| // |
| // Below is an example command line and the program's output. |
| // We used an SGI Origin 200 running IRIX 6.4 (64bit). |
| // |
| // % KCC_3.3a +K3 -O2 oopack.C |
| // |
| // % a.out Max=100000 Matrix=2000 Complex=200000 Iterator=200000 |
| // OOPACK Version 1.7 |
| // |
| // For results on various systems and compilers, examine this Web Page: |
| // http://www.kai.com/oopack/oopack.html |
| // |
| // Report your results by sending e-mail to oopack@kai.com. |
| // For a run to be accepted, adjust the number of iterations for each test |
| // so that each time reported is greater than 10 seconds. |
| // |
| // Send this output, along with: |
| // |
| // * your |
| // + name ------------------- |
| // + company/institution ---- |
| // |
| // * the compiler |
| // + name ------------------- |
| // + version number --------- |
| // + options used ----------- |
| // |
| // * the operating system |
| // + name ------------------- |
| // + version number --------- |
| // |
| // * the machine |
| // + manufacturer ----------- |
| // + model number ----------- |
| // + processor clock speed -- |
| // + cache memory size ------ |
| // |
| // Seconds Mflops |
| // Test Iterations C OOP C OOP Ratio |
| // ---- ---------- ----------- ----------- ----- |
| // Max 100000 1.4 1.4 71.4 71.4 1.0 |
| // Matrix 2000 3.4 3.3 147.9 150.2 1.0 |
| // Complex 200000 7.8 7.3 204.9 218.9 0.9 |
| // Iterator 200000 2.2 2.2 177.8 177.8 1.0 |
| // |
| // |
| // The ``Test'' column gives the names of the four tests that are run. |
| // The ``Iterations'' column gives the number of iterations that a test |
| // was run. The two ``Seconds'' columns give the C-style and OOP-style |
| // running times for a test. The two ``Mflops'' columns give the |
| // corresponding megaflop rates. The ``Ratio'' column gives the ratio |
| // between the times. |
| // |
| // Beware that a low ``Ratio'' could indicate either that the OOP-style |
| // code is compiled very well, or that the C-style code is compiled poorly. |
| // OOPACK performance figures for KAI's C++ and some other compilers |
| // can be found in http://www.kai.com/oopack/oopack.html. |
| // |
| // Revision History |
| // 9/17/93 Version 1.0 released |
| // 10/5/93 Allow results to be printed even if checksums do not match. |
| // 10/5/93 Increased ``Tolerance'' to allow 10-second runs on RS/6000. |
| // 10/5/93 Version 1.1 released |
| // 1/10/94 Change author's address from Shell to KAI |
| // 1/13/94 Added #define's for conditional compilation of individual tests |
| // 1/21/94 Converted test functions to virtual members of class Benchmark. |
| // 10/11/94 Added routine to inform user of command-line usage. |
| // 10/11/94 Version 1.5 released. |
| // 11/21/95 V1.6 Added "mail results to oopack@kai.com" message in output |
| // 11/28/95 V1.7 Added company/institution to requested information |
| |
| //============================================================================= |
| |
| #include <assert.h> |
| #include <ctype.h> |
| #include <float.h> |
| #include <math.h> |
| #include <stdio.h> |
| #include <time.h> |
| #include <string.h> |
| #include <stdlib.h> |
| |
| // |
| // The source-code begins with the benchmark computations themselves and |
| // ends with code for collecting statistics. Each benchmark ``Foo'' is |
| // a class FooBenchmark derived from class Benchmark. The relevant methods |
| // are: |
| // |
| // init - Initialize the input data for the benchmark |
| // |
| // c_style - C-style code |
| // |
| // oop_style - OOP-style code |
| // |
| // check - computes number of floating-point operations and a checksum. |
| // |
| const int BenchmarkListMax = 4; |
| |
| class Benchmark { |
| public: |
| void time_both( int iteration_count ) const; |
| void time_one( void (Benchmark::*function)() const, int iterations, double& sec, double& Mflop, double& checksum ) const; |
| virtual const char * name() const = 0; |
| virtual void init() const = 0; |
| virtual void c_style() const = 0; |
| virtual void oop_style() const = 0; |
| virtual void check( int iterations, double& flops, double& checksum ) const = 0; |
| static Benchmark * find( const char * name ); |
| private: |
| static Benchmark * list[BenchmarkListMax]; |
| static int count; |
| protected: |
| Benchmark() {list[count++] = this;} |
| }; |
| |
| // The initializer for Benchmark::count *must* precede the declarations |
| // of derived of class Benchmark. |
| int Benchmark::count = 0; |
| Benchmark * Benchmark::list[BenchmarkListMax]; |
| |
| // |
| // The ``iterations'' argument is the number of times that the benchmark |
| // computation was called. The computed checksum that ensures that the |
| // C-style code and OOP code are computing the same result. This |
| // variable also prevents really clever optimizers from removing the |
| // the guts of the computations that otherwise would be unused. |
| // |
| |
| // Each of the following symbols must be defined to enable a test, or |
| // undefined to disable a test. The reason for doing this with the |
| // preprocessor is that some compilers may choke on specific tests. |
| #define HAVE_MAX 1 |
| #define HAVE_MATRIX 1 |
| #define HAVE_COMPLEX 1 |
| #define HAVE_ITERATOR 1 |
| |
| const int N = 1000; |
| |
| #if HAVE_MAX |
| //============================================================================= |
| // |
| // Max benchmark |
| // |
| // This benchmark measures how well a C++ compiler inlines a function that |
| // returns the result of a comparison. |
| // |
| // The functions C_Max and OOP_Max compute the maximum over a vector. |
| // The only difference is that C_Max writes out the comparison operation |
| // explicitly, and OOP_Max calls an inline function to do the comparison. |
| // |
| // This benchmark is included because some compilers do not compile |
| // inline functions into conditional branches as well as they might. |
| // |
| const int M = 1000; // Dimension of vector |
| double U[M]; // The vector |
| double MaxResult; // Result of max computation |
| |
| class MaxBenchmark: public Benchmark { |
| private: |
| const char * name() const {return "Max";} |
| void init() const; |
| void c_style() const; |
| void oop_style() const; |
| void check( int iterations, double& flops, double& checksum ) const; |
| } TheMaxBenchmark; |
| |
| void MaxBenchmark::c_style() const // Compute max of vector (C-style) |
| { |
| double max = U[0]; |
| for( int k=1; k<M; k++ ) // Loop over vector elements |
| if( U[k] > max ) |
| max=U[k]; |
| MaxResult = max; |
| } |
| |
| inline int Greater( double i, double j ) |
| { |
| return i>j; |
| } |
| |
| void MaxBenchmark::oop_style() const // Compute max of vector (OOP-style) |
| { |
| double max = U[0]; |
| for( int k=1; k<M; k++ ) // Loop over vector elements |
| if( Greater( U[k], max ) ) |
| max=U[k]; |
| MaxResult = max; |
| } |
| |
| void MaxBenchmark::init() const |
| { |
| for( int k=0; k<M; k++ ) |
| U[k] = k&1 ? -k : k; |
| } |
| |
| void MaxBenchmark::check( int iterations, double& flops, double& checksum ) const |
| { |
| flops = (double)M*iterations; |
| checksum = MaxResult; |
| } |
| #endif /* HAVE_MAX */ |
| |
| #if HAVE_MATRIX |
| //============================================================================= |
| // |
| // Matrix benchmark |
| // |
| // This benchmark measures how well a C++ compiler performs constant propagation and |
| // strength-reduction on classes. C_Matrix multiplies two matrices using C-style code; |
| // OOP_Matrix does the same with OOP-style code. To maximize performance on most RISC |
| // processors, the benchmark requires that the compiler perform strength-reduction and |
| // constant-propagation in order to simplify the indexing calculations in the inner loop. |
| // |
| const int L = 50; // Dimension of (square) matrices. |
| |
| double C[L*L], D[L*L], E[L*L]; // The matrices to be multiplied. |
| |
| class MatrixBenchmark: public Benchmark { |
| private: |
| const char * name() const {return "Matrix";} |
| void init() const; |
| void c_style() const; |
| void oop_style() const; |
| void check( int iterations, double& flops, double& checksum ) const; |
| } TheMatrixBenchmark; |
| |
| void MatrixBenchmark::c_style() const { // Compute E=C*D with C-style code. |
| for( int i=0; i<L; i++ ) |
| for( int j=0; j<L; j++ ) { |
| double sum = 0; |
| for( int k=0; k<L; k++ ) |
| sum += C[L*i+k]*D[L*k+j]; |
| E[L*i+j] = sum; |
| } |
| } |
| |
| class Matrix { // Class Matrix represents a matrix stored in row-major format (same as C). |
| private: |
| double *data; // Pointer to matrix data |
| public: |
| int rows, cols; // Number of rows and columns |
| |
| Matrix( int rows_, int cols_, double * data_ ) : |
| data(data_), rows(rows_), cols(cols_) |
| {} |
| |
| double& operator()( int i, int j ) { // Access element at row i, column j |
| return data[cols*i+j]; |
| } |
| }; |
| |
| void MatrixBenchmark::oop_style() const { // Compute E=C*D with OOP-style code. |
| Matrix c( L, L, C ); // Set up three matrices |
| Matrix d( L, L, D ); |
| Matrix e( L, L, E ); |
| for( int i=0; i<e.rows; i++ ) // Do matrix-multiplication |
| for( int j=0; j<e.cols; j++ ) { |
| double sum = 0; |
| for( int k=0; k<e.cols; k++ ) |
| sum += c(i,k)*d(k,j); |
| e(i,j) = sum; |
| } |
| } |
| |
| void MatrixBenchmark::init() const |
| { |
| for( int j=0; j<L*L; j++ ) { |
| C[j] = j+1; |
| D[j] = 1.0/(j+1); |
| } |
| } |
| |
| void MatrixBenchmark::check( int iterations, double& flops, double& checksum ) const |
| { |
| double sum = 0; |
| for( int k=0; k<L*L; k++ ) |
| sum += E[k]; |
| checksum = sum; |
| flops = 2.0*L*L*L*iterations; |
| } |
| #endif /* HAVE_MATRIX */ |
| |
| #if HAVE_ITERATOR |
| //============================================================================= |
| // |
| // Iterator benchmark |
| // |
| // Iterators are a common abstraction in object-oriented programming, which |
| // unfortunately may incur a high cost if compiled inefficiently. |
| // The iterator benchmark below computes a dot-product using C-style code |
| // and OOP-style code. All methods of the iterator are inline, and in |
| // principle correspond exactly to the C-style code. |
| // |
| // Note that the OOP-style code uses two iterators, but the C-style |
| // code uses a single index. Good common-subexpression elimination should, |
| // in principle, reduce the two iterators to a single index variable, or |
| // conversely, good strength-reduction should convert the single index into |
| // two iterators! |
| // |
| double A[N]; |
| double B[N]; |
| double IteratorResult; |
| |
| class IteratorBenchmark: public Benchmark { |
| private: |
| const char * name() const {return "Iterator";} |
| void init() const; |
| void c_style() const; |
| void oop_style() const; |
| void check( int iterations, double& flops, double& checksum ) const; |
| } TheIteratorBenchmark; |
| |
| void IteratorBenchmark::c_style() const // Compute dot-product with C-style code |
| { |
| double sum = 0; |
| for( int i=0; i<N; i++ ) |
| sum += A[i]*B[i]; |
| IteratorResult = sum; |
| } |
| |
| class Iterator { // Iterator for iterating over array of double |
| private: |
| int index; // Index of current element |
| const int limit; // 1 + index of last element |
| double * const array; // Pointer to array |
| public: |
| double look() {return array[index];} // Get current element |
| void next() {index++;} // Go to next element |
| int done() {return index>=limit;} // True iff no more elements |
| Iterator( double * array1, int limit1 ) : |
| index(0), |
| limit(limit1), |
| array(array1) |
| {} |
| }; |
| |
| void IteratorBenchmark::oop_style() const // Compute dot-product with OOP-style code |
| { |
| double sum = 0; |
| for( Iterator ai(A,N), bi(B,N); !ai.done(); ai.next(), bi.next() ) |
| sum += ai.look()*bi.look(); |
| IteratorResult = sum; |
| } |
| |
| void IteratorBenchmark::init() const |
| { |
| for( int i=0; i<N; i++ ) { |
| A[i] = i+1; |
| B[i] = 1.0/(i+1); |
| } |
| } |
| |
| void IteratorBenchmark::check( int iterations, double& flops, double& checksum ) const { |
| flops = 2*N*iterations; |
| checksum = IteratorResult; |
| } |
| #endif /* HAVE_ITERATOR */ |
| |
| #if HAVE_COMPLEX |
| //============================================================================= |
| // |
| // Complex benchmark |
| // |
| // Complex numbers are a common abstraction in scientific programming. |
| // This benchmark measures how fast they are in C++ relative to the same |
| // calculation done by explicitly writing out the real and imaginary parts. |
| // The calculation is a complex-valued ``SAXPY'' operation. |
| // |
| // The complex arithmetic is all inlined, so in principle the code should |
| // run as fast as the version using explicit real and imaginary parts. |
| // |
| class ComplexBenchmark: public Benchmark { |
| private: |
| const char * name() const {return "Complex";} |
| void init() const; |
| void c_style() const; |
| void oop_style() const; |
| void check( int iterations, double& flops, double& checksum ) const; |
| } TheComplexBenchmark; |
| |
| class Complex { |
| public: |
| double re, im; |
| Complex( double r, double i ) : re(r), im(i) {} |
| Complex() {} |
| }; |
| |
| inline Complex operator+( Complex a, Complex b ) // Complex add |
| { |
| return Complex( a.re+b.re, a.im+b.im ); |
| } |
| |
| inline Complex operator*( Complex a, Complex b ) // Complex multiply |
| { |
| return Complex( a.re*b.re-a.im*b.im, a.re*b.im+a.im*b.re ); |
| } |
| |
| Complex X[N], Y[N]; // Arrays used by benchmark |
| |
| void ComplexBenchmark::c_style() const // C-style complex-valued SAXPY operation |
| { |
| double factor_re = 0.5; |
| double factor_im = 0.86602540378443864676; |
| for( int k=0; k<N; k++ ) { |
| Y[k].re = Y[k].re + factor_re*X[k].re - factor_im*X[k].im; |
| Y[k].im = Y[k].im + factor_re*X[k].im + factor_im*X[k].re; |
| } |
| } |
| |
| void ComplexBenchmark::oop_style() const // OOP-style complex-valued SAXPY operation |
| { |
| Complex factor( 0.5, 0.86602540378443864676 ); |
| for( int k=0; k<N; k++ ) |
| Y[k] = Y[k] + factor*X[k]; |
| } |
| |
| void ComplexBenchmark::init() const |
| { |
| for( int k=0; k<N; k++ ) { |
| X[k] = Complex( k+1, 1.0/(k+1) ); |
| Y[k] = Complex( 0, 0 ); |
| } |
| } |
| |
| void ComplexBenchmark::check( int iterations, double& flops, double& checksum ) const { |
| double sum = 0; |
| for( int k=0; k<N; k++ ) |
| sum += Y[k].re + Y[k].im; |
| checksum = sum; |
| flops = 8*N*iterations; |
| } |
| #endif /* HAVE_COMPLEX */ |
| |
| //============================================================================= |
| // End of benchmark computations. |
| //============================================================================= |
| |
| // All the code below is for running and timing the benchmarks. |
| #if defined(sun4) && !defined(CLOCKS_PER_SEC) |
| // Sun/4 include-files seem to be missing CLOCKS_PER_SEC. |
| #define CLOCKS_PER_SEC 1000000 |
| #endif |
| |
| // |
| // TimeOne |
| // |
| // Time a single benchmark computation. |
| // |
| // Inputs |
| // function = pointer to function to be run and timed. |
| // iterations = number of times to call function. |
| // |
| // Outputs |
| // sec = Total number of seconds for calls of function. |
| // Mflop = Megaflop rate of function. |
| // checksum = checksum computed by function. |
| // |
| void Benchmark::time_one( void (Benchmark::*function)() const, int iterations, double& sec, double& Mflop, double& checksum ) const |
| { |
| // Initialize and run code once to load caches |
| init(); |
| (this->*function)(); |
| |
| // Initialize and run code. |
| init(); |
| clock_t t0 = clock(); |
| for( int k=0; k<iterations; k++ ) |
| (this->*function)(); |
| clock_t t1 = clock(); |
| |
| // Update checksum and compute number of floating-point operations. |
| double flops; |
| check( iterations, flops, checksum ); |
| |
| sec = (t1-t0) / (double)CLOCKS_PER_SEC; |
| Mflop = flops/sec*1e-6; |
| |
| } |
| |
| // |
| // The variable ``C_Seconds'' is the time in seconds in which to run the |
| // C-style benchmarks. |
| // |
| double C_Seconds = 1; |
| |
| // |
| // The variable ``Tolerance'' is the maximum allowed relative difference |
| // between the C and OOP checksums. Machines with multiply-add |
| // instructions may produce different answers when they use those |
| // instructions rather than separate instructions. |
| // |
| // There is nothing magic about the 32, it's just the result of tweaking. |
| // |
| const double Tolerance = 64*DBL_EPSILON; |
| |
| Benchmark * Benchmark::find( const char * name ) { |
| for( int i=0; i<count; i++ ) |
| if( strcmp( name, list[i]->name() )== 0 ) |
| return list[i]; |
| return NULL; |
| } |
| |
| // |
| // Benchmark::time_both |
| // |
| // Runs the C and Oop versions of a benchmark computation, and print the |
| // results. |
| // |
| // Inputs |
| // name = name of the benchmark |
| // c_style = benchmark written in C-style code |
| // oop_style = benchmark written in OOP-style code |
| // check = routine to compute checksum on answer |
| // |
| void Benchmark::time_both( int iterations ) const { |
| // Run the C-style code. |
| double c_sec, c_Mflop, c_checksum; |
| time_one( &Benchmark::c_style, iterations, c_sec, c_Mflop, c_checksum ); |
| |
| // Run the OOP-style code. |
| double oop_sec, oop_Mflop, oop_checksum; |
| time_one( &Benchmark::oop_style, iterations, oop_sec, oop_Mflop, oop_checksum ); |
| |
| // Compute execution-time ratio of OOP to C. This is also the |
| // reciprocal of the Megaflop ratios. |
| double ratio = oop_sec/c_sec; |
| |
| // Compute the absolute and relative differences between the checksums |
| // for the two codes. |
| double diff = c_checksum - oop_checksum; |
| double min = c_checksum < oop_checksum ? c_checksum : oop_checksum; |
| double rel = diff/min; |
| |
| // If the relative difference exceeds the tolerance, print an error-message, |
| // otherwise print the statistics. |
| if( rel > Tolerance || rel < -Tolerance ) { |
| printf( "%-10s: warning: relative checksum error of %g between C (%g) and oop (%g)\n", |
| name(), rel, c_checksum, oop_checksum ); |
| } |
| printf( "%-10s %10d %5.1f %5.1f %5.1f %5.1f %5.1f\n", |
| name(), iterations, c_sec, oop_sec, c_Mflop, oop_Mflop, ratio ); |
| } |
| |
| const char * Version = "Version 1.7"; // The OOPACK version number |
| |
| void Usage( int /*argc*/, char * argv[] ) { |
| printf( "Usage:\t%s test1=iterations1 test2=iterations2 ...\n", argv[0] ); |
| printf( "E.g.:\ta.out Max=5000 Matrix=50 Complex=2000 Iterator=5000\n" ); |
| exit(1); |
| } |
| |
| int main( int argc, char * argv[] ) |
| { |
| // The available benchmarks are automatically put into the list of available benchmarks |
| // by the constructor for Benchmark. |
| |
| // Check if user does not know command-line format. |
| if( argc==1 ) { |
| Usage( argc, argv ); |
| } |
| int i; |
| for( i=1; i<argc; i++ ) { |
| if( !isalpha(argv[i][0]) ) |
| Usage( argc, argv ); |
| } |
| |
| // Print the request for results |
| printf("\n"); |
| printf("OOPACK %s\n",Version); |
| printf("\n"); |
| printf("For results on various systems and compilers, examine this Web Page:\n"); |
| printf(" http://www.kai.com/oopack/oopack.html\n"); |
| printf("\n"); |
| printf("Report your results by sending e-mail to oopack@kai.com.\n"); |
| printf("For a run to be accepted, adjust the number of iterations for each test\n"); |
| printf("so that each time reported is greater than 10 seconds.\n"); |
| printf("\n"); |
| printf("Send this output, along with:\n"); |
| printf("\n"); |
| printf(" * your\n"); |
| printf(" + name ------------------- \n"); |
| printf(" + company/institution ---- \n"); |
| printf("\n"); |
| printf(" * the compiler\n"); |
| printf(" + name ------------------- \n"); |
| printf(" + version number --------- \n"); |
| printf(" + options used ----------- \n"); |
| printf("\n"); |
| printf(" * the operating system\n"); |
| printf(" + name ------------------- \n"); |
| printf(" + version number --------- \n"); |
| printf("\n"); |
| printf(" * the machine\n"); |
| printf(" + manufacturer ----------- \n"); |
| printf(" + model number ----------- \n"); |
| printf(" + processor clock speed -- \n"); |
| printf(" + cache memory size ------ \n"); |
| printf("\n"); |
| |
| // Print header. |
| printf("%-10s %10s %11s %11s %5s\n", "", "", "Seconds ", "Mflops ", "" ); |
| printf("%-10s %10s %5s %5s %5s %5s %5s\n", |
| "Test", "Iterations", " C ", "OOP", " C ", "OOP", "Ratio" ); |
| printf("%-10s %10s %11s %11s %5s\n", "----", "----------", "-----------", "-----------", "-----" ); |
| |
| for( i=1; i<argc; i++ ) { |
| const char * test_name = strtok( argv[i], "=" ); |
| const char * rhs = strtok( NULL, "" ); |
| if( rhs==NULL ) { |
| printf("missing iteration count for test '%s'\n", test_name ); |
| } else { |
| int test_count = (int)strtol( rhs, 0, 0 ); |
| Benchmark * b = Benchmark::find( test_name ); |
| if( b==NULL ) { |
| printf("skipping non-existent test = '%s'\n", test_name ); |
| abort(); |
| } else { |
| b->time_both( test_count ); |
| } |
| } |
| } |
| |
| /* Print blank line. */ |
| printf("\nDONE!\n"); |
| |
| return 0; |
| } |