Разница во времени между выполнением двух операторов не согласована

0

Не могли бы вы рассказать мне, почему значение timediff напечатанное следующей программой, часто составляет 4 микросекунды (в диапазоне от 90 до 1000 раз для разных прогонов), но иногда 70 или более микросекунд для нескольких случаев (в диапазоне от 2 до 10 раз для разных прогонов):

#include <iostream>
using namespace std;
#include<sys/time.h>
#define MAXQ 1000000
#define THRDS 3
double GetMicroSecond()
{
    timeval tv;
    gettimeofday (&tv, NULL);
    return (double) (((double)tv.tv_sec * 1000000) + (double)tv.tv_usec);
}

int main()
{
        double timew, timer, timediff;
        bool flagarray[MAXQ];
        int x=0, y=0;
        for(int i=0; i<MAXQ; ++i)
            flagarray[i] = false;
        while(y <MAXQ)
       {
            x++;
            if(x%1000 == 0)
            {
                    timew = GetMicroSecond();
                    flagarray[y++]=true;
                    timer = GetMicroSecond();
                    timediff = timer - timew;
                    if(timediff > THRDS) cout << timer-timew << endl;
            }
       }
}

Скомпилировано с использованием: g++ testlatency.cpp -o testlatency

Примечание. В моей системе имеется 12 ядер. Производительность проверяется только с помощью этой программы, запущенной в системе.

  • 0
    Вы не можете полагаться на точность в микросекундах в большинстве операционных систем.
  • 1
    Думаю, ваша программа не единственная, запущенная в системе ...
Показать ещё 1 комментарий
Теги:
latency

4 ответа

0
Лучший ответ

timew = GetMicroSecond();
flagarray [y++] = TRUE;
timer = GetMicroSecond();

Утверждение flagarray[y++]=true; займет гораздо меньше, чем микросекунда, чтобы выполнить на современном компьютере, если flagarray[y++ ] находится в кеше уровня 1. Задание займет больше времени, если это место находится в кеше уровня 2, но не в кеше уровня 1, гораздо дольше, если оно находится в кэше уровня 3, но не в кеше уровня 1 или уровня 2, и намного, гораздо дольше, если оно не является в любом из кешей.

Еще одна вещь, которая может заставить timer-timew превышать три миллисекунды, - это когда ваша программа выдает ОС. Недостатки кэша могут привести к урону. Таким образом, системные вызовы. Функция gettimeofday - системный вызов. Как правило, вы должны ожидать, что любой системный вызов будет работать.


Примечание. В моей системе имеется 12 ядер. Производительность проверяется только с помощью этой программы, запущенной в системе.

Это неправда. Всегда есть много других программ, и многие, многие другие потоки работают на вашем 12-ядерном компьютере. К ним относятся сама операционная система (которая включает в себя множество потоков сама по себе), плюс множество и множество маленьких демонов. Всякий раз, когда ваша программа дает, ОС может решить временно приостановить вашу программу, чтобы один из мириадов других потоков, которые временно приостановлены, но просят использовать процессор.

Один из этих демонов - демон протокола сетевого времени (ntpd). Это делает всевозможные фанковые мелочи для системных часов, чтобы синхронизировать их с атомными часами. С маленькой инструкцией, такой как flagarray[y++]=true является единственной вещью между последовательными вызовами gettimeofday, вы можете даже иногда видеть время назад.


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

Современные системы на базе Unix имеют лучшие таймеры (например, clock_gettime), чем gettimeofday, которые не подпадают под изменения, внесенные демоном протокола сетевого времени. Вы должны использовать один из них, а не gettimeofday.

0

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

  • Кэш-пропуск/заполнение
  • События планировщика
  • Прерывания

    bool flagarray [MAXQ];

Поскольку вы определили MAXQ на 1000000, допустим, что flagarray занимает 1 flagarray пространства.

Вы можете вычислить, сколько промахов может возникнуть на основе ваших размеров D-кеша L1/L2. Затем вы можете соотнести с тем, сколько итераций требуется для заполнения всего L1, и начать пропустить, а также с L2. OS может отложить процесс и перенести его, но я надеюсь, что это меньше вероятность из-за количества ядер, которые у вас есть. То же самое происходит с прерываниями. Простаивающая система никогда не бывает полностью бездействующей. Вы можете перенести свой процесс на основной номер, скажем, N, делая

taskset 0x<MASK>./exe и контролировать его выполнение.

Если вам действительно интересно, я бы предположил, что вы используете инструмент "perf", доступный на большинстве дистрибутивов Linux.

Вы можете сделать

perf stat -e L1-dcache-loadmisses

или

perf stat -e LLC-load-misses

Когда у вас есть эти числа и количество итераций, вы начинаете создавать картину активности, которая вызывает замеченное отставание. Вы также можете отслеживать события планировщика ОС, используя "perf stat".

0

Есть много вещей, происходящих внутри современной ОС одновременно с выполнением вашей программы. Некоторые из них могут "красть" процессор из вашей программы, как указано в ответе NPE. Еще несколько примеров того, что может повлиять на время:

  • прерывания от устройств (таймер, HDD, сетевые интерфейсы, некоторые из них упоминаются);
  • доступ к ОЗУ (кэширование)

Ни одно из них не является легко предсказуемым.

Вы можете ожидать согласованности, если вы запускаете свой код на каком-то микроконтроллере или, возможно, используете ОС реального времени.

0

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

Даже если ваш код работает бесперебойно, линия, которую вы пытаетесь выполнить:

flagarray[y++]=true;

скорее всего, потребуется намного меньше времени для выполнения, чем сам код измерения.

  • 0
    Это нормально: «Даже если ваш код работает непрерывно, строка, которую вы пытаетесь рассчитать: flagarray [y ++] = true; скорее всего, для ее выполнения требуется гораздо меньше времени, чем для самого кода измерения». Но почему это не консистенция?
  • 0
    @PradipGhanty Прерывания, кэширование и т. Д. Время выполнения не согласовано на современном компьютере общего назначения. (Они могут быть согласованы на некоторых встроенных процессорах. И в далеком прошлом каждая машинная инструкция выполнялась за фиксированное количество часов. Но это очень далекое прошлое.)

Ещё вопросы

Сообщество Overcoder
Наверх
Меню