วิธีคำนวณเวลาดำเนินการของข้อมูลโค้ดใน C ++


121

ฉันต้องคำนวณเวลาดำเนินการของข้อมูลโค้ด C ++ เป็นวินาที ต้องใช้งานได้ทั้งบนเครื่อง Windows หรือ Unix

ฉันใช้รหัสรหัสต่อไปนี้เพื่อทำสิ่งนี้ (นำเข้าก่อน)

clock_t startTime = clock();
// some code here
// to compute its execution duration in runtime
cout << double( clock() - startTime ) / (double)CLOCKS_PER_SEC<< " seconds." << endl;

อย่างไรก็ตามสำหรับอินพุตขนาดเล็กหรือคำสั่งสั้น ๆ เช่น a = a + 1 ฉันจะได้ผลลัพธ์ "0 วินาที" ฉันคิดว่ามันต้องเป็น 0.0000001 วินาทีหรืออะไรประมาณนั้น

ฉันจำได้ว่าSystem.nanoTime()ใน Java ทำงานได้ดีในกรณีนี้ อย่างไรก็ตามฉันไม่สามารถรับฟังก์ชันการทำงานที่แน่นอนเหมือนกันจากclock()ฟังก์ชันของ C ++

คุณมีวิธีแก้ไขหรือไม่?


29
โปรดทราบว่าการเปรียบเทียบตามความแตกต่างของเวลาใด ๆ อาจไม่ถูกต้องเนื่องจากระบบปฏิบัติการอาจไม่สามารถรันเธรดของคุณได้ตั้งแต่ต้นจนจบ มันอาจขัดจังหวะและเรียกใช้เธรดอื่น ๆ ที่สอดประสานกับของคุณซึ่งจะมีผลกระทบอย่างมากต่อเวลาจริงที่ใช้ในการดำเนินการของคุณ คุณสามารถเรียกใช้หลายครั้งและเฉลี่ยผลลัพธ์ คุณสามารถลดจำนวนกระบวนการอื่น ๆ ที่กำลังทำงานอยู่ แต่ไม่มีสิ่งเหล่านี้จะกำจัดเอฟเฟกต์การระงับเธรดได้ทั้งหมด
มอร์ดาชัย

14
Mordachi ทำไมคุณถึงต้องการกำจัดมัน? คุณต้องการดูว่าฟังก์ชันของคุณทำงานอย่างไรในสภาพแวดล้อมโลกแห่งความเป็นจริงไม่ใช่ในดินแดนมหัศจรรย์ที่เธรดจะไม่ถูกขัดจังหวะ ตราบใดที่คุณเรียกใช้หลาย ๆ ครั้งและทำการหาค่าเฉลี่ยก็จะแม่นยำมาก
Thomas Bonini

ใช่ฉันเรียกใช้มันสองสามครั้งและเฉลี่ยผลลัพธ์
AhmetB - Google

14
Andreas ความคิดเห็นของ Mordachai มีความเกี่ยวข้องหาก OP ต้องการเปรียบเทียบประสิทธิภาพของโค้ดของเขากับอัลกอริทึมอื่น ตัวอย่างเช่นหากเขาทำการทดสอบนาฬิกาหลายครั้งในบ่ายวันนี้และทำการทดสอบอัลกอริทึมที่แตกต่างกันในเช้าวันพรุ่งนี้การเปรียบเทียบของเขาอาจไม่น่าเชื่อถือเนื่องจากเขาอาจใช้ทรัพยากรร่วมกับกระบวนการอื่น ๆ ในช่วงบ่ายมากกว่าตอนเช้า หรือบางทีโค้ดชุดเดียวจะทำให้ระบบปฏิบัติการใช้เวลาในการประมวลผลน้อยลง มีสาเหตุหลายประการที่ทำให้การวัดประสิทธิภาพประเภทนี้ไม่น่าเชื่อถือหากเขาต้องการทำการเปรียบเทียบตามเวลา
weberc2

4
@Mordachai ฉันรู้ว่าฉันกำลังตอบกลับความคิดเห็นเก่า ๆ แต่สำหรับใครก็ตามที่สะดุดกับสิ่งนี้เหมือนที่ฉันทำ - ประสิทธิภาพของอัลกอริทึมในเวลาที่คุณต้องการใช้เวลาน้อยที่สุดในการวิ่งไม่กี่ครั้งไม่ใช่ค่าเฉลี่ย นี่คือสิ่งที่มีการขัดจังหวะน้อยที่สุดโดยระบบปฏิบัติการดังนั้นเวลาส่วนใหญ่จะเป็นรหัสของคุณ
บารุค

คำตอบ:


115

คุณสามารถใช้ฟังก์ชันนี้ที่ฉันเขียน คุณโทรGetTimeMs64()และจะส่งกลับจำนวนมิลลิวินาทีที่ผ่านไปนับตั้งแต่ยุคยูนิกซ์โดยใช้นาฬิการะบบ - เช่นเดียวกับtime(NULL)ยกเว้นในมิลลิวินาที

มันทำงานได้ทั้งบน windows และ linux; ปลอดภัยด้าย

โปรดทราบว่าความละเอียดคือ 15 ms บน windows บน linux นั้นขึ้นอยู่กับการใช้งาน แต่โดยปกติจะเป็น 15 ms เช่นกัน

#ifdef _WIN32
#include <Windows.h>
#else
#include <sys/time.h>
#include <ctime>
#endif

