LINUX.ORG.RU

Передача функции и подсчет времени ее работы

 


0

3

Задача:

необходимо создать функцию(1) для измерения скорости выполнения функции(2) и передать функции(1) функцию(2). Функция(1) должна просто выводить в консоль время работы функции(1).

Функция(1)(данный код работает и показывает некий результат для различных сортировок):

#include <stdio.h>
#include <chrono>

const auto start_time = std::chrono::steady_clock::now();

foo2(array, size);

double result_time = std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::steady_clock::now() - start_time).count() / 1000000.0;
printf("foo2 duration: %.10lf", result_time);

Функция(2):
void foo2(int array[], int array_size){
...
}

Вопросы:
1. Как вы считаете, корректен ли подобный способ получения продолжительности работы функции?
2. Как лучше передать функции(1) функцию(2)? Имеется множество алгоритмов, и требуется вычислять скорость их работы.

Deleted

Последнее исправление: Deleted (всего исправлений: 3)
template<class Clock, typename F, typename... Args>
std::chrono::nanoseconds
measure(F const& f, Args... args) {
    const auto start = Clock::now();
    f(args...);
    const auto end = Clock::now();
    return end - start;
}

int f(int a, float b) {
    return a + b;
}

struct F {
    int x;
    int operator()(int a, float b) const {
        return x + a + b;
    }
};

    std::cout << measure<std::chrono::high_resolution_clock>([]() { sleep(1); }).count() << std::endl;
    std::cout << measure<std::chrono::system_clock>(f, 1, 2.).count() << std::endl;
    std::cout << measure<std::chrono::steady_clock>(F{1}, 2, 3.).count() << std::endl;

high_resolution_clock / system_clock / steady_clock — на выбор, отличаются монотонностью/реальностью. Ну и такты ещё можно RDTSC-ом считать, да.

quasimoto ★★★★
()
Ответ на: комментарий от SZT

STL

20 лет уже как не STL, а стандартная библиотека :)

chrono — так вообще относительно новая вещь из неё.

Для этого rdtsc есть

Всё-таки, часы и такты это разные вещи.

quasimoto ★★★★
()
Ответ на: комментарий от quasimoto

20 лет уже как не STL, а стандартная библиотека :)

Стандартная библиотека это слишком общее слово. В Pure C тоже есть стандартная библиотека libc, и на плюсах можно писать вообще без STL, используя libc или даже вообще без библиотек, дергая ядро напрямую системными вызовами, используя для этого ассемблерные вставки. Вон Qt вообще без STL нормально работают, и ничего. Впрочем, я отвлекся

chrono — так вообще относительно новая вещь из неё.

Которая к тому же есть далеко не во всех вариантах STL.

Всё-таки, часы и такты это разные вещи.

Зная число тактов и тактовую частоту процессора, несложно посчитать время

SZT ★★★★★
()

А почему бы не воспользоваться clock_gettime() из time.h? Будет что-то вроде:

timespec after, before;
clock_gettime(CLOCK_MONOTONIC, &before);
func2();
clock_gettime(CLOCK_MONOTONIC, &after);
printf("%l s, %l ms\n\r", after.tv_sec  - before.tv_sec,
                       after.tv_nsec - before.nsec);

// Если что, на чистых сях давно не писал

anonymous
()
Ответ на: комментарий от SZT

Верно, давайте тащить чудесный непереносимый между компиляторами ассемблер. Зачем использовать всякие неудобные высокоуровневые костыли?

staseg ★★★★★
()
Ответ на: комментарий от SZT

Стандартная библиотека это слишком общее слово. В Pure C тоже есть стандартная библиотека libc, и на плюсах можно писать вообще без STL, используя libc или даже вообще без библиотек, дергая ядро напрямую системными вызовами, используя для этого ассемблерные вставки.

Вспоминается старый прикол, как из буханки хлеба можно смастерить троллейбус,.. но зачем?

staseg ★★★★★
()
Ответ на: комментарий от SZT

