ความแตกต่างระหว่าง std :: system_clock และ std :: steady_clock?


98

อะไรคือความแตกต่างระหว่างstd::system_clockและstd::steady_clock? (กรณีตัวอย่างที่แสดงผลลัพธ์ / พฤติกรรมที่แตกต่างกันจะดีมาก)

ถ้าเป้าหมายของฉันคือการได้อย่างแม่นยำวัดเวลาการดำเนินการของฟังก์ชั่น (เช่นมาตรฐาน) สิ่งที่จะเป็นทางเลือกที่ดีที่สุดระหว่างstd::system_clock, std::steady_clockและstd::high_resolution_clock?


9
ในการเริ่มต้น system_clock อาจไม่คงที่
James McNellis

12
@CharlesSalvia ฉันไม่สามารถพูดสำหรับแพลตฟอร์มอื่น ๆ ได้ แต่system_clockไม่คงที่บน Windows ใน Windows เวลาของระบบอาจเปลี่ยนแปลงเป็นค่าใดก็ได้โดยผู้ใช้ที่มีสิทธิพิเศษเพียงพอ นอกจากนี้บริการซิงโครไนซ์เวลาอาจปรับเวลาของระบบย้อนหลังหากจำเป็น ฉันคาดว่าแพลตฟอร์มอื่น ๆ ส่วนใหญ่จะมีคุณสมบัติที่คล้ายกันซึ่งอนุญาตให้ปรับเวลาของระบบได้
James McNellis

3
@ ชาร์ลส์: กล่อง POSIX ส่วนใหญ่ที่ฉันรู้จักได้รับผลกระทบในทำนองเดียวกันและจะมีการเปลี่ยนแปลงเวลาหากผู้ใช้เปลี่ยนเวลา
Billy ONeal

5
วิดีโอตอบคำถามนี้: youtube.com/watch?v=P32hvk8b13M&t=48m44s
Howard Hinnant

1
@CharlesSalvia. จากประสบการณ์ของฉันเองในการวิเคราะห์เอาท์พุตเวลาจากระบบเก็บข้อมูลพีซีหลายสิบเครื่องเวลาจากคอมพิวเตอร์ไม่คงที่ Linux, Windows และการเรียกระบบเฉพาะที่ใช้ไม่เป็นที่รู้จัก แต่ความเหมือนกันคือความแตกต่างของเวลาเชิงลบที่พบบ่อยระหว่างค่าเวลาที่ตามมา เวลาเส้นตรงไม่ใช่บรรทัดฐาน
Tyson Hilmer

คำตอบ:


74

จาก N3376:

20.11.7.1 [time.clock.system] / 1:

วัตถุของคลาสsystem_clockแสดงเวลานาฬิกาแขวนจากนาฬิกาเรียลไทม์ทั้งระบบ

20.11.7.2 [time.clock.steady] / 1:

ออบเจ็กต์ของคลาสsteady_clockแสดงถึงนาฬิกาที่ค่าtime_pointไม่เคยลดลงเมื่อเวลาทางกายภาพก้าวหน้าขึ้นและค่าใดtime_pointล่วงหน้าในอัตราคงที่เมื่อเทียบกับเวลาจริง นั่นคืออาจไม่มีการปรับนาฬิกา

20.11.7.3 [time.clock.hires] / 1:

ออบเจ็กต์ของคลาสhigh_resolution_clockเป็นตัวแทนของนาฬิกาที่มีช่วงเวลาขีดสั้นที่สุด high_resolution_clockอาจจะเป็นคำพ้องหาหรือsystem_clocksteady_clock

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

อีกวิธีหนึ่งในการคิดเกี่ยวกับ "คงที่" ในกรณีนี้อยู่ในข้อกำหนดที่กำหนดไว้ในตาราง 20.11.3 [time.clock.req] / 2:

ในตารางที่ 59 C1และC2แสดงประเภทนาฬิกา t1และt2จะมีค่าส่งกลับโดยC1::now()ที่โทรกลับมาt1เกิดขึ้นก่อนที่จะเรียกกลับมาและทั้งสองของสายเหล่านี้เกิดขึ้นก่อนt2 C1::time_point::max()[หมายเหตุ: วิธีนี้C1ไม่ได้ล้อมรอบระหว่างt1และt2และ- ส่งหมายเหตุ]

นิพจน์: C1::is_steady
การส่งคืน: const bool
ความหมายในการดำเนินงาน: trueถ้าเป็นจริงเสมอและเวลาระหว่างนาฬิกาเห็บเป็นค่าคงที่มิฉะนั้นt1 <= t2false