/* Remove if already defined */
typedef long long int64; typedef unsigned long long uint64;

/* Returns the amount of milliseconds elapsed since the UNIX epoch. Works on both
 * windows and linux. */

uint64 GetTimeMs64()
{
#ifdef _WIN32
 /* Windows */
 FILETIME ft;
 LARGE_INTEGER li;

 /* Get the amount of 100 nano seconds intervals elapsed since January 1, 1601 (UTC) and copy it
  * to a LARGE_INTEGER structure. */
 GetSystemTimeAsFileTime(&ft);
 li.LowPart = ft.dwLowDateTime;
 li.HighPart = ft.dwHighDateTime;

 uint64 ret = li.QuadPart;
 ret -= 116444736000000000LL; /* Convert from file time to UNIX epoch time. */
 ret /= 10000; /* From 100 nano seconds (10^-7) to 1 millisecond (10^-3) intervals */

 return ret;
#else
 /* Linux */
 struct timeval tv;

 gettimeofday(&tv, NULL);

 uint64 ret = tv.tv_usec;
 /* Convert from micro seconds (10^-6) to milliseconds (10^-3) */
 ret /= 1000;

 /* Adds the seconds (10^0) after converting them to milliseconds (10^-3) */
 ret += (tv.tv_sec * 1000);

 return ret;
#endif
}

1
สำหรับการอ้างอิงในอนาคต: ฉันแค่โยนมันลงในไฟล์ส่วนหัวและใช้มัน ดีใจที่มีค่ะ
Daniel Handojo

1
ฉันเชื่อว่าวิธีนี้gettimeofdayสามารถให้ผลลัพธ์ที่ไม่ได้ตั้งใจหากนาฬิการะบบมีการเปลี่ยนแปลง หากสิ่งนี้เป็นปัญหาสำหรับคุณคุณอาจต้องการดูclock_gettimeแทน
Azmisov

วิธีนี้สำหรับ Windows มีข้อดีกว่าGetTickCountหรือไม่?
MicroVirus

ไม่ได้รวบรวมโดยใช้gcc -std=c99
Assimilater

@MicroVirus: ใช่GetTickCountเวลาที่ผ่านไปนับตั้งแต่ระบบเริ่มทำงานในขณะที่ฟังก์ชันของฉันส่งคืนเวลาตั้งแต่ยุค UNIX ซึ่งหมายความว่าคุณสามารถใช้สำหรับวันที่และเวลาได้ หากคุณสนใจเฉพาะเวลาที่ผ่านไประหว่างสองเหตุการณ์ของฉันยังคงเป็นทางเลือกที่ดีกว่าเพราะเป็น int64 GetTickCount เป็น int32 และล้นทุกๆ 50 วันซึ่งหมายความว่าคุณจะได้รับผลลัพธ์แปลก ๆ หากทั้งสองเหตุการณ์ที่คุณลงทะเบียนอยู่ระหว่างการล้น
Thomas Bonini

43

ฉันมีตัวอย่างการทำงานอื่นที่ใช้ไมโครวินาที (UNIX, POSIX ฯลฯ )

    #include <sys/time.h>
    typedef unsigned long long timestamp_t;

    static timestamp_t
    get_timestamp ()
    {
      struct timeval now;
      gettimeofday (&now, NULL);
      return  now.tv_usec + (timestamp_t)now.tv_sec * 1000000;
    }

    ...
    timestamp_t t0 = get_timestamp();
    // Process
    timestamp_t t1 = get_timestamp();

    double secs = (t1 - t0) / 1000000.0L;

นี่คือไฟล์ที่เราเขียนโค้ดนี้:

https://github.com/arhuaco/junkcode/blob/master/emqbit-bench/bench.c


5
คุณควรเพิ่ม#include <sys/time.h>เมื่อเริ่มต้นตัวอย่างของคุณ
niekas

40

นี่คือวิธีง่ายๆใน C ++ 11 ซึ่งให้ความละเอียดที่น่าพอใจ

#include <iostream>
#include <chrono>

class Timer
{
public:
    Timer() : beg_(clock_::now()) {}
    void reset() { beg_ = clock_::now(); }
    double elapsed() const { 
        return std::chrono::duration_cast<second_>
            (clock_::now() - beg_).count(); }

private:
    typedef std::chrono::high_resolution_clock clock_;
    typedef std::chrono::duration<double, std::ratio<1> > second_;
    std::chrono::time_point<clock_> beg_;
};

หรือบน * nix สำหรับ c ++ 03

#include <iostream>
#include <ctime>

class Timer
{
public:
    Timer() { clock_gettime(CLOCK_REALTIME, &beg_); }

    double elapsed() {
        clock_gettime(CLOCK_REALTIME, &end_);
        return end_.tv_sec - beg_.tv_sec +
            (end_.tv_nsec - beg_.tv_nsec) / 1000000000.;
    }

    void reset() { clock_gettime(CLOCK_REALTIME, &beg_); }

private:
    timespec beg_, end_;
};

นี่คือตัวอย่างการใช้งาน:

int main()
{
    Timer tmr;
    double t = tmr.elapsed();
    std::cout << t << std::endl;

    tmr.reset();
    t = tmr.elapsed();
    std::cout << t << std::endl;

    return 0;
}

จากhttps://gist.github.com/gongzhitaao/7062087


