WedX - журнал о программировании и компьютерных науках

Получение тайминга определенной части кода в цикле в C

Описание проблемы

Ниже у меня есть программа, которая выполняет две простые операции сложения и умножения. Затем я сохраняю сумму этих двух простых операций в двух соответствующих переменных с именами total1 и total2. С точки зрения вычислений, для полного выполнения total2 потребуется больше времени. То, как я реализовал код, в настоящее время я синхронизирую всю симуляцию обеих математических операций.

Вопрос

Можно ли замерять только конечный результат total1 и total 2 отдельно? Я спрашиваю, так как хочу получить конкретное время total1 и total2 по отдельности.

Цель задачи

Я полностью осознаю, что long long дорог в отношении памяти и не является самым эффективным способом сэкономить память. Единственная цель этого кода и вопроса - синхронизация, а не оптимизация кода.

C-код

#include <stdio.h>
#include <time.h>

int main()
{

     long long total1 = 0, total2 = 0, i = 0;
     double simulation_time = 0;

     clock_t Start = clock();

     do
     {
          total1 += i + i; 
          total2 += i * i * i * i; 

          i++;

     } while (i < 1000000000);

     clock_t End = clock();

     printf("Total 1 = %u \n", total1);
     printf("Total 2 = %u \n", total2);

     simulation_time = (double)(End - Start) / CLOCKS_PER_SEC;
     printf("Runtime of Whole Simulation using clock_t: %f\n", simulation_time);


     return 0;
}
24.06.2018

  • Глядя на это в Visual Studio, отладчик говорит мне, что все переменные были оптимизированы, поэтому нет памяти. Только регистрирует. Код для total1 += i + i; становится одной операцией lea rdi,[rdi+rbx*2] , которая, согласно руководствам Intel, занимает доли наносекунды на ПК. Вам действительно нужно измерить это более точно? 24.06.2018

Ответы:


1

Я не уверен, что понимаю вашу проблему, но для каждой операции по отдельности вам просто нужно сделать два отдельных цикла.

#include <stdio.h>
#include <time.h>

int main()
{
    long long total1 = 0, total2 = 0, i = 0, j = 1000000000;
    double simulation_time1, simulation_time2;
    clock_t Start, End;

    /* addition */
    Start = clock();
    do
    {
         total1 += i + i;
         i++;
    } while (i < j);
    End = clock();
    simulation_time1 = (double)(End - Start) / CLOCKS_PER_SEC;

    /* multiplication */
    Start = clock();
    do
    {
         total2 += i * i * i * i;
         i++;
    } while (i < j);
    End = clock();
    simulation_time2 = (double)(End - Start) / CLOCKS_PER_SEC;

    printf("Total 1 = %u \n", total1);
    printf("Total 2 = %u \n", total2);
    printf("Runtime of Whole Simulation: %f\n"
        "Runtime of Addition:         %f\n"
        "Runtime of Multiplication:   %f\n",
        simulation_time1 + simulation_time2,
        simulation_time1, simulation_time2);

    return 0;
}
24.06.2018
  • Проблема здесь в том, что (после добавления i = 0 перед вторым циклом) я получаю Addition 0.23 и Multiplication 0.68. Но в исходном коде я получаю всего 0.68. Итак, что мы измеряем? Подсказка: на ПК с несколькими исполняющими устройствами add и mul могут выполняться параллельно. 24.06.2018

  • 2

    У вас есть две операции, которые вы хотите разделить по времени. Первое — это накопление i+i, а второе — накопление i*i*i*i.

    Я предполагаю, что вы используете GCC на x86-64 с -O2.

    Если мы закомментируем total2, сгенерированная сборка для вычисления total1 будет следующей:

      movabs rdx, 999999999000000000
    

    Умный компилятор! Он выполняет все вычисления во время компиляции. Таким образом, время, затраченное на это, в основном равно нулю.

    Если мы вместо этого закомментируем total1, сборка цикла для вычисления total2 будет следующей:

    .L2:
      mov rdx, rax
      imul rdx, rax       ; i squared
      add rax, 1
      imul rdx, rdx       ; i squared squared
      add rsi, rdx        ; accumulate
      cmp rax, 1000000000 ; loop condition
      jne .L2
    

    Вместо того, чтобы пытаться проводить микротестирование отдельных строк кода, мы можем обратиться к таблицам инструкций Agner Fog: https://www.agner.org/optimize/instruction_tables.pdf

    Предполагая, что вы используете Intel Haswell и немного распределяете порты вручную, таблицы сообщают нам:

    .L2:                  ; ports  cycles  latency
      mov rdx, rax        ; p0     0.25    1
      imul rdx, rax       ; p1     1       3
      add rax, 1          ; p0     0.25    1
      imul rdx, rdx       ; p1     1       3
      add rsi, rdx        ; p0     0.25    1
      cmp rax, 1000000000 ; p5     0.25    1
      jne .L2             ; p6     1-2
    

    Некоторые из этих инструкций могут перекрываться, поэтому на итерацию должно приходиться примерно 3-4 такта ядра. На процессоре с частотой 3-4 ГГц для выполнения одного миллиарда итераций цикла потребуется около 1 секунды.

    24.06.2018
    Новые материалы

    Объяснение документов 02: BERT
    BERT представил двухступенчатую структуру обучения: предварительное обучение и тонкая настройка. Во время предварительного обучения модель обучается на неразмеченных данных с помощью..

    Как проанализировать работу вашего классификатора?
    Не всегда просто знать, какие показатели использовать С развитием глубокого обучения все больше и больше людей учатся обучать свой первый классификатор. Но как только вы закончите..

    Работа с цепями Маркова, часть 4 (Машинное обучение)
    Нелинейные цепи Маркова с агрегатором и их приложения (arXiv) Автор : Бар Лайт Аннотация: Изучаются свойства подкласса случайных процессов, называемых дискретными нелинейными цепями Маркова..

    Crazy Laravel Livewire упростил мне создание электронной коммерции (панель администратора и API) [Часть 3]
    Как вы сегодня, ребята? В этой части мы создадим CRUD для данных о продукте. Думаю, в этой части я не буду слишком много делиться теорией, но чаще буду делиться своим кодом. Потому что..

    Использование машинного обучения и Python для классификации 1000 сезонов новичков MLB Hitter
    Чему может научиться машина, глядя на сезоны новичков 1000 игроков MLB? Это то, что исследует это приложение. В этом процессе мы будем использовать неконтролируемое обучение, чтобы..

    Учебные заметки: создание моего первого пакета Node.js
    Это мои обучающие заметки, когда я научился создавать свой самый первый пакет Node.js, распространяемый через npm. Оглавление Глоссарий I. Новый пакет 1.1 советы по инициализации..

    Забудьте о Matplotlib: улучшите визуализацию данных с помощью умопомрачительных функций Seaborn!
    Примечание. Эта запись в блоге предполагает базовое знакомство с Python и концепциями анализа данных. Привет, энтузиасты данных! Добро пожаловать в мой блог, где я расскажу о невероятных..


    Для любых предложений по сайту: [email protected]