簡體   English   中英

標准C ++ 11是否保證high_resolution_clock能夠測量實時(非CPU周期)?

[英]Does standard C++11 guarantee that high_resolution_clock measure real time (non CPU-cycles)?

由於已知的clock()可能顯示小於或大於實時值 - 兩種情況都顯示在以下示例1和2中。

為了在C ++ 11中高精度測量時間,我們可以使用:

  • std::chrono::high_resolution_clock::now(); - 保證高精度
  • std::chrono::steady_clock::now(); - 保證實時測量
  • clock(); - 保證高精度,但測量CPU周期而不是時間
  • time(&t_start); - 不是高精度,而是實時測量

1-例如: http//ideone.com/SudWTM

#include <stdio.h>
#include <time.h>
#include <thread>
#include <iostream>
#include <chrono>

int main(void) {

    std::cout << "sleep(3) took: \n\n";

    clock_t c_start, c_end;
    time_t t_start, t_end;
    std::chrono::high_resolution_clock::time_point h_start, h_end;
    std::chrono::steady_clock::time_point steady_start, steady_end;

    time(&t_start);  // less precise than clock() but always get the real actual time
    c_start = clock(); // clock() get only CPU-time, it can be more than real or less - sleep(3); took 0.00 seconds 
    h_start = std::chrono::high_resolution_clock::now();
    steady_start = std::chrono::steady_clock::now(); 

    std::this_thread::sleep_for(std::chrono::seconds(3));

    steady_end = std::chrono::steady_clock::now();
    h_end = std::chrono::high_resolution_clock::now();
    c_end = clock();
    time(&t_end);

    std::cout << "highres = " << std::chrono::duration<double>(h_end - h_start).count() << " s \n";
    std::cout << "steady = " << std::chrono::duration<double>(steady_end - steady_start).count() << " s \n";

    printf("clock() = %.2lf seconds \n", (c_end - c_start) / (double)CLOCKS_PER_SEC);
    printf("time() = %.2lf seconds \n", difftime(t_end, t_start));

    return 0;
}

關於g ++的結果(Debian 4.9.2-10)4.9.2: clock()= 0.00秒

sleep(3) took: 

highres = 3.00098 s 
steady = 3.00098 s 
clock() = 0.00 seconds 
time() = 3.00 seconds 

C ++ MSVS 2013 v120(Windows 7x64)上的結果:

sleep(3) took:

highres = 3.00017 s
steady = 3.00017 s
clock() = 3.00 seconds
time() = 3.00 seconds

2-第二個例子OpenMP或<thread>http//coliru.stacked-crooked.com/a/2922c85385d197e1

#include <stdio.h>
#include <time.h>
#include <thread>
#include <iostream>
#include <chrono>
#include <vector>

int main(void) {

    std::cout << "for-loop took: \n\n";

    clock_t c_start, c_end;
    time_t t_start, t_end;
    std::chrono::high_resolution_clock::time_point h_start, h_end;
    std::chrono::steady_clock::time_point steady_start, steady_end;

    time(&t_start);  // less precise than clock() but always get the real actual time
    c_start = clock(); // clock() get only CPU-time, it can be more than real or less - sleep(3); took 0.00 seconds 
    h_start = std::chrono::high_resolution_clock::now();
    steady_start = std::chrono::steady_clock::now();

    #pragma omp parallel num_threads(10)
    {
        for (volatile int i = 0; i < 200000000; ++i);
    }

    steady_end = std::chrono::steady_clock::now();
    h_end = std::chrono::high_resolution_clock::now();
    c_end = clock();
    time(&t_end);

    std::cout << "highres = " << std::chrono::duration<double>(h_end - h_start).count() << " s \n";
    std::cout << "steady = " << std::chrono::duration<double>(steady_end - steady_start).count() << " s \n";

    printf("clock() = %.2lf seconds \n", (c_end - c_start) / (double)CLOCKS_PER_SEC);
    printf("time() = %.2lf seconds \n", difftime(t_end, t_start));

    int b = getchar();

    return 0;
}

結果g ++(Debian 4.9.2-10)4.9.2: clock()= 1.35秒

for-loop took: 

highres = 0.213906 s 
steady = 0.213905 s 
clock() = 1.35 seconds 
time() = 0.00 seconds 

C ++ MSVS 2013 v120(Windows 7x64)上的結果:

for-loop took:

highres = 1.49109 s
steady = 1.49109 s
clock() = 1.49 seconds
time() = 2.00 seconds

恢復:

  1. 當線程休眠時,g ++ 4.9.2上的clock()不像其他函數那樣測量時間。

  2. 當我們使用OpenMP或使用<thread>link )來使用多線程時,g ++ 4.9.2上的clock()測量所有線程的CPU周期。

同樣在Windows MSVS 2013上, clock()在兩種情況下都會測量所需的實時時間,但這並不能保證clock()在其他平台上的測量值相同(在Linux上,g ++的睡眠為0,多線程為x倍)。

基於此,如果std::chrono::high_resolution_clock::now(); 在Windows MSVS 2013和g ++ 4.9.2兩種情況下都需要實時測量,這是否能保證它能在所有其他平台上測量真正的高分辨率時間,並確保它是否能保證標准C ++ 11/14?

簡短回答:從C ++ 14標准來看, high_resolution_clock沒有明確提供您正在尋找的保證。