นั่นคือมาตรฐานทั้งหมดที่มีต่อความแตกต่าง

หากคุณต้องการทำการเปรียบเทียบทางออกที่ดีที่สุดของคุณน่าจะเป็นstd::high_resolution_clockเพราะเป็นไปได้ว่าแพลตฟอร์มของคุณใช้ตัวจับเวลาความละเอียดสูง (เช่นQueryPerformanceCounterบน Windows) สำหรับนาฬิกานี้ อย่างไรก็ตามหากคุณกำลังเปรียบเทียบคุณควรพิจารณาใช้ตัวจับเวลาเฉพาะแพลตฟอร์มสำหรับการเปรียบเทียบของคุณเนื่องจากแพลตฟอร์มต่างๆจัดการสิ่งนี้แตกต่างกัน ตัวอย่างเช่นบางแพลตฟอร์มอาจให้วิธีการบางอย่างในการกำหนดจำนวนนาฬิกาจริงที่โปรแกรมต้องการ (เป็นอิสระจากกระบวนการอื่น ๆ ที่ทำงานบน CPU เดียวกัน) ยังดีกว่าให้รับมือกับผู้สร้างโปรไฟล์จริงและใช้สิ่งนั้น


1
@ ชาร์ลส์: โปรดชี้ให้เห็นในมาตรฐานที่เป็นกรณี? ดูเหมือนจะบ่งบอกตรงกันข้ามอย่างชัดเจน
Billy ONeal

9
@ ชาร์ลส์: นอกจากนี้เวลา POSIX ยังไม่ "คงที่" - หากผู้ใช้เปลี่ยนการตั้งค่าเวลาบนคอมพิวเตอร์เวลา POSIX จะเปลี่ยนไป หากคุณกำลังปรุงไข่และต้องการตัวจับเวลาที่ใช้เวลา 4 นาทีคุณจะต้องใช้เวลา 4 นาทีแม้ว่าเวลาปัจจุบันจะเปลี่ยนไป หากคุณมีตัวจับเวลาสำหรับการประชุมในวันที่ 5 เวลา 3 คุณต้องใช้ตัวจับเวลานั้นอย่างแน่นอนหากเวลาท้องถิ่นเปลี่ยนไป ดังนั้นความแตกต่างระหว่างsteady_clockและsystem_clockที่นี่
Billy ONeal

1
@ 5gon: ไม่มีอะไรต้องsystem_clockเป็น UTC
Billy ONeal

1
@CharlesSalvia โปรดทราบว่าเนื่องจากเวลา POSIX เชื่อมโยงกับ UTC และ UTC มีเวลาอธิกสุรทิน (cf. en.wikipedia.org/wiki/Unix_time#Leap_seconds ) นั่นหมายความว่าแม้ว่าจะไม่มีการปรับเวลาบนเครื่อง แต่เวลา C / POSIX อาจไม่ใช่แบบโมโนโทนิค
Michael Schlottke-Lakemper

3
UPDATE (Visual Studio 2015) การปรับใช้ steady_clock ได้เปลี่ยนไปแล้ว [..... ] ขณะนี้ steady_clock ใช้ QueryPerformanceCounter () และ high_resolution_clock เป็น typedef สำหรับ steady_clock อ้างจากmsdn.microsoft.com/en-us/library/hh874757.aspx
felix-b

47

Billy ให้คำตอบที่ยอดเยี่ยมตามมาตรฐาน ISO C ++ ที่ฉันเห็นด้วยอย่างเต็มที่ อย่างไรก็ตามยังมีอีกด้านหนึ่งของเรื่องราวนั่นคือชีวิตจริง ดูเหมือนว่าตอนนี้ไม่มีความแตกต่างระหว่างนาฬิกาเหล่านี้ในการใช้งานคอมไพเลอร์ยอดนิยม:

gcc 4.8:

#ifdef _GLIBCXX_USE_CLOCK_MONOTONIC
   ...
#else
  typedef system_clock steady_clock;
#endif
  typedef system_clock high_resolution_clock;

Visual Studio 2012:

class steady_clock : public system_clock
{   // wraps monotonic clock
public:
  static const bool is_monotonic = true;    // retained
  static const bool is_steady = true;
};

typedef system_clock high_resolution_clock;

ในกรณีของ gcc คุณสามารถตรวจสอบว่าคุณจัดการกับนาฬิกาที่นิ่งเพียงแค่ตรวจสอบ is_steadyและปฏิบัติตาม อย่างไรก็ตาม VS2012 ดูเหมือนจะโกงเล็กน้อยที่นี่ :-)