std:: это вообще не STL — называется стандартная библиотека, std::chrono — тем более. STL это то что лежит на http://www.sgi.com/tech/stl/ — большинство её вообще не используют, просто она в своё время оказала влияние на _стандартную_ библиотеку плюсов (то есть то что описано в стандарте и идёт, например, с GCC) непосредственно некоторыми контейнерами и паттернами написания обобщённого кода вообще. С тех пор такое же влияние на стандарт оказывали и другие сторонние библиотеки — boost, например.

Которая к тому же есть далеко не во всех вариантах STL.

Эти штуки shipped с компиляторами — если претендуют на поддержку C++11, то они там есть (GCC, Clang).

Зная число тактов и тактовую частоту процессора, несложно посчитать время

Там есть некоторые проблемы связанные с out of order, multi-processor / multi-cores системами, сменой частоты. А часы это отдельный чип на плате со своей частотой. То есть разные вещи — так-то приготовить можно (с cpuid, повторением в цикле, калибровкой), но это имеет смысл если нужны именно такты а не время и для относительно малых их количеств.

Adonai, они — раз уж всё равно C++11.

quasimoto ★★★★
()
Последнее исправление: quasimoto (всего исправлений: 1)

1. Как вы считаете, корректен ли подобный способ получения продолжительности работы функции?

надо код смотреть. Если нет побочных эффектов по времени — да, корректно. А может foo2 форкается и возвращается запустив сама себя ещё раз?

Как лучше передать функции(1) функцию(2)?

если у тебя PureC функции, то передай указатель на функцию.

если у тебя ООП код C++, то сделай класс для расчёта времени(а дальше от задачи зависит. Смотря что ты считаешь «временем»)

emulek
()
Ответ на: комментарий от staseg

Верно, давайте тащить чудесный непереносимый между компиляторами ассемблер.

если ТС считает, что «время это число тактов моего x86», то иначе нельзя.

А как он считает на самом деле — я не знаю. В многозадачной ОС время можно определять по разному, способ с RDTSC самый простой и потому самый переносимый ИМХО.

emulek
()

1. Как вы считаете, корректен ли подобный способ получения продолжительности работы функции?

в корне ошибочен.

Во первых для измерений заюзанных ресурсов (CPU в том числе) есть getrusage. Замеров времени недостаточно.

Во вторых время исполнения зависит от хреновой горы неподконтрольных параметров и чтобы получить «объективку» статистику надо (как-то и где-то) накапливать и потом уже считать. Хотя бы усреднять до min max avg

MKuznetsov ★★★★★
()
Ответ на: комментарий от MKuznetsov

Во первых для измерений заюзанных ресурсов

а нужны разве ресурсы, а не время?

Во вторых время исполнения зависит от хреновой горы неподконтрольных параметров

а если ТСа волнуют эти параметры, и он хочет их замерить?

emulek
()

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

а недолжно быть «функции 2»: это либо класс-измеритель и шаблон-измеряемое в C++ либо макросы С и кучка функций. Чтобы как минимум сохранить возможность передачи нужных параметров в функцию 2.

и вообще для этого есть профилировщики :-)

MKuznetsov ★★★★★
()
Ответ на: комментарий от emulek

а нужны разве ресурсы, а не время?

нужно и то и другое.

Из топика следует что ТС хочет(и будет) сравнивать алгоритмы по производительности. Тупые отсечки времени при этом - «сферичный конь» как уже все выше высказали.

MKuznetsov ★★★★★
()
Ответ на: комментарий от MKuznetsov

Можно ж foo2 сделать функцией которая будет принимать ещё один функтор real_foo2, какие-то нужные параметры, крутить real_foo2 в цикле, усреднять, а сама как функтор тоже передаваться в foo1 которая только снимает время до и после (как у ТС сейчас).

quasimoto ★★★★
()
Ответ на: комментарий от MKuznetsov

Из топика следует что ТС хочет(и будет) сравнивать алгоритмы по производительности. Тупые отсечки времени при этом - «сферичный конь» как уже все выше высказали.