目前, steady_clocksystem_clock提供更好,更明確的保證。 但是,大多數實現可能會確保HRC在其線程處於休眠狀態時前進。 盡管如此,最好還是進行自己的類型別名。 請參閱下面的“編輯”部分和評論中的討論。

答案很長:

標准草案做其實隱含確認(附注30.2.4“時序規范”,注1) 要求時鍾對象,而其相關的線程休眠推進。 對於上下文,本節將解釋標准庫計時器對象的工作方式; 計時器的行為基於用於設置它的時鍾的行為。

[ 注意:如果時鍾與穩定時鍾不同步,例如CPU時鍾,則這些超時可能無法提供有用的功能。 - 結束說明 ]

請注意,在這種情況下,“超時可能不提供有用的功能”意味着如果使用定時器使用非同步(非實時)時鍾 sleep_until特定時鍾時間,則線程將不會被喚醒 所以上面的說明有點輕描淡寫。

事實上,時鍾規范(20.13.3)中沒有任何內容實際上需要與穩定時鍾同步。

然而,標准似乎隱含縱容兩個潛在的別名high_resolution_clock在20.13.7.3的定義:

high_resolution_clock可以是system_clocksteady_clock的同義詞。

當然, steady_clock是穩定的。 system_clock 不是 ,因為在程序運行時系統時間可能會改變(例如,作為NTP更新的結果)。

然而, system_clock (20.13.7.1)仍然一個“實時”時鍾:

system_clock類的對象表示來自系統范圍實時時鍾的掛鍾時間。

因此,當線程休眠時, system_clock 不會停止前進。 這證實了尼科爾流星錘的觀點,即一個is_steady可能是high_resolution_clock即使如你所期望的時鍾的行為(即它不考慮推進其相關線程的狀態)。

基於此,期望大多數主流實現對high_resolution_clock使用某種實時(即同步)時鍾似乎是合理的。 畢竟,實現被設計為有用,並且如果時鍾不是實時的話通常不太有用,特別是如果它按照上面“有用功能”的注釋與定時器一起使用。

但是,由於無法保證 ,您應該檢查要使用的每個實現的行為和/或文檔。

編輯:我已經就此問題開始討論ISO C ++標准組 ,這表明這是標准中的一個錯誤。 第一個回復,從霍華德Hinnant(欣南特),誰需要信貸把它標准,是值得引用:

我不反對棄用high_resolution_clock ,意圖在適當的棄用期后刪除它。 實際情況是,它總是一個typedef,無論是steady_clock還是system_clock ,程序員最好選擇其中一個並知道他得到了什么,而不是選擇high_resolution_clock並通過擲骰子獲得一些其他時鍾。

......因此,根據Hinnant的說法,道德不是使用high_resolution_clock

編輯2:

這個問題high_resolution_clock根據Hinnant(欣南特)是沒有這么多,你可能會碰到與HRC的問題(盡管這可能的,即使使用符合標准的編譯,按照上面的說法),但因為你通常不實際獲得比其他兩個時鍾中的一個更低的分辨率(盡管你需要在類型別名或typedef中手動比較它們的分辨率以獲得“最大分辨率”非睡眠時鍾),沒有具體的好處。 因此,您需要權衡線程在符合實現方面永遠休眠的風險與名稱high_resolution_clock的語義優勢以及避免僅創建自己的typedef或type-alias的簡單/簡潔優勢。

以下是各種方法的實際代碼:

  • 使用static_assert 檢查 high_resolution_clock是否實際是別名為真實時鍾。 可能永遠不會觸發,這意味着您將自動獲得分辨率最高的“實時”時鍾而不會弄亂您自己的typedef:

      static_assert( std::is_same<high_resolution_clock, steady_clock>::value || std::is_same<high_resolution_clock, system_clock>::value, "high_resolution_clock IS NOT aliased to one of the other standard clocks!"); 
  • 如果high_resolution_clock::is_steady為true,請使用HRC; 否則更喜歡system_clocksteady_clock之間的高分辨率時鍾。 請注意 ,如果high_resolution_clock::is_steady為false,這可能只意味着HRC別名為system_clock ,在這種情況下,您最終會得到一個新的類型別名,實際上與high_resolution_clock類型相同。 但是,創建自己的類型別名會使其明確,並保證即使是惡意但符合要求的實現也不會出現上述問題。

     using maxres_sys_or_steady = std::conditional< system_clock::period::den <= steady_clock::period::den, system_clock, steady_clock >::type; using maxres_nonsleeping_clock = std::conditional< high_resolution_clock::is_steady, high_resolution_clock, maxres_sys_or_steady >::type; 

該標准未從其時鍾中指定此行為。 不完全是。

時鍾具有is_steady靜態屬性,可以檢查。 is_steady返回true的任何時鍾都不能是那種因為你讓一個線程進入休眠而停止運行的時鍾。 然而,由於各種原因,該值為假的時鍾可能是不穩定的。 它可能不穩定,因為它是一個掛鍾,如果系統時間改變,它會改變。 或者因為滴答之間的時間段是平均值,而不是確切的數字。

所以is_steady並沒有真正回答你的問題。

該標准沒有指定high_resolution_clock::is_steady ,但它確實需要實現來回答該問題。 如果它是穩定的,那么你可以保證睡眠線程不會停止時鍾。 但如果它不穩定......你根本得不到任何保證。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM