#include <stdio.h>

#include <stdlib.h>

#include <string.h>


typedef unsigned long long      i64u;


// Benchmark ----------------------------------------------------------------


#ifdef _MSC_VER

    #include <intrin.h>

#else

    #define CHAR_BIT 8

    inline unsigned long long __rdtsc()

    {

        i64u tick;

        __asm volatile ( ".byte 0x0f, 0x31" : "=A" ( tick ) );

        return tick;

    }

#endif


#define _min( a, b )            ( (a) < (b)? (a): (b) )

#define _max( a, b )            ( (a) > (b)? (a): (b) )

#define update_min( a, b )      ( (a) = _min( (a), (b) ) )

#define update_max( a, b )      ( (a) = _max( (a), (b) ) )


//---------------------------------------------------------------------------


class                           SOME_RUNNER

{

private:

    const   char*               function_name;

    i64u                        elapsed;


protected:

    i64u                        sum;


public:

                                SOME_RUNNER( const char* function_name )

        : function_name( function_name ) { init(); }

    virtual                    ~SOME_RUNNER() {};

    virtual void                init()

    {

        sum = 0;

        elapsed = -1;

    }

    virtual void                run() = 0;

    void                        check()

    {

        i64u    begin    = __rdtsc();

        run();

        i64u    duration = __rdtsc() - begin;

        update_min( elapsed, duration );

    }

    const   char*               set_length( const char* str, const size_t size )

    {

        static  char    temp[80];

        size_t          index;

        for( index = 0; str[index]; ++index ) temp[index] = str[index];

        for( ; index < size; ++index ) temp[index] = ' ';

        temp[index] = 0;

        return  temp;

    }

    const   char*               report()

    {

        static  char temp[80];

        sprintf( temp, "[llu] %s llu clocks", sum,

            set_length( function_name, 20 ), elapsed );

        return  temp;

    }

    const   i64u                elapsed_clocks(){ return elapsed; }

    const   char*               name(){ return function_name; }

};

//---------------------------------------------------------------------------


#include <vector>

template < size_t LOOP_COUNT >

class                           BENCH

{

private:

    std::vector< SOME_RUNNER* > runner_list;


public:

    /*con*/                     BENCH() {};

    /*des*/                    ~BENCH() {};


    void                        add( SOME_RUNNER* runner )

    {

        runner_list.push_back( runner );

    }


    void                        run()

    {

        printf( "< %d bits > %d trials", sizeof( size_t ) * CHAR_BIT, LOOP_COUNT );

        puts( "" );

        puts( "-------------------------------------------------------------" );

        puts( "|  CHECKER      | Function  name      |   minimum  clocks   |" );

        puts( "-------------------------------------------------------------" );

        i64u    min = -1;

        i64u    max = 0;

        size_t  min_index = 0;

        size_t  max_index = 0;

        for( size_t test_case = 0; test_case < runner_list.size(); ++test_case )

        {

            runner_list[test_case]->init();

            for( size_t test_count = 0; test_count < LOOP_COUNT; ++ test_count )

                runner_list[test_case]->check();

            puts( runner_list[test_case]->report() );

            if( min > runner_list[test_case]->elapsed_clocks() )

            {

                min_index = test_case;

                min = runner_list[test_case]->elapsed_clocks();

            }

            if( max < runner_list[test_case]->elapsed_clocks() )

            {

                max_index = test_case;

                max = runner_list[test_case]->elapsed_clocks();

            }

        }

        puts( "-------------------------------------------------------------" );

        printf( "Winner is %s  ( %.2f times faster than looser )",

            runner_list[min_index]->name(),

            float( runner_list[max_index]->elapsed_clocks() ) /

            runner_list[min_index]->elapsed_clocks() );

        puts( "" );

    }

};

//---------------------------------------------------------------------------


#define DEFINE_FUNCTION(return_type, function, ...)

    (SOME_RUNNER*)new RUNNER<return_type>( std::bind(function, __VA_ARGS__), #function )


//---------------------------------------------------------------------------


#include <functional>

template< typename RET_TYPE >

class                           RUNNER  : public    SOME_RUNNER

{

public:

    /*con*/                     RUNNER( std::function< RET_TYPE() > function,

        const char* function_name )

        : test( function ), SOME_RUNNER( function_name ) {}

    void                        run()

    {

        sum += (i64u)test();

    }

    std::function< RET_TYPE() > test;

};


template< >

class                           RUNNER< void > : public    SOME_RUNNER

{

public:

    /*con*/                     RUNNER( std::function< void() > function,

        const char* function_name )

        : test( function ), SOME_RUNNER( function_name ) {}

    void                        run()

    {

        sum++;

        test();

    }

    std::function< void() >     test;

};

//---------------------------------------------------------------------------

// 수정해서 사용해야 하는 부분에 진하게 표시해 둡니다.


size_t  strlen_boost( const char* s );

size_t  strlen_hybrid( const char* s );


int main()

{

    #define     STRING_LENGTH           1000000

    char*       test_string = new char[STRING_LENGTH + 1];

    for( int i = 0; i < STRING_LENGTH; ++i )

    {

        test_string[i] = rand() % 127 + 1;

//        test_string[i] = rand() % 255 + 1;

    }

    test_string[STRING_LENGTH] = 0;


    BENCH< 1000 > bench;

    bench.add( DEFINE_FUNCTION( size_t, strlen,           test_string ) );

    bench.add( DEFINE_FUNCTION( size_t, strlen_boost,     test_string ) );

    bench.add( DEFINE_FUNCTION( size_t, strlen_hybrid,    test_string ) );

    bench.run();


    getchar();

    return 0;

}

//---------------------------------------------------------------------------