2016-12-14 7 views
0

Есть ли простой, но верный способ измерения относительных различий в производительности между двумя реализациями алгоритмов в программах на C. В частности, я хочу сравнить эффективность реализации A против B? Я думаю о схеме, как это:Относительный анализ времени выполнения кода C с использованием системных монотонных часов

  • В блок тестовой программы:

    1. запуска таймера
    2. вызов функции
    3. остановка таймера
    4. разница
    5. получить от начала времени остановки
  • Выполните приведенную выше схему для пары функций A и B, затем получить процентную разницу во времени выполнения, чтобы определить, что быстрее.

Пройдя некоторые исследования, я столкнулся с этим вопросом об использовании в C Monotonic clock on OSX, что, по-видимому, может дать мне хотя бы наносекундную точность. Чтобы быть ясным, я понимаю, что точные контролируемые измерения трудно выполнить, например, то, что обсуждалось в "With O(N) known and system clock known, can we calculate the execution time of the code?, которое, как я полагаю, должно быть неактуальным в этом случае, потому что мне нужно только относительное измерение.

Все рассмотрено, является ли это достаточным и обоснованным подходом к анализу, который я хочу выполнить? Есть ли какие-либо подробности или соображения, которые я могу пропустить?

+0

Если вы «Работая с macOS Sierra (10.12) или более поздней версией, в конечном итоге поддерживается« clock_gettime() », предоставляемая системой, хотя реальное разрешение - это только микросекунды, а не наносекунды. (То есть компонент 'tv_nsec' ответа от' clock_gettime() 'всегда заканчивается тремя нулями.) Вопрос, на который вы ссылаетесь, предваряет Сьерра более чем на четыре года. –

+0

Ну, я работаю с 10.11, а вызовы функций заголовка машины из задания все еще работают. Но это хорошо знать. – TSIguy

ответ

0

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

В качестве примера у меня есть код, который выполняет некоторые функции, которые проверяют, является ли заданное число простым. Функция управления:

static void test_primality_tester(const char *tag, int seed, int (*prime)(unsigned), int count) 
{ 
    srand(seed); 
    Clock clk; 
    int nprimes = 0; 
    clk_init(&clk); 

    clk_start(&clk); 
    for (int i = 0; i < count; i++) 
    { 
     if (prime(rand())) 
      nprimes++; 
    } 
    clk_stop(&clk); 

    char buffer[32]; 
    printf("%9s: %d primes found (out of %d) in %s s\n", tag, nprimes, 
      count, clk_elapsed_us(&clk, buffer, sizeof(buffer))); 
} 

Я хорошо осведомлен о srand() — why call it once?, но смысл использовать srand() раз каждый раз, когда эта функция вызывается, чтобы гарантировать, что тесты обработать ту же последовательность случайных чисел. На macOS, RAND_MAX - 0x7FFFFFFF.

Тип Clock содержит аналоги для двух конструкций struct timespec, для начала и остановки. Функция clk_init() инициализирует структуру; clk_start() записывает время начала в структуре; clk_stop() записывает время остановки в структуре; и clk_elapsed_us() вычисляет истекшее время между временем начала и остановки в микросекундах. Пакет написан, чтобы предоставить мне кросс-платформенную переносимость (за счет некоторых головных болей при определении того, какая из лучших подсекундных подпрограмм, доступных во время компиляции).

Вы можете найти свой код для таймеров на Github в хранилище https://github.com/jleffler/soq, в каталоге src/libsoq - файлы timer.h и timer.c. Код еще не догнал macOS Sierra с clock_gettime(), хотя его можно было скомпилировать с использованием -DHAVE_CLOCK_GETTIME в качестве параметра компилятора командной строки.

Этот код вызывается из функции one_test():

static void one_test(int seed) 
{ 
    printf("Seed; %d\n", seed); 
    enum { COUNT = 10000000 }; 
    test_primality_tester("IsPrime1", seed, IsPrime1, COUNT); 
    test_primality_tester("IsPrime2", seed, IsPrime2, COUNT); 
    test_primality_tester("IsPrime3", seed, IsPrime3, COUNT); 
    test_primality_tester("isprime1", seed, isprime1, COUNT); 
    test_primality_tester("isprime2", seed, isprime2, COUNT); 
    test_primality_tester("isprime3", seed, isprime3, COUNT); 
} 

И основной программе может принять одну или серию семян, или использует текущее время в качестве затравки:

int main(int argc, char **argv) 
{ 
    if (argc > 1) 
    { 
     for (int i = 1; i < argc; i++) 
      one_test(atoi(argv[i])); 
    } 
    else 
     one_test(time(0)); 
    return(0); 
} 
+0

Спасибо за этот ответ, он подтверждает мои мысли. Пример, который вы указали в каталоге libsoq, ясен, но я не вижу кода настройки для конфигурации монотонных часов. Я предполагаю, что могу просто использовать COCK_MONOTONIC define вместо CLOCK_REALTIME правильно? – TSIguy

+0

Вы правы. До Сьерры у меня не было машины, где CLOCK_MONOTONIC был вариантом - я в основном использовал 'gettimeofday()' - так что это было не так много для меня. Это мой «рабочий код»; достаточно хорош для моих целей, но не обязательно совершенен. Одним из недостатков CLOCK_MONOTONIC является то, что он не дает вам абсолютной отметки времени (относительно эпохи - aka 1970-01-01 00:00:00 +00: 00), тогда как 'gettimeofday()' делает. Но для измерения производительности вы редко беспокоитесь о времени, когда оно запускалось на том же уровне точности, что и вы беспокоитесь о том, сколько времени потребовалось для запуска. –