меня мало волнуют «все». СПВ они на то и СПВ, что придумывают всякую ерунду, которая на практике обычно не имеет значения. На практике «тупые отсечки» дают годный, повторяемый, и главное — практически важный результат. А вот всякие usage дают как раз сферического коня в вакууме. Т.е. не реальное время, а некое виртуальное, без учёта всякого разного, которое IRL имеет место быть.

Т.ч. для практики и то и другое нужно, а что важнее и нужнее ТСу — я не знаю.

emulek
()
Ответ на: комментарий от quasimoto

Можно ж foo2 сделать функцией которая будет принимать ещё один функтор real_foo2, какие-то нужные параметры, крутить real_foo2 в цикле, усреднять, а сама как функтор тоже передаваться в foo1 которая только снимает время до и после (как у ТС сейчас).

и чего только не придумают... А почему нельзя сделать всё это внутри foo1()? Которая принимает функтор foo2().

emulek
()
Ответ на: комментарий от emulek

меня мало волнуют «все». СПВ они на то и СПВ, что придумывают всякую ерунду, которая на практике обычно не имеет значения. На практике «тупые отсечки» дают годный, повторяемый, и главное — практически важный результат. А вот всякие usage дают как раз сферического коня в вакууме. Т.е. не реальное время, а некое виртуальное, без учёта всякого разного, которое IRL имеет место быть.

ты когда-нибудь читаешь например man`ы? почитай что-ли..

MKuznetsov ★★★★★
()
Ответ на: комментарий от MKuznetsov

ты когда-нибудь читаешь например man`ы?

читаю. И что? В манах как раз и пишут про сферических коней в вакууме, их ещё на практику надо допиливать.

Нет, если у ТСа задача — написать документацию к какой-то либе, да, твой подход корректен, а мой нет. Признаю.

emulek
()
Ответ на: комментарий от staseg

Эти высокоуровневые костыли на данный момент переносимы между разными компиляторами примерно в той же степени, что и ассемблерные вставки, если смотреть в пределах одной архитектуры. Если рассматривать конкретно разные версии компилятора GCC (например если надо сравнивать скорость кода в разных версиях), то далеко не во всех его версиях эти самые высокоуровневые костыли имеются, асмовставки же есть и формат их не менялся.

SZT ★★★★★
()
Ответ на: комментарий от emulek

Просто чтобы получить то и другое

#include <chrono>

using Duration = std::chrono::nanoseconds;

template<class Clock, typename F, typename... Args>
Duration measure(F const& f, Args... args) {
    const auto start = Clock::now();
    f(args...);
    const auto end = Clock::now();
    return std::chrono::duration_cast<Duration>(end - start);
}

template<typename F, typename... Args>
void repeate(unsigned n, F const& f, Args... args) {
    while (n--) f(args...);
}

template<class Clock, typename F, typename... Args>
Duration measure_avg(unsigned n, F const& f, Args... args) {
    return n ? measure<Clock>(repeate<F>, n, f, args...) / n : Duration(0);
}
quasimoto ★★★★
()
Ответ на: комментарий от SZT

Таки как асмом время измерять?

А так есть CLOCK_MONOTONIC/CLOCK_MONOTONIC_RAW (если «в пределах» :)).

quasimoto ★★★★
()
Ответ на: комментарий от SZT

Для этого rdtsc есть

rdtsc

  1. Не переносим
  2. Значение счетчика разное для каждого CPU в системе. Таким образом переброс планировщиком задачи на другой CPU во время замера даст весьма удивительные результаты.
  3. Не отражает время выполнения задачи из-за переключений контекста на другие процессы (как и все остальные способы кроме getrusage(2) ).
  4. Дает бесполезний результат при изменении тактовой частоты проца (что в современных процах происходит чуть менее чем всегда для энергосбережения или при turbo boost). При этом другие параметры железа никуда не меняются и полученое кол-во тактов ничего внятного не сможет показать.

Итого не нужно.

Pavval ★★★★★
()
Ответ на: комментарий от Pavval

Не переносим

Более-менее переносимым можно назвать лишь вариант использования функции clock() ибо она есть в стандарте C

Значение счетчика разное для каждого CPU в системе. Таким образом переброс планировщиком задачи на другой CPU во время замера даст весьма удивительные результаты.

man taskset.

Не отражает время выполнения задачи из-за переключений контекста на другие процессы (как и все остальные способы кроме getrusage(2) ).

Согласен. Можно еще при желании код запихнуть в модуль ядра, чтобы оно куда попало не переключалось и не мигрировало с ядра на ядро, и время тогда можно измерять другими способами

Дает бесполезний результат при изменении тактовой частоты проца

Любой способ будет давать бесполезный результат при изменении тактовогй частоты проца, в том числе и getrusage(2)

SZT ★★★★★
()
Ответ на: комментарий от SZT

Еще можно вспомнить про System Management Mode, который тоже может подпортить результаты измерения

SZT ★★★★★
()
Ответ на: комментарий от SZT

Более-менее переносимым можно назвать лишь вариант использования функции clock() ибо она есть в стандарте C

http://cpprocks.com/c11-compiler-support-shootout-visual-studio-gcc-clang-intel/

Feature                 MSVC    libstdc++       libc++
Time utilities (chrono) Yes     Yes             Yes

Любой способ будет давать бесполезный результат при изменении тактовогй частоты проца, в том числе и getrusage(2)

https://access.redhat.com/site/documentation/en-US/Red_Hat_Enterprise_MRG/2/h...

quasimoto ★★★★
()
Ответ на: комментарий от quasimoto

http://cpprocks.com/c11-compiler-support-shootout-visual-studio-gcc-clang-intel/

Есть старые версии библиотек с++, старые версии студии, другие компиляторы и другие библиотеки, которые это не поддерживают.

https://access.redhat.com/site/documentation/en-US/Red_Hat_Enterprise_MRG/2/h...

И что? Если часть времени программа работала с одной частотой процесса, часть с другой, что этот таймер намеряет?

SZT ★★★★★
()
Ответ на: комментарий от SZT

Есть старые версии библиотек с++, старые версии студии, другие компиляторы и другие библиотеки, которые это не поддерживают.

И поэтому всем остальным нельзя пользоваться стандартными chrono/clock_gettime/getrusage и прочими boost::timer, а нужно писать на ассемблере с rdtsc и модулями ядра?)

И что?

HPET независим от частоты CPU — у него своя частота (я выше уже писал про «отдельный чип на плате»). Ну и стандартный таймер это TSC, уже приготовленный в ядре, разумеется — например, constant_tsc/invariant_tsc/nonstop_tsc тоже не зависят от смен частоты/смены режимов/того и другого. Смысл заморачиваться с rdtsc?

quasimoto ★★★★
()
Последнее исправление: quasimoto (всего исправлений: 2)
Ответ на: комментарий от quasimoto

И поэтому всем остальным нельзя пользоваться стандартными chrono/clock_gettime/getrusage и прочими boost::timer, а нужно писать на ассемблере с rdtsc и модулями ядра?)

Ну почему нельзя? Если нет требований собирать код на каких-нибудь старых версиях студии или других компиляторах - пользуйтесь на здоровье. И я не призываю использовать только лишь rdtsc на все случаи жизни, есть много других вариантов.

HPET независим от частоты CPU

От частоты CPU зависит то, с какой скоростью процессор будет выполнять инструкции. Если часть времени программа выполнялась с одной частотой CPU, другую часть времени с другой, измерение времени выполнения кода в таком «смешанном» режиме - бесполезны. Лучше зафиксировать частоту процессора на одном уровне

SZT ★★★★★
()
Ответ на: комментарий от SZT

Т.о. такты и CPU time это такты и CPU time (натурально), а время — время, т.е. реальное, с учётом всего что вообще может происходить при выполнении (переключения, смены частот, IO). Надо смотреть что нужно и выбирать.

quasimoto ★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.