Quick and dirty way to profile your code

后端 未结 7 517
轮回少年
轮回少年 2020-12-04 18:25

What method do you use when you want to get performance data about specific code paths?

7条回答
  •  粉色の甜心
    2020-12-04 19:16

    Note, the following is all written specifically for Windows.

    I also have a timer class that I wrote to do quick-and-dirty profiling that uses QueryPerformanceCounter() to get high-precision timings, but with a slight difference. My timer class doesn't dump the elapsed time when the Timer object falls out of scope. Instead, it accumulates the elapsed times in to an collection. I added a static member function, Dump(), which creates a table of elapsed times, sorted by timing category (specified in Timer's constructor as a string) along with some statistical analysis such as mean elapsed time, standard deviation, max and min. I also added a Clear() static member function which clears the collection & lets you start over again.

    How to use the Timer class (psudocode):

    int CInsertBuffer::Read(char* pBuf)
    {
           // TIMER NOTES: Avg Execution Time = ~1 ms
           Timer timer("BufferRead");
           :      :
           return -1;
    }
    

    Sample output :

    Timer Precision = 418.0095 ps
    
    === Item               Trials    Ttl Time  Avg Time  Mean Time StdDev    ===
        AddTrade           500       7 ms      14 us     12 us     24 us
        BufferRead         511       1:19.25   0.16 s    621 ns    2.48 s
        BufferWrite        516       511 us    991 ns    482 ns    11 us
        ImportPos Loop     1002      18.62 s   19 ms     77 us     0.51 s
        ImportPosition     2         18.75 s   9.38 s    16.17 s   13.59 s
        Insert             515       4.26 s    8 ms      5 ms      27 ms
        recv               101       18.54 s   0.18 s    2603 ns   1.63 s
    

    file Timer.inl :

    #include 
    #include "x:\utils\stlext\stringext.h"
    #include 
    #include 
    #include 
    #include 
    #include "x:\utils\stlext\algorithmext.h"
    #include 
    
        class Timer
        {
        public:
            Timer(const char* name)
            {
                label = std::safe_string(name);
                QueryPerformanceCounter(&startTime);
            }
    
            virtual ~Timer()
            {
                QueryPerformanceCounter(&stopTime);
                __int64 clocks = stopTime.QuadPart-startTime.QuadPart;
                double elapsed = (double)clocks/(double)TimerFreq();
                TimeMap().insert(std::make_pair(label,elapsed));
            };
    
            static std::string Dump(bool ClipboardAlso=true)
            {
                static const std::string loc = "Timer::Dump";
    
                if( TimeMap().empty() )
                {
                    return "No trials\r\n";
                }
    
                std::string ret = std::formatstr("\r\n\r\nTimer Precision = %s\r\n\r\n", format_elapsed(1.0/(double)TimerFreq()).c_str());
    
                // get a list of keys
                typedef std::set keyset;
                keyset keys;
                std::transform(TimeMap().begin(), TimeMap().end(), std::inserter(keys, keys.begin()), extract_key());
    
                size_t maxrows = 0;
    
                typedef std::vector strings;
                strings lines;
    
                static const size_t tabWidth = 9;
    
                std::string head = std::formatstr("=== %-*.*s %-*.*s %-*.*s %-*.*s %-*.*s %-*.*s ===", tabWidth*2, tabWidth*2, "Item", tabWidth, tabWidth, "Trials", tabWidth, tabWidth, "Ttl Time", tabWidth, tabWidth, "Avg Time", tabWidth, tabWidth, "Mean Time", tabWidth, tabWidth, "StdDev");
                ret += std::formatstr("\r\n%s\r\n", head.c_str());
                if( ClipboardAlso ) 
                    lines.push_back("Item\tTrials\tTtl Time\tAvg Time\tMean Time\tStdDev\r\n");
                // dump the values for each key
                {for( keyset::iterator key = keys.begin(); keys.end() != key; ++key )
                {
                    time_type ttl = 0;
                    ttl = std::accumulate(TimeMap().begin(), TimeMap().end(), ttl, accum_key(*key));
                    size_t num = std::count_if( TimeMap().begin(), TimeMap().end(), match_key(*key));
                    if( num > maxrows ) 
                        maxrows = num;
                    time_type avg = ttl / num;
    
                    // compute mean
                    std::vector sortedTimes;
                    std::transform_if(TimeMap().begin(), TimeMap().end(), std::inserter(sortedTimes, sortedTimes.begin()), extract_val(), match_key(*key));
                    std::sort(sortedTimes.begin(), sortedTimes.end());
                    size_t mid = (size_t)floor((double)num/2.0);
                    double mean = ( num > 1 && (num % 2) != 0 ) ? (sortedTimes[mid]+sortedTimes[mid+1])/2.0 : sortedTimes[mid];
                    // compute variance
                    double sum = 0.0;
                    if( num > 1 )
                    {
                        for( std::vector::iterator timeIt = sortedTimes.begin(); sortedTimes.end() != timeIt; ++timeIt )
                            sum += pow(*timeIt-mean,2.0);
                    }
                    // compute std dev
                    double stddev = num > 1 ? sqrt(sum/((double)num-1.0)) : 0.0;
    
                    ret += std::formatstr("    %-*.*s %-*.*s %-*.*s %-*.*s %-*.*s %-*.*s\r\n", tabWidth*2, tabWidth*2, key->c_str(), tabWidth, tabWidth, std::formatstr("%d",num).c_str(), tabWidth, tabWidth, format_elapsed(ttl).c_str(), tabWidth, tabWidth, format_elapsed(avg).c_str(), tabWidth, tabWidth, format_elapsed(mean).c_str(), tabWidth, tabWidth, format_elapsed(stddev).c_str()); 
                    if( ClipboardAlso )
                        lines.push_back(std::formatstr("%s\t%s\t%s\t%s\t%s\t%s\r\n", key->c_str(), std::formatstr("%d",num).c_str(), format_elapsed(ttl).c_str(), format_elapsed(avg).c_str(), format_elapsed(mean).c_str(), format_elapsed(stddev).c_str())); 
    
                }
                }
                ret += std::formatstr("%s\r\n", std::string(head.length(),'=').c_str());
    
                if( ClipboardAlso )
                {
                    // dump header row of data block
                    lines.push_back("");
                    {
                        std::string s;
                        for( keyset::iterator key = keys.begin(); key != keys.end(); ++key )
                        {
                            if( key != keys.begin() )
                                s.append("\t");
                            s.append(*key);
                        }
                        s.append("\r\n");
                        lines.push_back(s);
                    }
    
                    // blow out the flat map of time values to a seperate vector of times for each key
                    typedef std::map > nodematrix;
                    nodematrix nodes;
                    for( Times::iterator time = TimeMap().begin(); time != TimeMap().end(); ++time )
                        nodes[time->first].push_back(time->second);
    
                    // dump each data point
                    for( size_t row = 0; row < maxrows; ++row )
                    {
                        std::string rowDump;
                        for( keyset::iterator key = keys.begin(); key != keys.end(); ++key )
                        {
                            if( key != keys.begin() )
                                rowDump.append("\t");
                            if( nodes[*key].size() > row )
                                rowDump.append(std::formatstr("%f", nodes[*key][row]));
                        }
                        rowDump.append("\r\n");
                        lines.push_back(rowDump);
                    }
    
                    // dump to the clipboard
                    std::string dump;
                    for( strings::iterator s = lines.begin(); s != lines.end(); ++s )
                    {
                        dump.append(*s);
                    }
    
                    OpenClipboard(0);
                    EmptyClipboard();
                    HGLOBAL hg = GlobalAlloc(GMEM_MOVEABLE, dump.length()+1);
                    if( hg != 0 )
                    {
                        char* buf = (char*)GlobalLock(hg);
                        if( buf != 0 )
                        {
                            std::copy(dump.begin(), dump.end(), buf);
                            buf[dump.length()] = 0;
                            GlobalUnlock(hg);
                            SetClipboardData(CF_TEXT, hg);
                        }
                    }
                    CloseClipboard();
                }
    
                return ret;
            }
    
            static void Reset()
            {
                TimeMap().clear();
            }
    
            static std::string format_elapsed(double d) 
            {
                if( d < 0.00000001 )
                {
                    // show in ps with 4 digits
                    return std::formatstr("%0.4f ps", d * 1000000000000.0);
                }
                if( d < 0.00001 )
                {
                    // show in ns
                    return std::formatstr("%0.0f ns", d * 1000000000.0);
                }
                if( d < 0.001 )
                {
                    // show in us
                    return std::formatstr("%0.0f us", d * 1000000.0);
                }
                if( d < 0.1 )
                {
                    // show in ms
                    return std::formatstr("%0.0f ms", d * 1000.0);
                }
                if( d <= 60.0 )
                {
                    // show in seconds
                    return std::formatstr("%0.2f s", d);
                }
                if( d < 3600.0 )
                {
                    // show in min:sec
                    return std::formatstr("%01.0f:%02.2f", floor(d/60.0), fmod(d,60.0));
                }
                // show in h:min:sec
                return std::formatstr("%01.0f:%02.0f:%02.2f", floor(d/3600.0), floor(fmod(d,3600.0)/60.0), fmod(d,60.0));
            }
    
        private:
            static __int64 TimerFreq()
            {
                static __int64 freq = 0;
                static bool init = false;
                if( !init )
                {
                    LARGE_INTEGER li;
                    QueryPerformanceFrequency(&li);
                    freq = li.QuadPart;
                    init = true;
                }
                return freq;
            }
            LARGE_INTEGER startTime, stopTime;
            std::string label;
    
            typedef std::string key_type;
            typedef double time_type;
            typedef std::multimap Times;
    //      static Times times;
            static Times& TimeMap()
            {
                static Times times_;
                return times_;
            }
    
            struct extract_key : public std::unary_function
            {
                std::string operator()(Times::value_type const & r) const
                {
                    return r.first;
                }
            };
    
            struct extract_val : public std::unary_function
            {
                time_type operator()(Times::value_type const & r) const
                {
                    return r.second;
                }
            };
            struct match_key : public std::unary_function
            {
                match_key(key_type const & key_) : key(key_) {};
                bool operator()(Times::value_type const & rhs) const
                {
                    return key == rhs.first;
                }
            private:
                match_key& operator=(match_key&) { return * this; }
                const key_type key;
            };
    
            struct accum_key : public std::binary_function
            {
                accum_key(key_type const & key_) : key(key_), n(0) {};
                time_type operator()(time_type const & v, Times::value_type const & rhs) const
                {
                    if( key == rhs.first )
                    {
                        ++n;
                        return rhs.second + v;
                    }
                    return v;
                }
            private:
                accum_key& operator=(accum_key&) { return * this; }
                const Times::key_type key;
                mutable size_t n;
            };
        };
    

    file stringext.h (provides formatstr() function):

    namespace std
    {
        /*  ---
    
        Formatted Print
    
            template
            int strprintf(basic_string* pString, const C* pFmt, ...);
    
            template
            int vstrprintf(basic_string* pString, const C* pFmt, va_list args);
    
        Returns :
    
            # characters printed to output
    
    
        Effects :
    
            Writes formatted data to a string.  strprintf() works exactly the same as sprintf(); see your
            documentation for sprintf() for details of peration.  vstrprintf() also works the same as sprintf(), 
            but instead of accepting a variable paramater list it accepts a va_list argument.
    
        Requires :
    
            pString is a pointer to a basic_string<>
    
        --- */
    
        template int vprintf_generic(char_type* buffer, size_t bufferSize, const char_type* format, va_list argptr);
    
        template<> inline int vprintf_generic(char* buffer, size_t bufferSize, const char* format, va_list argptr)
        {
    #       ifdef SECURE_VSPRINTF
            return _vsnprintf_s(buffer, bufferSize-1, _TRUNCATE, format, argptr);
    #       else
            return _vsnprintf(buffer, bufferSize-1, format, argptr);
    #       endif
        }
    
        template<> inline int vprintf_generic(wchar_t* buffer, size_t bufferSize, const wchar_t* format, va_list argptr)
        {
    #       ifdef SECURE_VSPRINTF
            return _vsnwprintf_s(buffer, bufferSize-1, _TRUNCATE, format, argptr);
    #       else
            return _vsnwprintf(buffer, bufferSize-1, format, argptr);
    #       endif
        }
    
        template
        inline int vstringprintf(basic_string & outStr, const Type* format, va_list args)
        {
            // prologue
            static const size_t ChunkSize = 1024;
            size_t curBufSize = 0;
            outStr.erase(); 
    
            if( !format )
            {
                return 0;
            }
    
            // keep trying to write the string to an ever-increasing buffer until
            // either we get the string written or we run out of memory
            while( bool cont = true )
            {
                // allocate a local buffer
                curBufSize += ChunkSize;
                std::ref_ptr localBuffer = new Type[curBufSize];
                if( localBuffer.get() == 0 )
                {
                    // we ran out of memory -- nice goin'!
                    return -1;
                }
                // format output to local buffer
                int i = vprintf_generic(localBuffer.get(), curBufSize * sizeof(Type), format, args);
                if( -1 == i )
                {
                    // the buffer wasn't big enough -- try again
                    continue;
                }
                else if( i < 0 )
                {
                    // something wierd happened -- bail
                    return i;
                }
                // if we get to this point the string was written completely -- stop looping
                outStr.assign(localBuffer.get(),i);
                return i;
            }
            // unreachable code
            return -1;
        };
    
        // provided for backward-compatibility
        template
        inline int vstrprintf(basic_string * outStr, const Type* format, va_list args)
        {
            return vstringprintf(*outStr, format, args);
        }
    
        template
        inline int stringprintf(std::basic_string & outString, const Char* format, ...)
        {
            va_list args;
            va_start(args, format);
            int retval = vstringprintf(outString, format, args);
            va_end(args);
            return retval;
        }
    
        // old function provided for backward-compatibility
        template
        inline int strprintf(std::basic_string * outString, const Char* format, ...)
        {
            va_list args;
            va_start(args, format);
            int retval = vstringprintf(*outString, format, args);
            va_end(args);
            return retval;
        }
    
        /*  ---
    
        Inline Formatted Print
    
            string strprintf(const char* Format, ...);
    
        Returns :
    
            Formatted string
    
    
        Effects :
    
            Writes formatted data to a string.  formatstr() works the same as sprintf(); see your
            documentation for sprintf() for details of operation.  
    
        --- */
    
        template
        inline std::basic_string formatstr(const Char * format, ...)
        {
            std::string outString;
    
            va_list args;
            va_start(args, format);
            vstringprintf(outString, format, args);
            va_end(args);
            return outString;
        }
    };
    

    File algorithmext.h (provides transform_if() function) :

    /*  ---
    
    Transform
    25.2.3
    
        template
            OutputIterator transform_if(InputIterator first, InputIterator last, OutputIterator result, UnaryOperation op, Predicate pred)
    
        template
            OutputIterator transform_if(InputIterator first, InputIterator last, OutputIterator result, BinaryOperation binary_op, Predicate pred)
    
    Requires:   
    
        T is of type EqualityComparable (20.1.1) 
        op and binary_op have no side effects
    
    Effects :
    
        Assigns through every iterator i in the range [result, result + (last1-first1)) a new corresponding value equal to one of:
            1:  op( *(first1 + (i - result)) 
            2:  binary_op( *(first1 + (i - result), *(first2 + (i - result))
    
    Returns :
    
        result + (last1 - first1)
    
    Complexity :
    
        At most last1 - first1 applications of op or binary_op
    
    --- */
    
    template
    OutputIterator transform_if(InputIterator first, 
                                InputIterator last, 
                                OutputIterator result, 
                                UnaryFunction f, 
                                Predicate pred)
    {
        for (; first != last; ++first)
        {
            if( pred(*first) )
                *result++ = f(*first);
        }
        return result; 
    }
    
    template
    OutputIterator transform_if(InputIterator1 first1, 
                                InputIterator1 last1, 
                                InputIterator2 first2, 
                                OutputIterator result, 
                                BinaryOperation binary_op, 
                                Predicate pred)
    {
        for (; first1 != last1 ; ++first1, ++first2)
        {
            if( pred(*first1) )
                *result++ = binary_op(*first1,*first2);
        }
        return result;
    }
    

提交回复
热议问题