ฉันได้รับข้อผิดพลาดนี้กับโซลูชัน c ++ 11 ของคุณ:/usr/lib/x86_64-linux-gnu/libstdc++.so.6: version GLIBCXX_3.4.19 not found (required by ../cpu_2d/g500)
user9869932

@julianromera คุณใช้แพลตฟอร์มอะไร? คุณติดตั้งไลบรารี libstdc ++ และ g ++ หรือไม่
gongzhitaao

มันเป็นตาราง Slurm ของ Linux ubuntu 12 ฉันเพิ่งได้รับการแก้ไข ฉันเพิ่ม -static-libstdc ++ ที่ส่วนท้ายของตัวเชื่อมโยง ขอบคุณสำหรับการถาม @gongzhitaao
user9869932

18
#include <boost/progress.hpp>

using namespace boost;

int main (int argc, const char * argv[])
{
  progress_timer timer;

  // do stuff, preferably in a 100x loop to make it take longer.

  return 0;
}

เมื่อprogress_timerอยู่นอกขอบเขตจะพิมพ์เวลาที่ผ่านไปนับตั้งแต่สร้าง

อัปเดต : นี่คือเวอร์ชันที่ใช้งานได้โดยไม่ต้อง Boost (ทดสอบบน macOS / iOS):

#include <chrono>
#include <string>
#include <iostream>
#include <math.h>
#include <unistd.h>

class NLTimerScoped {
private:
    const std::chrono::steady_clock::time_point start;
    const std::string name;

public:
    NLTimerScoped( const std::string & name ) : name( name ), start( std::chrono::steady_clock::now() ) {
    }


    ~NLTimerScoped() {
        const auto end(std::chrono::steady_clock::now());
        const auto duration_ms = std::chrono::duration_cast<std::chrono::milliseconds>( end - start ).count();

        std::cout << name << " duration: " << duration_ms << "ms" << std::endl;
    }

};

int main(int argc, const char * argv[]) {

    {
        NLTimerScoped timer( "sin sum" );

        float a = 0.0f;

        for ( int i=0; i < 1000000; i++ ) {
            a += sin( (float) i / 100 );
        }

        std::cout << "sin sum = " << a << std::endl;
    }



    {
        NLTimerScoped timer( "sleep( 4 )" );

        sleep( 4 );
    }



    return 0;
}

2
วิธีนี้ใช้งานได้ แต่โปรดทราบว่า progress_timer เลิกใช้งานแล้ว (บางครั้งก่อนบูสต์ 1.50) - auto_cpu_timer อาจเหมาะสมกว่า
davidA

3
@meowsqueak อืม auto_cpu_timer ดูเหมือนว่าจะต้องเชื่อมโยงไลบรารีระบบ Boost ดังนั้นจึงไม่ใช่โซลูชันเฉพาะส่วนหัวอีกต่อไป แย่จัง ... ทำให้ตัวเลือกอื่น ๆ น่าสนใจขึ้นในทันที
Tomas Andrle

1
ใช่นั่นเป็นจุดที่ดีหากคุณยังไม่ได้เชื่อมต่อ Boost มันจะเป็นปัญหามากกว่าที่จะคุ้มค่า แต่ถ้าคุณทำไปแล้วมันได้ผลดีทีเดียว
davidA

@meowsqueak ใช่หรือสำหรับการทดสอบเกณฑ์มาตรฐานอย่างรวดเร็วเพียงรับ Boost เวอร์ชันเก่ากว่านั้น
Tomas Andrle

@TomasAndrle ไม่มีลิงก์อีกต่อไป
Zheng Qu

5

Windows มีฟังก์ชัน QueryPerformanceCounter () และ Unix มี gettimeofday () ฟังก์ชันทั้งสองสามารถวัดความแตกต่างอย่างน้อย 1 ไมโครวินาที


แต่การใช้ windows.h ถูก จำกัด แหล่งที่คอมไพล์เดียวกันต้องทำงานบนทั้ง Windows และ Unix จะจัดการปัญหานี้อย่างไร?
AhmetB - Google

2
จากนั้นมองหาคลังกระดาษห่อหุ้มstackoverflow.com/questions/1487695/…
Captain Comic

4
แหล่งที่มาที่คอมไพล์เดียวกันดูเหมือนว่าคุณต้องการเรียกใช้ไบนารีเดียวกันบนทั้งสองระบบซึ่งดูเหมือนจะไม่เป็นเช่นนั้น ถ้าคุณหมายถึงแหล่งเดียวกันแล้ว#ifdefจะต้องตกลง (และจะมีการตัดสินจากคำตอบที่คุณได้รับการยอมรับ) #ifdef WIN32 #include <windows.h> ... #else ... #endifแล้วฉันไม่เห็นปัญหา:
แค่ใครสักคน

3

ในบางโปรแกรมฉันเขียนว่าฉันใช้RDTSเพื่อจุดประสงค์ดังกล่าว RDTSC ไม่เกี่ยวกับเวลา แต่เกี่ยวกับจำนวนรอบจากการเริ่มทำงานของโปรเซสเซอร์ คุณต้องปรับเทียบในระบบของคุณเพื่อให้ได้ผลลัพธ์เป็นวินาที แต่มันมีประโยชน์มากเมื่อคุณต้องการประเมินประสิทธิภาพการใช้จำนวนรอบโดยตรงจะดีกว่าโดยไม่ต้องพยายามเปลี่ยนกลับเป็นวินาที

(ลิงก์ด้านบนเป็นหน้าวิกิพีเดียภาษาฝรั่งเศส แต่มีตัวอย่างโค้ด C ++ เวอร์ชันภาษาอังกฤษอยู่ที่นี่ )


2

ฉันขอแนะนำให้ใช้ฟังก์ชันไลบรารีมาตรฐานเพื่อรับข้อมูลเวลาจากระบบ

หากคุณต้องการความละเอียดที่ละเอียดกว่าให้ดำเนินการซ้ำการดำเนินการเพิ่มเติม แทนที่จะรันโปรแกรมเพียงครั้งเดียวและรับตัวอย่างให้รันโปรแกรม 1,000 ครั้งขึ้นไป


2

จะเป็นการดีกว่าที่จะเรียกใช้วงในหลาย ๆ ครั้งโดยใช้เวลาการทำงานเพียงครั้งเดียวและเฉลี่ยโดยการแบ่งการทำซ้ำวงในมากกว่าที่จะเรียกใช้สิ่งทั้งหมด (ลูป + เวลาประสิทธิภาพ) หลาย ๆ ครั้งและเฉลี่ย ซึ่งจะช่วยลดค่าใช้จ่ายของโค้ดเวลาประสิทธิภาพเทียบกับส่วนโปรไฟล์จริงของคุณ

ตัดสายจับเวลาของคุณสำหรับระบบที่เหมาะสม สำหรับ Windows QueryPerformanceCounter ค่อนข้างเร็วและ "ปลอดภัย" ที่จะใช้

คุณสามารถใช้ "rdtsc" บนพีซี X86 รุ่นใหม่ ๆ ได้เช่นกัน แต่อาจมีปัญหากับเครื่องมัลติคอร์บางรุ่น (การกระโดดหลักอาจเปลี่ยนตัวจับเวลา) หรือหากคุณเปิดใช้งาน speed-step ของการเรียงลำดับบางอย่าง


2

(โซลูชันเฉพาะของ windows) วิธีปัจจุบัน (ประมาณปี 2017) ในการกำหนดเวลาที่ถูกต้องภายใต้หน้าต่างคือการใช้ "QueryPerformanceCounter" แนวทางนี้มีประโยชน์ในการให้ผลลัพธ์ที่แม่นยำมากและแนะนำโดย MS เพียงแค่ใส่โค้ดหยดลงในแอปคอนโซลใหม่เพื่อรับตัวอย่างการทำงาน มีการสนทนาที่ยาวนานที่นี่: การได้รับการประทับเวลาความละเอียดสูง

#include <iostream>
#include <tchar.h>
#include <windows.h>

int main()
{
constexpr int MAX_ITER{ 10000 };
constexpr __int64 us_per_hour{ 3600000000ull }; // 3.6e+09
constexpr __int64 us_per_min{ 60000000ull };
constexpr __int64 us_per_sec{ 1000000ull };
constexpr __int64 us_per_ms{ 1000ull };

// easy to work with
__int64 startTick, endTick, ticksPerSecond, totalTicks = 0ull;

QueryPerformanceFrequency((LARGE_INTEGER *)&ticksPerSecond);

for (int iter = 0; iter < MAX_ITER; ++iter) {// start looping
    QueryPerformanceCounter((LARGE_INTEGER *)&startTick); // Get start tick
    // code to be timed
    std::cout << "cur_tick = " << iter << "\n";
    QueryPerformanceCounter((LARGE_INTEGER *)&endTick); // Get end tick
    totalTicks += endTick - startTick; // accumulate time taken
}

// convert to elapsed microseconds
__int64 totalMicroSeconds =  (totalTicks * 1000000ull)/ ticksPerSecond;

__int64 hours = totalMicroSeconds / us_per_hour;
totalMicroSeconds %= us_per_hour;
__int64 minutes = totalMicroSeconds / us_per_min;
totalMicroSeconds %= us_per_min;
__int64 seconds = totalMicroSeconds / us_per_sec;
totalMicroSeconds %= us_per_sec;
__int64 milliseconds = totalMicroSeconds / us_per_ms;
totalMicroSeconds %= us_per_ms;


std::cout << "Total time: " << hours << "h ";
std::cout << minutes << "m " << seconds << "s " << milliseconds << "ms ";
std::cout << totalMicroSeconds << "us\n";

return 0;
}

2

โซลูชันที่สมบูรณ์แบบสำหรับการตั้งเวลาเธรดซึ่งควรให้เวลาเท่ากันทุกครั้งในการทดสอบแต่ละครั้งคือการคอมไพล์โปรแกรมของคุณให้เป็นระบบปฏิบัติการที่เป็นอิสระและบูตเครื่องคอมพิวเตอร์ของคุณเพื่อรันโปรแกรมในสภาพแวดล้อมที่ไม่มีระบบปฏิบัติการ อย่างไรก็ตามนี่เป็นสิ่งที่ทำไม่ได้เป็นส่วนใหญ่และจะเป็นเรื่องยากที่ดีที่สุด

สิ่งทดแทนที่ดีในการใช้ OS-free คือการตั้งค่าความสัมพันธ์ของเธรดปัจจุบันเป็น 1 คอร์และลำดับความสำคัญสูงสุด ทางเลือกนี้ควรให้ผลลัพธ์ที่สม่ำเสมอเพียงพอ

นอกจากนี้คุณควรปิดการปรับให้เหมาะสมซึ่งจะรบกวนการดีบักซึ่งสำหรับ g ++ หรือ gcc หมายถึงการเพิ่ม-Ogลงในบรรทัดคำสั่งเพื่อป้องกันไม่ให้โค้ดที่ทดสอบถูกปรับให้เหมาะสม -O0ธงไม่ควรใช้เพราะมันแนะนำค่าใช้จ่ายที่ไม่จำเป็นเป็นพิเศษซึ่งจะถูกรวมอยู่ในผลเวลาจึงบิดเบือนความเร็วหมดเวลาของรหัส

ในทางตรงกันข้ามทั้งที่สมมติว่าคุณใช้-Ofast(หรืออย่างน้อยที่สุด-O3) ในการสร้างการผลิตขั้นสุดท้ายและเพิกเฉยต่อปัญหาของการกำจัดโค้ดที่ "ตาย" -Ogทำการเพิ่มประสิทธิภาพน้อยมากเมื่อเทียบกับ-Ofast; ดังนั้นจึง-Ogสามารถบิดเบือนความจริงความเร็วจริงของรหัสในผลิตภัณฑ์ขั้นสุดท้าย

นอกจากนี้การทดสอบความเร็วทั้งหมด (ในบางส่วน) ให้การเท็จ: ในผลิตภัณฑ์การผลิตขั้นสุดท้ายที่รวบรวมด้วย-Ofastแต่ละส่วนย่อย / ส่วน / ฟังก์ชันของโค้ดจะไม่แยกออกจากกัน แต่แต่ละส่วนของโค้ดจะไหลไปสู่ส่วนถัดไปอย่างต่อเนื่องดังนั้นจึงทำให้คอมไพเลอร์สามารถรวมผสานและเพิ่มประสิทธิภาพโค้ดร่วมกันได้จากทุกที่

ในขณะเดียวกันหากคุณกำลังเปรียบเทียบข้อมูลโค้ดที่ใช้งานหนักrealloc()ข้อมูลโค้ดอาจทำงานช้าลงในผลิตภัณฑ์ที่ใช้งานจริงที่มีการกระจายตัวของหน่วยความจำสูงเพียงพอ ดังนั้นนิพจน์ "ทั้งหมดมากกว่าผลรวมของชิ้นส่วน" จึงนำไปใช้กับสถานการณ์นี้เนื่องจากโค้ดในบิลด์การผลิตขั้นสุดท้ายอาจทำงานได้เร็วหรือช้ากว่าข้อมูลโค้ดแต่ละรายการที่คุณกำลังทดสอบความเร็วอย่างเห็นได้ชัด

วิธีแก้ปัญหาบางส่วนที่อาจลดความไม่ลงรอยกันกำลังใช้-Ofastสำหรับการทดสอบความเร็วด้วยการเพิ่มasm volatile("" :: "r"(var))ตัวแปรที่เกี่ยวข้องในการทดสอบเพื่อป้องกันการกำจัดรหัสตาย / ลูป

นี่คือตัวอย่างวิธีการเปรียบเทียบฟังก์ชันรากที่สองบนคอมพิวเตอร์ Windows

// set USE_ASM_TO_PREVENT_ELIMINATION  to 0 to prevent `asm volatile("" :: "r"(var))`
// set USE_ASM_TO_PREVENT_ELIMINATION  to 1 to enforce `asm volatile("" :: "r"(var))`
#define USE_ASM_TO_PREVENT_ELIMINATION 1

#include <iostream>
#include <iomanip>
#include <cstdio>
#include <chrono>
#include <cmath>
#include <windows.h>
#include <intrin.h>
#pragma intrinsic(__rdtsc)
#include <cstdint>

class Timer {
public:
    Timer() : beg_(clock_::now()) {}
    void reset() { beg_ = clock_::now(); }
    double elapsed() const { 
        return std::chrono::duration_cast<second_>
            (clock_::now() - beg_).count(); }
private:
    typedef std::chrono::high_resolution_clock clock_;
    typedef std::chrono::duration<double, std::ratio<1> > second_;
    std::chrono::time_point<clock_> beg_;
};

unsigned int guess_sqrt32(register unsigned int n) {
    register unsigned int g = 0x8000;
    if(g*g > n) {
        g ^= 0x8000;
    }
    g |= 0x4000;
    if(g*g > n) {
        g ^= 0x4000;
    }
    g |= 0x2000;
    if(g*g > n) {
        g ^= 0x2000;
    }
    g |= 0x1000;
    if(g*g > n) {
        g ^= 0x1000;
    }
    g |= 0x0800;
    if(g*g > n) {
        g ^= 0x0800;
    }
    g |= 0x0400;
    if(g*g > n) {
        g ^= 0x0400;
    }
    g |= 0x0200;
    if(g*g > n) {
        g ^= 0x0200;
    }
    g |= 0x0100;
    if(g*g > n) {
        g ^= 0x0100;
    }
    g |= 0x0080;
    if(g*g > n) {
        g ^= 0x0080;
    }
    g |= 0x0040;
    if(g*g > n) {
        g ^= 0x0040;
    }
    g |= 0x0020;
    if(g*g > n) {
        g ^= 0x0020;
    }
    g |= 0x0010;
    if(g*g > n) {
        g ^= 0x0010;
    }
    g |= 0x0008;
    if(g*g > n) {
        g ^= 0x0008;
    }
    g |= 0x0004;
    if(g*g > n) {
        g ^= 0x0004;
    }
    g |= 0x0002;
    if(g*g > n) {
        g ^= 0x0002;
    }
    g |= 0x0001;
    if(g*g > n) {
        g ^= 0x0001;
    }
    return g;
}