หากคุณต้องการนาฬิกาที่มีความแม่นยำสูงฉันขอแนะนำให้เขียนนาฬิกาของคุณเองที่สอดคล้องกับอินเทอร์เฟซนาฬิกาอย่างเป็นทางการของ C ++ 11 และรอการใช้งานให้ทัน จะเป็นแนวทางที่ดีกว่าการใช้ OS เฉพาะ API ในโค้ดของคุณโดยตรง สำหรับ Windows คุณสามารถทำได้ดังนี้:

// Self-made Windows QueryPerformanceCounter based C++11 API compatible clock
struct qpc_clock {
  typedef std::chrono::nanoseconds                       duration;      // nanoseconds resolution
  typedef duration::rep                                  rep;
  typedef duration::period                               period;
  typedef std::chrono::time_point<qpc_clock, duration>   time_point;
  static bool is_steady;                                                // = true
  static time_point now()
  {
    if(!is_inited) {
      init();
      is_inited = true;
    }
    LARGE_INTEGER counter;
    QueryPerformanceCounter(&counter);
    return time_point(duration(static_cast<rep>((double)counter.QuadPart / frequency.QuadPart *
                                                period::den / period::num)));
  }

private:
  static bool is_inited;                                                // = false
  static LARGE_INTEGER frequency;
  static void init()
  {
    if(QueryPerformanceFrequency(&frequency) == 0)
      throw std::logic_error("QueryPerformanceCounter not supported: " + std::to_string(GetLastError()));
  }
};

สำหรับลินุกซ์นั้นง่ายยิ่งขึ้น เพียงแค่อ่านหน้าคนของclock_gettimeและแก้ไขโค้ดด้านบน


19
การใช้งาน VC ++ 2012 ได้รับการยอมรับว่าเป็นข้อบกพร่องโดยผู้ดูแลไลบรารีมาตรฐานของ MS
ildjarn


1
Boost ใช้ QueryPerformanceCounter ดังนั้นการใช้ boost :: chrono จึงเป็นวิธีแก้ปัญหาที่ดีสำหรับข้อบกพร่องนี้จนกว่า Visual Studio 14 จะเปิดตัว
Mohamed El-Nakib

และนี่คือการเรียก POSIX ที่ส่งต่อไปยัง GCC 5.3.0: stackoverflow.com/a/36700301/895245
Ciro Santilli 郝海东冠状病六四事件

19

การใช้งาน GCC 5.3.0

C ++ stdlib อยู่ในแหล่ง GCC:

  • high_resolution_clock เป็นนามแฝงสำหรับ system_clock
  • system_clock ส่งต่อไปยังรายการแรกต่อไปนี้ที่มีให้:
    • clock_gettime(CLOCK_REALTIME, ...)
    • gettimeofday
    • time
  • steady_clock ส่งต่อไปยังรายการแรกต่อไปนี้ที่มีให้:
    • clock_gettime(CLOCK_MONOTONIC, ...)
    • system_clock

แล้วCLOCK_REALTIMEเทียบกับCLOCK_MONOTONICจะมีการอธิบายที่: ความแตกต่างระหว่าง CLOCK_REALTIME และ CLOCK_MONOTONIC?


3

บางทีความแตกต่างที่สำคัญที่สุดคือความจริงที่ว่าจุดเริ่มต้นstd::chrono:system_clockคือ 1.1.1970 หรือที่เรียกว่า UNIX-epoch ในอีกด้านหนึ่งสำหรับstd::chrono::steady_clockเวลาบูตเครื่องพีซีของคุณโดยทั่วไปและเหมาะที่สุดสำหรับการวัดช่วงเวลา


2

การพูดคุยที่เกี่ยวข้องเกี่ยวกับโครโนโดย Howard Hinnantผู้เขียนchrono :

อย่าใช้high_resolution_clockเนื่องจากเป็นนามแฝงสำหรับหนึ่งในนั้น:

  • system_clock: เหมือนนาฬิกาทั่วไปใช้สำหรับเวลา / วันที่ที่เกี่ยวข้องกับสิ่งต่างๆ
  • steady_clock: มันเหมือนกับนาฬิกาจับเวลาใช้สำหรับจับเวลา
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.