Подтвердить что ты не робот

Значит ли стандарт С++ 11 гарантировать, что high_resolution_clock измеряет реальное время (не CPU-циклы)?

Как известно, clock() может показывать меньше или больше значения реального времени - оба случая показаны в следующих примерах 1 и 2.

Для высокоточных измерений времени в С++ 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 

Результат на С++ 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 

Результат на С++ MSVS 2013 v120 (Windows 7x64):

for-loop took:

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

Резюме:

  • Когда поток спит, тогда clock() на g++ 4.9.2 не измеряет время, в отличие от других функций.

  • Когда мы используем многопоточность с помощью OpenMP или используя <thread> (ссылка), тогда clock() в g++ 4.9.2 измеряет циклы CPU всех потоков.

Также в Windows MSVS 2013 clock() в обоих случаях требуется измерение реального времени, но это не гарантирует, что clock() измеряет то же самое на других платформах (в linux g++ равен 0 для сна и x-fold для многопоточность).

Исходя из этого, если std::chrono::high_resolution_clock::now(); измеряет требуемое реальное время в обоих случаях как на Windows MSVS 2013, так и на g++ 4.9.2, это гарантирует, что оно будет измерять реальное время высокого разрешения на всех других платформах и будет ли оно гарантировать стандарт С++ 11/14 ~

4b9b3361

Ответ 1

Краткий ответ: по стандарту С++ 14 high_resolution_clock НЕ предоставляет явную гарантию, которую вы ищете.

В настоящее время steady_clock и system_clock предоставляют лучшие и более явные гарантии. Тем не менее, большинство реализаций, вероятно, обеспечат продвижение HRC во время сна. Тем не менее, может быть предпочтительнее сделать свой собственный тип сглаживания. См. Разделы "РЕДАКТИРОВАТЬ" ниже и обсуждение в комментариях.

Длинный ответ:

черновик стандарта на самом деле неявно подтверждается (в примечании 30.2.4 "Технические характеристики синхронизации", примечание 5), что объекты Clock не, требуемый для продвижения, пока их связанный поток спал. Для контекста в этом разделе объясняется, как работают объекты таймера стандартной библиотеки; поведение таймера основано на поведении часов, используемых для его установки.

[Примечание: если часы не синхронизированы с постоянными часами, например, CPU time clock, эти тайм-ауты могут не обеспечивать полезную функциональность. - конечная нота]

Обратите внимание, что в этом случае "тайм-ауты могут не предоставлять полезную функциональность" означает, что если вы используете таймер для sleep_until определенного времени в часах с использованием несинхронизированного (не реального времени) часов, ваш поток не будет просыпаться. Таким образом, примечание выше немного преуменьшает.

И, действительно, в спецификации Clock (20.13.3) нет ничего, что на самом деле требует синхронизации с устойчивыми часами.

Однако стандарт, по-видимому, неявно допускает две потенциальные псевдонимы для high_resolution_clock в определении в 20.13.7.3:

high_resolution_clock может быть синонимом system_clock или steady_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 С++ по этому вопросу, предполагая, что это является ошибкой в ​​стандарте. Первый ответ от Говарда Хиннанта, который берет на себя ответственность за то, что он поставил его в стандарт, заслуживает цитирования:

Я не возражаю против устаревания high_resolution_clock с намерением удалить его после подходящего периода отсрочки. Реальность такова, что она всегда является typedef для steady_clock или system_clock, и программисту лучше выбрать один из этих двух и узнать, что происходит с hes, чем выбрать high_resolution_clock и получить некоторые другие часы с помощью рулона кости.

... Итак, мораль, согласно Хиннанту, не использует high_resolution_clock.

ИЗМЕНИТЬ 2:

Проблема с high_resolution_clock в соответствии с Hinnant заключается не столько в том, что вы, вероятно, столкнетесь с проблемой с HRC (хотя это возможно даже с соответствующим компилятором в соответствии с приведенным выше аргументом), но так как вы обычно не получается получить более низкое разрешение, чем вы могли, с одним из двух других часов (хотя вам нужно вручную сравнить свои разрешения в псевдониме типа или typedef, чтобы получить "несовместимые часы с максимальным разрешением" ), нет никакой конкретной выгоды. Таким образом, вам нужно взвесить риск того, что потоки будут спать навсегда при выполнении соответствующих реализаций в сравнении с семантической выгодой имени high_resolution_clock и простотой/краткости, чтобы избежать просто сделать свой собственный typedef или тип-псевдоним.

Вот некоторый фактический код для различных подходов:

  • Используйте static_assert, чтобы проверить, действительно ли high_resolution_clock накладывается на настоящие часы. Это, вероятно, никогда не будет срабатывать, а это означает, что вы автоматически получаете часы реального времени с высоким разрешением, не входя в свои собственные typedefs:

     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!");
    
  • Используйте HRC, если high_resolution_clock::is_steady истинно; в противном случае предпочитают часы с более высоким разрешением между system_clock и steady_clock. ПРИМЕЧАНИЕ, что если high_resolution_clock::is_steady является ложным, это, вероятно, означает, что 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;
    

Ответ 2

Стандарт не определяет это поведение из его часов. Не совсем.

Часы имеют статическое свойство is_steady, которое можно проверить. Любые часы, для которых is_steady возвращает true, не могут быть типом часов, которые перестают работать только потому, что вы помещаете поток в режим сна. Однако часы, для которых это значение является ложным, могут быть неустойчивыми по целому ряду причин. Это может быть неустойчивым, потому что настенные часы будут меняться при изменении системного времени. Или потому, что период между тиками является средним, а не точным числом.

Итак, is_steady не отвечает на ваш вопрос.

В стандарте не указывается high_resolution_clock::is_steady, но для выполнения этого вопроса требуется выполнение ответа. Если он устойчив, то вам гарантировано, что спящий поток не остановит часы. Но если он не будет устойчивым... вы не получите никакой гарантии.