unsigned int empty_function( unsigned int _input ) {
    return _input;
}

unsigned long long empty_ticks=0;
double empty_seconds=0;
Timer my_time;

template<unsigned int benchmark_repetitions>
void benchmark( char* function_name, auto (*function_to_do)( auto ) ) {
    register unsigned int i=benchmark_repetitions;
    register unsigned long long start=0;
    my_time.reset();
    start=__rdtsc();
    while ( i-- ) {
        auto result = (*function_to_do)( i << 7 );
        #if USE_ASM_TO_PREVENT_ELIMINATION == 1
            asm volatile("" :: "r"(
                // There is no data type in C++ that is smaller than a char, so it will
                //  not throw a segmentation fault error to reinterpret any arbitrary
                //  data type as a char. Although, the compiler might not like it.
                result
            ));
        #endif
    }
    if ( function_name == nullptr ) {
        empty_ticks = (__rdtsc()-start);
        empty_seconds = my_time.elapsed();
        std::cout<< "Empty:\n" << empty_ticks
              << " ticks\n" << benchmark_repetitions << " repetitions\n"
               << std::setprecision(15) << empty_seconds
                << " seconds\n\n";
    } else {
        std::cout<< function_name<<":\n" << (__rdtsc()-start-empty_ticks)
              << " ticks\n" << benchmark_repetitions << " repetitions\n"
               << std::setprecision(15) << (my_time.elapsed()-empty_seconds)
                << " seconds\n\n";
    }
}


int main( void ) {
    void* Cur_Thread=   GetCurrentThread();
    void* Cur_Process=  GetCurrentProcess();
    unsigned long long  Current_Affinity;
    unsigned long long  System_Affinity;
    unsigned long long furthest_affinity;
    unsigned long long nearest_affinity;

    if( ! SetThreadPriority(Cur_Thread,THREAD_PRIORITY_TIME_CRITICAL) ) {
        SetThreadPriority( Cur_Thread, THREAD_PRIORITY_HIGHEST );
    }
    if( ! SetPriorityClass(Cur_Process,REALTIME_PRIORITY_CLASS) ) {
        SetPriorityClass( Cur_Process, HIGH_PRIORITY_CLASS );
    }
    GetProcessAffinityMask( Cur_Process, &Current_Affinity, &System_Affinity );
    furthest_affinity = 0x8000000000000000ULL>>__builtin_clzll(Current_Affinity);
    nearest_affinity  = 0x0000000000000001ULL<<__builtin_ctzll(Current_Affinity);
    SetProcessAffinityMask( Cur_Process, furthest_affinity );
    SetThreadAffinityMask( Cur_Thread, furthest_affinity );

    const int repetitions=524288;

    benchmark<repetitions>( nullptr, empty_function );
    benchmark<repetitions>( "Standard Square Root", standard_sqrt );
    benchmark<repetitions>( "Original Guess Square Root", original_guess_sqrt32 );
    benchmark<repetitions>( "New Guess Square Root", new_guess_sqrt32 );


    SetThreadPriority( Cur_Thread, THREAD_PRIORITY_IDLE );
    SetPriorityClass( Cur_Process, IDLE_PRIORITY_CLASS );
    SetProcessAffinityMask( Cur_Process, nearest_affinity );
    SetThreadAffinityMask( Cur_Thread, nearest_affinity );
    for (;;) { getchar(); }

    return 0;
}

นอกจากนี้ให้เครดิตกับ Mike Jarvis สำหรับตัวจับเวลาของเขา

โปรดทราบ (สิ่งนี้สำคัญมาก) ว่าหากคุณกำลังจะเรียกใช้ข้อมูลโค้ดที่ใหญ่ขึ้นคุณจะต้องลดจำนวนการทำซ้ำลงเพื่อป้องกันไม่ให้คอมพิวเตอร์ของคุณค้าง


2
คำตอบที่ดียกเว้นการปิดใช้งานการเพิ่มประสิทธิภาพ การเปรียบเทียบ-O0รหัสเป็นใหญ่เสียเวลาเพราะค่าใช้จ่ายของ-O0 แทนปกติ-O2หรือ-O3 -march=nativeแตกต่างกันไปอย่างดุเดือดขึ้นอยู่กับรหัสและภาระงาน เช่นพิเศษชื่อ tmp vars -O0เวลาค่าใช้จ่ายที่ มีวิธีอื่นในการหลีกเลี่ยงไม่ให้สิ่งต่างๆถูกปรับให้เหมาะสมเช่นการซ่อนสิ่งต่างๆจากเครื่องมือเพิ่มประสิทธิภาพด้วยvolatileฟังก์ชันที่ไม่ใช่แบบอินไลน์หรือคำสั่ง asm แบบอินไลน์ที่ว่างเปล่า -O0ไม่ได้ใกล้เคียงกับการใช้งานเนื่องจากโค้ดมีปัญหาคอขวดที่ต่างกัน-O0ไม่เหมือนกัน แต่แย่กว่า
Peter Cordes

1
ฮึ-Ogยังไม่ค่อยสมจริงเท่าไหร่ขึ้นอยู่กับรหัส อย่างน้อย-O2ควร-O3เป็นจริงมากกว่า ใช้asm volatile("" ::: "+r"(var))หรือบางสิ่งบางอย่างเพื่อทำให้คอมไพเลอร์เป็นรูปธรรมของค่าในรีจิสเตอร์และกำจัดการแพร่กระจายอย่างต่อเนื่องผ่านมัน
Peter Cordes

@PeterCordes ขอบคุณอีกครั้งสำหรับข้อมูลเชิงลึก ฉันมีการปรับปรุงเนื้อหาที่มีและข้อมูลโค้ดด้วย-O3 asm volatile("" ::: "+r"(var))
Jack Giffin

1
asm volatile("" ::: "+r"( i ));ดูเหมือนไม่จำเป็น ในโค้ดที่ปรับให้เหมาะสมไม่มีเหตุผลใดที่จะบังคับให้คอมไพเลอร์เป็นจริงiเช่นเดียวกับi<<7ภายในลูป คุณกำลังหยุดไม่ให้เพิ่มประสิทธิภาพtmp -= 128แทนที่จะเปลี่ยนทุกครั้ง โดยใช้ผลจากการเรียกฟังก์ชั่นเป็นสิ่งที่ดี voidแต่ถ้ามันไม่ใช่ ชอบint result = (*function_to_do)( i << 7 );. คุณสามารถใช้asmคำสั่งเกี่ยวกับผลลัพธ์นั้น
Peter Cordes

@PeterCordes ขอบคุณมากอีกครั้งหรือข้อมูลเชิงลึกของคุณ ตอนนี้โพสต์ของฉันมีการแก้ไขสำหรับค่าที่ส่งคืนจากfunction_to_doเพื่อให้function_to_doสามารถอินไลน์ได้โดยไม่ถูกตัดออก โปรดแจ้งให้เราทราบหากคุณมีข้อเสนอแนะเพิ่มเติม
Jack Giffin

1

สำหรับกรณีที่คุณต้องการใช้เวลาส่วนรหัสเดิมทุกครั้งที่เรียกใช้งาน (เช่นสำหรับรหัสการทำโปรไฟล์ที่คุณคิดว่าอาจเป็นคอขวด) นี่คือกระดาษห่อหุ้มรอบ ๆ (การปรับเปลี่ยนเล็กน้อยสำหรับ) ฟังก์ชันของ Andreas Bonini ที่ฉันคิดว่ามีประโยชน์:

#ifdef _WIN32
#include <Windows.h>
#else
#include <sys/time.h>
#endif

/*
 *  A simple timer class to see how long a piece of code takes. 
 *  Usage:
 *
 *  {
 *      static Timer timer("name");
 *
 *      ...
 *
 *      timer.start()
 *      [ The code you want timed ]
 *      timer.stop()
 *
 *      ...
 *  }
 *
 *  At the end of execution, you will get output:
 *
 *  Time for name: XXX seconds
 */
class Timer
{
public:
    Timer(std::string name, bool start_running=false) : 
        _name(name), _accum(0), _running(false)
    {
        if (start_running) start();
    }

    ~Timer() { stop(); report(); }

    void start() {
        if (!_running) {
            _start_time = GetTimeMicroseconds();
            _running = true;
        }
    }
    void stop() {
        if (_running) {
            unsigned long long stop_time = GetTimeMicroseconds();
            _accum += stop_time - _start_time;
            _running = false;
        }
    }
    void report() { 
        std::cout<<"Time for "<<_name<<": " << _accum / 1.e6 << " seconds\n"; 
    }
private:
    // cf. http://stackoverflow.com/questions/1861294/how-to-calculate-execution-time-of-a-code-snippet-in-c
    unsigned long long GetTimeMicroseconds()
    {
#ifdef _WIN32
        /* Windows */
        FILETIME ft;
        LARGE_INTEGER li;

        /* Get the amount of 100 nano seconds intervals elapsed since January 1, 1601 (UTC) and copy it
         *   * to a LARGE_INTEGER structure. */
        GetSystemTimeAsFileTime(&ft);
        li.LowPart = ft.dwLowDateTime;
        li.HighPart = ft.dwHighDateTime;

        unsigned long long ret = li.QuadPart;
        ret -= 116444736000000000LL; /* Convert from file time to UNIX epoch time. */
        ret /= 10; /* From 100 nano seconds (10^-7) to 1 microsecond (10^-6) intervals */
#else
        /* Linux */
        struct timeval tv;

        gettimeofday(&tv, NULL);

        unsigned long long ret = tv.tv_usec;
        /* Adds the seconds (10^0) after converting them to microseconds (10^-6) */
        ret += (tv.tv_sec * 1000000);
#endif
        return ret;
    }
    std::string _name;
    long long _accum;
    unsigned long long _start_time;
    bool _running;
};

1

เป็นเพียงคลาสง่ายๆที่เปรียบเทียบ codeblock:

using namespace std::chrono;

class benchmark {
  public:
  time_point<high_resolution_clock>  t0, t1;
  unsigned int *d;
  benchmark(unsigned int *res) : d(res) { 
                 t0 = high_resolution_clock::now();
  }
  ~benchmark() { t1 = high_resolution_clock::now();
                  milliseconds dur = duration_cast<milliseconds>(t1 - t0);
                  *d = dur.count();
  }
};
// simple usage 
// unsigned int t;
// { // put the code in a block
//  benchmark bench(&t);
//  // ...
//  // code to benchmark
// }
// HERE the t contains time in milliseconds

// one way to use it can be :
#define BENCH(TITLE,CODEBLOCK) \
  unsigned int __time__##__LINE__ = 0;  \
  { benchmark bench(&__time__##__LINE__); \
      CODEBLOCK \
  } \
  printf("%s took %d ms\n",(TITLE),__time__##__LINE__);


int main(void) {
  BENCH("TITLE",{
    for(int n = 0; n < testcount; n++ )
      int a = n % 3;
  });
  return 0;
}

0

boost :: ตัวจับเวลาอาจให้ความแม่นยำมากเท่าที่คุณต้องการ ไม่มีที่ไหนใกล้จะแม่นยำพอที่จะบอกคุณได้ว่าa = a+1;จะใช้เวลานานแค่ไหนแต่ฉันมีเหตุผลอะไรที่คุณจะต้องเผื่อเวลาบางอย่างที่ใช้เวลาสองสามนาโนวินาที?


มันอาศัยclock()ฟังก์ชันจากส่วนหัวมาตรฐาน C ++
Petter

0

ฉันสร้างแลมด้าที่เรียกคุณว่าฟังก์ชันเรียก N ครั้งและคืนค่าเฉลี่ยให้คุณ

double c = BENCHMARK_CNT(25, fillVectorDeque(variable));

คุณสามารถค้นหา C ++ 11 ส่วนหัวที่นี่


0

ฉันสร้างสาธารณูปโภคที่ง่ายสำหรับการวัดประสิทธิภาพการทำงานของกลุ่มของรหัสโดยใช้ high_resolution_clock ห้องสมุด Chrono ของ: https://github.com/nfergu/codetimer

สามารถบันทึกการกำหนดเวลาตามคีย์ต่างๆและสามารถแสดงมุมมองแบบรวมของการกำหนดเวลาสำหรับแต่ละคีย์ได้

การใช้งานมีดังนี้:

#include <chrono>
#include <iostream>
#include "codetimer.h"

int main () {
    auto start = std::chrono::high_resolution_clock::now();
    // some code here
    CodeTimer::record("mykey", start);
    CodeTimer::printStats();
    return 0;
}

0

คุณยังสามารถดู[cxx-rtimers][1]บน GitHub ซึ่งมีรูทีนเฉพาะส่วนหัวสำหรับรวบรวมสถิติเกี่ยวกับรันไทม์ของโค้ดบล็อกใด ๆ ที่คุณสามารถสร้างตัวแปรโลคัลได้ ตัวจับเวลาเหล่านั้นมีเวอร์ชันที่ใช้ std :: chrono บน C ++ 11 หรือตัวจับเวลาจากไลบรารี Boost หรือฟังก์ชันตัวจับเวลา POSIX มาตรฐาน ตัวจับเวลาเหล่านี้จะรายงานระยะเวลาโดยเฉลี่ยสูงสุดและต่ำสุดที่ใช้ภายในฟังก์ชันตลอดจนจำนวนครั้งที่เรียกใช้ สามารถใช้งานได้ง่ายๆดังนี้:

#include <rtimers/cxx11.hpp>

void expensiveFunction() {
    static rtimers::cxx11::DefaultTimer timer("expensive");
    auto scopedStartStop = timer.scopedStart();
    // Do something costly...
}

0

นั่นเป็นวิธีที่ฉันทำรหัสไม่มากเข้าใจง่ายเหมาะกับความต้องการของฉัน:

void bench(std::function<void()> fnBench, std::string name, size_t iterations)
{
    if (iterations == 0)
        return;
    if (fnBench == nullptr)
        return;
    std::chrono::high_resolution_clock::time_point start, end;
    if (iterations == 1)
    {
        start = std::chrono::high_resolution_clock::now();
        fnBench();
        end = std::chrono::high_resolution_clock::now();
    }
    else
    {
        start = std::chrono::high_resolution_clock::now();
        for (size_t i = 0; i < iterations; ++i)
            fnBench();
        end = std::chrono::high_resolution_clock::now();
    }
    printf
    (
        "bench(*, \"%s\", %u) = %4.6lfs\r\n",
        name.c_str(),
        iterations,
        std::chrono::duration_cast<std::chrono::duration<double>>(end - start).count()
    );
}

การใช้งาน:

bench
(
    []() -> void // function
    {
        // Put your code here
    },
    "the name of this", // name
    1000000 // iterations
);

0
#include <omp.h>

double start = omp_get_wtime();

// code 

double finish = omp_get_wtime();

double total_time = finish - start;

2
แม้ว่ารหัสนี้จะช่วยแก้ปัญหาได้รวมถึงคำอธิบายว่าทำไมจึงแก้ปัญหานี้ได้จะช่วยปรับปรุงคุณภาพของโพสต์ของคุณได้อย่างแท้จริงและอาจส่งผลให้มีการโหวตเพิ่มขึ้น จำไว้ว่าคุณกำลังตอบคำถามสำหรับผู้อ่านในอนาคตไม่ใช่แค่คนที่ถามตอนนี้ โปรดแก้ไขคำตอบของคุณเพื่อเพิ่มคำอธิบายและระบุข้อ จำกัด และสมมติฐานที่ใช้
Dharman
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.