Точное время: измеряем, применяем

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

       Статья рассчитана на читателей, уже имеющих некоторый опыт в программировании, и заметивших проблему точности выдержки временных интервалов стандартных функций. Автор статьи, begin_end, советует её читателям, программирующим на языке Delphi, так как все способы реализуются именно на этом языке.

       Наша задача – найти лучший метод точного измерения малых временных интервалов (желаемая точность – 10^-6 секунды), определить наиболее эффективный способ программирования задержек в исполнении кода, с такой же точностью.

       Программист, который уже пробовал разрабатывать различные прикладные приложения, например, связанные с передачей данных или с генерацией/анализом сигналов мог заметить, что все стандартные функции (sleep, beep, GetTickCount, таймеры) обладают большой погрешностью при работе с малыми значениями временного интервала. Это определено разрешением системного таймера, значение которого для разных компьютеров может несколько различаться. Узнать это разрешение можно, используя функцию GetSystemTimeAdjustment:

    BOOL GetSystemTimeAdjustment(
        PDWORD lpTimeAdjustment, // size, in 100-nanosecond units, of a periodic time adjustment
        PDWORD lpTimeIncrement, // time, in 100-nanosecond units, between periodic time adjustments
        PBOOL lpTimeAdjustmentDisabled // whether periodic time adjustment is disabled or enabled
       );


       Разберем эту функцию для использования в Delphi. В lpTimeIncrement записывается значение разрешения системного таймера в единицах по 100 наносекунд. Нам нужно получить это значение, и вывести его, к примеру, в миллисекундах. Получится такая программка (см. пример 1):

    program SysTmrCycle;

    {$APPTYPE CONSOLE}

    uses
      SysUtils, windows;

      var a,b:DWORD; c:bool;
    begin
      GetSystemTimeAdjustment(a,b,c);
      WriteLn('System time adjustment: '+FloatToStr(b / 10000)+' ms.');
      WriteLn;
      Writeln('Press any key for an exit...');
      Readln;
    end.


       Результат исполнения выводится на экран, у меня значение таймера оказалось равным 10,0144 миллисекунд.

       Что реально означает эта величина? То, что временные интервалы функций будут практически всегда кратны этой величине. Если это 10,0144 мс, то функция sleep(1000) вызовет задержку в 1001,44 мс. При вызове же sleep(5) задержка будет примерно 10 мс. Стандартный таймер Delphi, объект TTimer, естественно подвержен погрешности, но в еще большей степени. Объект TTimer основан на обычном таймере Windows, и посылает окну сообщения WM_TIMER, которые не являются асинхронными. Эти сообщения ставятся в обычную очередь сообщений приложения и обрабатываются, как и все остальные. Кроме того, WM_TIMER обладает самым низким приоритетом (исключая WM_PAINT), по отношению к другим сообщениям. GetMessage отправляет на обработку сообщение WM_TIMER лишь тогда, когда приоритетных сообщений в очереди больше не остается – сообщения WM_TIMER могут задерживаться на значительное время. Если время задержки превышает интервал, то сообщения объединяются вместе, таким образом, происходит еще и их утрата [1].
       Для того чтобы хоть как то производить замеры для сравнительного анализа функций задержки, необходим инструмент, позволяющий точно измерять временные интервалы выполнения некоторого участка кода. GetTickCount не подойдет ввиду вышеописанного. Но автор узнал об возможности опираться на частоту тактов процессора, за некоторый интервал времени. Начиная с Pentium III, процессоры обычно содержат достаточно доступный программистам счетчик меток реального времени, Time Stamp Counter, TSC, представляющий собой регистр на 64 разряда, содержимое которого с каждым тактом процессора инкрементируется [2]. Счет в счетчике начинается с нуля каждый раз при старте (или аппаратном сбросе) ЭВМ. Получить значение счетчика в Delphi можно следующим образом (см. пример 2):

    program rdtsc_view;

    {$APPTYPE CONSOLE}

    uses
      SysUtils, windows;

    function tsc: Int64;
    var ts: record
     case byte of
      1: (count: Int64);
      2: (b, a: cardinal);
     end;
    begin
     asm
      db $F;
      db $31;
      mov [ts.a], edx
      mov [ts.b], eax
     end;
     tsc:=ts.count;
    end;

    begin
     repeat WriteLn(FloatToStr(tsc)) until false;
    end.


       Здесь ассемблерная вставка помещает результат счетчика в регистры edx и eax, значение которых затем переносится в ts, откуда доступно как ts.count типа Int64. Приведенная программа непрерывно выводит в консоль значения счетчика. На некоторых версиях Delphi есть готовая команда rdtsc (read time stamp counter), позволяющая сразу получить значение счетчика функцией RDTSC [3] вот так:

    function RDTSC: Int64; register;
    asm
     rdtsc
    end;


       Предположим, у нас есть значение счетчика, но как использовать его? Очень просто. Опираясь на то, что значение изменяется с постоянной частотой можно вычислять разницу в количестве тактов процессора после исследуемой команды и до нее:

    a:=tsc;
    Command;
    b:=tsc-a;


       В b будет число тактов процессора, прошедшее за время исполнения Command. Но тут есть один момент. Вызов tsc, дающий нам число тактов сам должен тоже затрачивать на это какое то количество тактов. И, для верности результата, его нужно вносить, как поправку, вычитаемую из полученного количества тактов:

    a:=tsc;
    C:=tsc-a;
    a:=tsc;
    Command;
    b:=tsc-a-C;


       Все бы ничего, но экспериментально получается, что иногда значения нашей поправки C различаются. Причина этого была найдена. Дело тут в особенности функционирования процессора, точнее его конвейера. Продвижение машинных инструкций по конвейеру связано с рядом принципиальных трудностей, в случае каждой из них конвейер простаивает. Время выполнения инструкции в самом лучшем случае определяется пропускной способностью конвейера. Промежуток времени, которому можно гарантированно верить, получая такты процессора – от 50 тактов [2]. Получается, что в случае определения поправки, самым точным значением будет минимальная величина. Экспериментально достаточно производить вызов функции поправки до 10 раз:

    function calibrate_runtime:Int64;
    var i:byte; tstsc,tetsc,crtm:Int64;
    begin
     tstsc:=tsc;
     crtm:=tsc-tstsc;
     for i:=0 to 9 do
      begin
       tstsc:=tsc;
       crtm:=tsc-tstsc;
       if tetsc<crtm then crtm:=tetsc;
      end;
     calibrate_runtime:=crtm;
    end;


       Теперь, когда у нас есть необходимый инструмент, поэкспериментируем с функциями задержки. Начнем со всем известной и всеми применяемой sleep:

    procedure Sleep(milliseconds: Cardinal); stdcall;

       Чтобы провести проверку точности задержки, включим в нашу консольную программу, кроме кода tsc и кода calibrate_runtime следующий код:

    function cycleperms(pau_dur:cardinal):Int64;
    var tstsc,tetsc:Int64;
    begin
     tstsc:=tsc;
     sleep(pau_dur);
     tetsc:=tsc-tstsc;
     cycleperms:=(tetsc-calibrate_runtime) div pau_dur;
    end;


       Этот код мы вызовем из программы, задавая по нескольку раз разные значения pau_dur (паузы).Если вы обратили внимание, число тактов за время паузы затем делится на значение паузы. Так мы узнаем точность задержки в зависимости от ее времени. Для удобства проведения теста и вывода на экран/сохранения результата теста применен такой код (см. пример 3):

    var test_result,temp_result:string; n:cardinal; i:byte; aver,t_res:Int64; res:TextFile;
    begin
     WriteLn('The program will generate a file containing the table of results of measurements of quantity of cycles of the processor in a millisecond. Time of measurement is chosen'+' miscellaneous, intervals: 1, 10, 100, 1000, 10000 ms. You will see distinctions of measurements. If an interval of measurement longer - results will be more exact.');
     WriteLn;
     Writeln('Expected time of check - 1 minute. Press any key for start of the test...');
     ReadLn;
     temp_result:='Delay :'+#9+'Test 1:'+#9+'Test 2:'+#9+'Test 3:'+#9+'Test 4:'+#9+'Test 5:'+#9+'Average:';
     n:=1;
     test_result:=temp_result;
     WriteLn(test_result);
     while n<=10000 do
      begin
       temp_result:=IntToStr(n)+'ms'+#9;
       aver:=0;
       for i:=1 to 5 do
        begin
         t_res:=cycleperms(n);
         aver:=aver+t_res;
         temp_result:=temp_result+IntToStr(t_res)+#9;
        end;
       WriteLn(temp_result+IntToStr(aver div 5));
       test_result:=test_result+#13+#10+temp_result+IntToStr(aver div 5);
       n:=n*10;
      end;
     WriteLn;
     AssignFile(res,'TCC_DEF.xls');
     ReWrite(res);
     Write(res,test_result);
     CloseFile(res);
     WriteLn('The test is completed. The data are saved in a file TCC_DEF.xls.');
     Writeln('Press any key for an exit...');
     ReadLn;
    end.


       В нем мы исполняем cycleperms по пять раз для каждого временного интервала (от 1 до 10000 миллисекунд), а также считаем среднее значение. Получается таблица. Итак, полученные числа тактов процессора в ходе такого исследования:
    TCC_DEF

       Картину мы наблюдаем не самую лучшую. Поскольку частота процессора  примерно 1778,8 МГц (см. пример 4), то значения тактов за 1 миллисекунду должны стремиться к приблизительному числу 1778800. Точность функции sleep не дает нам этого ни за 1, 10, 100 или 1000 миллисекунд. Только за десятисекундный промежуток времени значения близки. Пожалуй, если бы в тесте 4 не было 1781146, то усредненная величина была бы приемлемой.
       Что можно сделать? Оставить функцию и рассмотреть что-то еще? Пока не стоит торопиться. Я узнал, что можно вручную задавать погрешность отсчета эталонного интервала времени, используя функцию timeBeginPeriod [2]:

    MMRESULT timeBeginPeriod(
        UINT uPeriod
       );


       Для поддержания такого высокоточного разрешения используются дополнительные системные ресурсы, поэтому нужно вызывать timeEndPeriod для их высвобождения по завершению всех операций. Код функции cycleperms для исследования такого sleep (см. пример 5):

    function cycleperms(pau_dur:cardinal):Int64;
    var tstsc,tetsc:Int64;
    begin
     timeBeginPeriod(1);
     sleep(10);
     tstsc:=tsc;
     sleep(pau_dur);
     tetsc:=tsc-tstsc;
     timeEndPeriod(1);
     cycleperms:=(tetsc-calibrate_runtime) div pau_dur;
    end;


       Еще есть малообъяснимая особенность, timeBeginPeriod(1), устанавливающая разрешение в 1 миллисекунду начинает давать эффект не сразу, а только после вызова sleep, поэтому в код, после timeBeginPeriod вставлено sleep(10). Результаты этого исследования:
    TCC

       Наблюдаемые данные гораздо лучше. Среднее значение за 10 секунд довольно точно. Среднее за 1 миллисекунду отличается от него всего на 1,7 %. Соответственно отличия за 10 мс составляет 0,056 %, за 100 мс – 0,33 % (странно вышло), за 1000 мс – 0,01 %. Меньший, чем 1 мс интервал, невозможно использовать в sleep. Но можно твердо сказать, что sleep годна для пауз в 1 мс при условии выполнения timeBeginPeriod(1), и точность sleep только растет с ростом задаваемого временного промежутка (см. пример 6).

       Функция sleep основана на Native API функции NtDelayExecution, которая имеет следующий вид [5]:

    NtDelayExecution(
      IN BOOLEAN              Alertable,
      IN PLARGE_INTEGER       DelayInterval );


       Попробуем провести тест ее задержек, подобно sleep, но учитывать будет она даже микросекунды:

    function cyclepermks(pau_dur:Int64):Int64;
    var tstsc,tetsc,p:Int64;
    begin
     p:=-10*pau_dur;
     tstsc:=tsc;
     NtDelayExecution(false,@p);
     tetsc:=tsc-tstsc;
     cyclepermks:=(tetsc-calibrate_runtime) *1000 div pau_dur;
    end;


       Эта функция не прописана в windows.pas или ином другом файле, потому вызовем ее, добавив строку:

    procedure NtDelayExecution(Alertable:boolean;Interval:PInt64); stdcall; external 'ntdll.dll';

       Код, в котором мы вызываем функцию и строим таблицу результатов, следует подкорректировать вот так (см. пример 7):

    var test_result,temp_result:string; n:Int64; i:byte; aver,t_res:Int64; res:TextFile;
    begin
     WriteLn('The program will generate a file containing the table of results of measurements of quantity of cycles of the processor in a mikrosecond. Time of measurement is chosen'+' miscellaneous, intervals: 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000 mks. You will see distinctions of measurements. If an interval of measurement longer - results will be more exact.');
     WriteLn;
     Writeln('Expected time of check - 1 minute. Press any key for start of the test...');
     temp_result:='Delay :'+#9+'Test 1:'+#9+'Test 2:'+#9+'Test 3:'+#9+'Test 4:'+#9+'Test 5:'+#9+'Average:';
     n:=1;
     test_result:=temp_result;
     WriteLn(test_result);
     while n<=10000000 do
      begin
       temp_result:='10^'+IntToStr(length(IntToStr(n))-1)+'mks'+#9;
       aver:=0;
       for i:=1 to 5 do
        begin
         t_res:=cyclepermks(n);
         aver:=aver+t_res;
         temp_result:=temp_result+IntToStr(t_res)+#9;
        end;
       WriteLn(temp_result+IntToStr(aver div 5));
       test_result:=test_result+#13+#10+temp_result+IntToStr(aver div 5);
       n:=n*10;
      end;
     WriteLn;
     AssignFile(res,'TCC_NTAPI.xls');
     ReWrite(res);
     Write(res,test_result);
     CloseFile(res);
     WriteLn('The test is completed. The data are saved in a file TCC_NTAPI.xls.');
     Writeln('Press any key for an exit...');
     ReadLn;
    end.


       После проведения исследования задержек, создаваемых NtDelayExecution получились интересные результаты:
    TCC_NTAPI

       Видно, что применять такую точность этой функции бесполезно на промежутках менее 1 миллисекунды. Прочие интервалы задержек несколько лучше, чем у sleep без измененного разрешения, но хуже, чем с высоким разрешением sleep (в принципе это понятно, ведь тут мы не создавали потоков с повышенным приоритетом, и вообще не делали ничего для повышения точности, подобно тому, как это делает timeBeginPeriod). А если добавить timeBeginPeriod? Посмотрим, что получится:
    NTAPI2

       На микросекундных интервалах ситуация все та же. А вот на интервалах, начиная с 1 миллисекунды отличие, относительно 10-секундного значения составляет 0,84 %, что лучше аналогичного использования sleep (1,7 %) –  NtDelayExecution дает задержку точнее.
       При поиске средств программирования задержек в исполнении кода был найден еще один вариант [4], вроде бы предоставляющий возможность указывать интервал в микросекундах. Это WaitableTimer. Работать с ним можно через функции CreateWaitableTimer, SetWaitableTimer, WaitForSingleObjectEx. Вид процедуры cyclepermks, куда мы добавили WaitableTimer:

    function cyclepermks(pau_dur:Int64):Int64;
    var tstsc,tetsc,p:Int64; tmr:cardinal;
    begin
     tmr:=CreateWaitableTimer(nil, false, nil);
     p:=-10*pau_dur;
     tstsc:=tsc;
     SetWaitableTimer(tmr, p, 0, nil, nil, false);
     WaitForSingleObjectEx(tmr, infinite, true);
     CloseHandle(tmr);
     tetsc:=tsc-tstsc;
     cyclepermks:=(tetsc-calibrate_runtime2) *1000 div pau_dur;
    end;


       Особенность применения WaitableTimer требует от нас также модификации расчета поправки, получаемой в calibrate_runtime:

    function calibrate_runtime2:Int64;
    var i:byte; tstsc,tetsc,crtm, p:Int64; tmr:cardinal;
    begin
     tstsc:=tsc;
     crtm:=tsc-tstsc;
     for i:=0 to 9 do
      begin
       tmr:=CreateWaitableTimer(nil, false, nil);
       p:=0;
       tstsc:=tsc;
       SetWaitableTimer(tmr, p, 0, nil, nil, false);
       CloseHandle(tmr);
       crtm:=tsc-tstsc;
       if tetsc<crtm then crtm:=tetsc;
      end;
     calibrate_runtime2:=crtm;
    end;


       Ведь SetWaitableTimer и CloseHandle тоже исполняются за период учитываемого нами количества тактов процессора. Сразу добавим в код cyclepermks вызов timeBeginPeriod, надеясь на помощь этой процедуры в приросте точности (см. пример 8). Таблица результатов:
    TCC_WFSO

       Увы, и здесь мы не получили возможность устанавливать задержки для промежутков меньше миллисекундных. Разница значений 1 миллисекунды и 10 секунд равна 5%. В сравнении с предыдущими способами, это хуже.
       Перед тем, как делать выводы, скажу немного о собственно самом измерении времени. В приведенных исследованиях основой сравнений было число тактов процессора и у каждого компьютера оно разное. Если понадобится привести его к единицам времени на основе секунд, то нужно сделать следующее: применяя 10-секундную задержку NtDelayExecution получить число тактов процессора за эти 10 секунд или узнать длительность одного такта (см. пример 9). Зная количество тактов процессора в единицу времени, можно спокойно преобразовывать меньшие значения числа тактов процессора в значения времени. Кроме этого рекомендуется установить приложению приоритет реального времени.

       Заключение. В результате проведенной работы было установлено, что можно очень точно (даже до отрезка времени, исчисляемого 50 тактами процессора) замерять время на ЭВМ. Эта задача решена успешно. Что же касается возможности самостоятельно задавать точные задержки в исполняемом коде, то тут ситуация такова: лучший обнаруженный метод, позволяет сделать это с разрешением не большим, чем 1 миллисекунда, с погрешностью разрешения на интервале 1 мс порядка 0,84 %. Это функция NtDelayExecution с установкой разрешения процедурой timeBeginInterval. Недостаток функции, по сравнению с оказавшейся менее точной sleep это громоздкий вызов и нахождение в составе недостаточно документированного Native API. Использовать Native API не советуют по причине возможной несовместимости отдельных API в разных операционных системах семейства Windows. В общем, то, очевидное преимущество функции NtDelayExecution все-таки вынуждает сделать выбор в ее пользу.

    Примеры:
     1. Определение разрешения системного таймера
     2. Вывод RDTSC
     3. Задаем интервал через sleep
     4. Узнаем частоту процессора
     5. Задаем интервал через sleep более точно
     6. Исследуем точность установки интервала через sleep на разных значениях
     7. Интервал с помощью NtDelayExecution
     8. Интервал посредством WaitableTimer
     9. Узнаем длительность одного процессорного такта
    Примеры содержат файлы *.dpr исходного кода (на языке Delphi), скомпилированное консольное *.exe приложение и (некоторые) *.xls таблицу уже полученных автором результатов (в формате, поддерживаемом MS Excel). Все примеры – одним файлом.

    Литература:
     1. Руссинович М., Соломон Д. Внутреннее устройство Microsoft Windows. – СПб.: Питер, 2005. – 992 с.
     2. Щупак Ю.А. Win32 API. Эффективная разработка приложений. – СПб.: Питер, 2007. – 572 с.
     3. RDTSC – Wikipedia [http://ru.wikipedia.org/wiki/Rdtsc]
     4. CreateWaitableTimer – MSDN [http://msdn.microsoft.com/en-us/library/ms682492(VS.85).aspx]
     5. NtDelayExecution – RealCoding [http://forums.realcoding.net/lofiversion/index.php/t16146.html]

       Статья была написана 13.11.2009, автор begin_end. Некоторые моменты, рассматриваемые в статье автор обсуждал со slesh’ем, которому выражается благодарность за такую помощь.
    Support the author
    Share post

    Similar posts

    AdBlock has stolen the banner, but banners are not teeth — they will be back

    More
    Ads

    Comments 65

      +7
      Почему не QueryPerformanceCounter()?
      +3
      На мой взгляд, само получение значения tsc затратит меньше процессорных тактов и данные получатся точнее (как вместо sleep я использовал NtDelayExecution. Еще мелочь, но QueryPerformanceCounter возвратил мне 0 на системе PIII с win2k, а tsc — нормальное значение.
      Хотя, я не против, конечно QueryPerformanceCounter, QueryPerformanceFrequency также вполне можно использовать.

      Я еще поэкспериментирую с применением этих функций.
        –2
        Почему на ум приходят постоянная Планка и принцип неопределённости Гейзенберга?)
          +2
          пока у вашей операционной системы не появится real-time scheduler говорить о каких бы то ни было точных измерениях времени бесполезно… thread/process switch отжирает драгоценное время, обработка прерываний происходит в ущерб программам уровня пользователя. опираться на полученные результаты нет смысла. единственное, что гарантируют все эти функции — процесс проспит не меньше указанного интервала времени. под различной нагрузкой в реальных приложениях все это треснет по швам к сожалению
            +2
            Дополнительная информация:
            1. На некоторых процессорах значение RDTSC может быть рассинхронизировано для разных ядер. Вы никогда не знаете, на каком именно ядре в данный момент выполняется Ваша программа (если только не выставили affinity mask, да и то, я бы не был на 100% уверен), поэтому чтение TSC процессорной командой иногда само вносит существенные искажения. В SMP системах RDTSC вообще по определению рассинхронизировано между процессорами. Поэтому думаю, что performance counters лучший метод для точных замеров времени в прикладных программах для Windows.
            2. Программу для Windows не стоит писать так, чтобы она зависела от задержек в функциях типа Sleep. Если речь идет об управлении устройством в реальном времени, то нужно выносить все это в драйвер. В других случаях использовать мультимедийные функции для таймирования. А если кто-то пишет часы для desktop-а – вероятно лучше всего подойдут waitable timers.
              0
              Используя для оптимизации кода профайлер из состава библиотеки Jedi, позволил себе исследовать его с целью определения не только общего времени выполнения блока кода, количества запусков и, соответственно, среднего времени операции, но и максимального времени выполнения одиночной операции. Обнаружилось нечто интересное — для блоков кода со средним временем выполнения менее 1 мс значение максимального времени давало часто 16 мс, а то и 32. Особенно когда общее время выполнения при сотнях и тысячах запусков как раз получается те же 16 или 32 мс.

              В том профайлере для замера временных отсечек используется функция GetTickCount. Не подскажете, чем заменить ее для более точного отсчета малых интервалов?
                0
                BOOL WINAPI QueryPerformanceCounter(
                __out LARGE_INTEGER *lpPerformanceCount
                );
              0
              denim, все так, но описанное хотя бы чуть-чуть поднимает и пробует решить проблему…

              sysprg, к сожалению не было возможности проверить и убедиться в преимуществах QueryPerformanceCounter на многоядерных системах, но судя по сказанному Вами, его действительно лучше использовать. Прежде всего я искал способ получить задержки, и для эксперимента, в котором RDTSC служил для подсчета, он пока мне сгодился.
              А задержки эти, увы с нужным интервалом не получил: тоже верно — нужен драйвер. Хотя если задержки до 1 мс, можно обойтись, как я описал (NtDelayExecution, sleep, таймеры).
                0
                Даже если выставить real-time приоритет для thread, то все равно из-за подкачки/откачки страниц или еще каких-то влияний ядро Windows не всегда мгновенно активирует пользовательскую программу, поэтому писать на прикладном уровне для Windows что-то зависящее от задержек в 1 миллисекунду и меньше — вообще неправильно и малореально. Как и для Linux. С такими вещами нужно или на уровне ядра работать, или использовать специализированные системы реального времени. Как минимум в Windows или в Linux нужно выключать своппинг и не запускать в параллель всякие мультимедийные программы. Но это уже превращает программу в пригодную к использованию только на выделенной машине. Вывод — не очень подходят ОС общего назначения для этого, на прикладном уровне…
                  +2
                  В любом случае спасибо за интересную статью! Можно убрать накопление погрешности, это да. Но нельзя обеспечить четкую активацию в точное время с точностью до миллисекунд. Если Вы запустите Ваши программы с любым методом точных задержек параллельно с какой-нибудь ресурсо-жрущей игрушкой, то Вы увидите, что какой бы Вы не использовали метод задержки и какой бы приоритет для thread не выбрали, но время от времени будут происходить задержки в активации thread более чем на одну миллисекунду из-за свопинга или из-за того, что некоторые directx операции не прерываются ядром, и обеспечить мгновенную активацию прикладной программы для Windows в суб-миллисекундном диапазоне в общем случае невозможно.
                  0
                  Очень интересное исследование, спасибо! Срочно ищу по всем юнитам проекта, не используются ли где-либо малые задержки…
                  • UFO just landed and posted this here
                    • UFO just landed and posted this here
                        +1
                        профилирование программ

                        • UFO just landed and posted this here
                            +2
                            всё зависит от конечной потребности;
                            иногда бывает что счёт идёт на такты, но rdtsc особо верить нельзя ± 50-100 тактов(pipe-line, out-of-order execution);
                            но да почти всегда достаточно и QueryPreformanceCounter/QueryPerformanceFrequency, которые работают через ZwQueryPerformanceCounter;
                        0
                        Далеко не все промышленные системы являются «тру» real-time.
                        Однако сертификацию им проходить все равно требуется.
                        И это лишь один из примеров.
                        • UFO just landed and posted this here
                            –1
                            В большинстве штатных ситуаций они, все-таки, предсказуемы.
                            Другое дело, что гарантий никаких нет 8)
                            • UFO just landed and posted this here
                                0
                                Это вы несколько путаете — теорию и реальную жизнь.
                                При бюджете, например, в 50 тысяч никто не будет вам разрабатывать систему реального времени, снимающую показания с измерительных приборов и отражающих их на индикаторах, за которыми наблюдает диспетчер. Есть ГОСТы, регламентирующие время от изменения показателя до отражения этого изменения, и программа(к слову, написанная на Delphi) с этим справляется — за несколько лет никаких проблем. Это я и называю «предсказуемость в штатной ситуации» — если система даже не real-time, то можно увеличить ее предсказуемость, снижая количество влияющих на нее факторов.
                                Можно сколько угодно пыжиться и надувать щеки в стиле «а вот если вдруг...», «гарантий нет, что ....» — факт в том, что есть решение, устраивающее заказчика(и реально работающее), и по устраивающей разработчика стоимости.
                                • UFO just landed and posted this here
                                    0
                                    Вообще — по-разному.
                                    Но в общем случае — приходят со счетчика с датой, временем и измерением.
                                    • UFO just landed and posted this here
                                        0
                                        Да по-разному. Есть такие, которые с COM-порта, есть такие, которые по Ethernet-у бегают(с TCP в качестве транспорта).
                                  • UFO just landed and posted this here
                                      0
                                      1. Его(измерения) появление в БД
                                      2. Его появление на индикаторе, на который смотрит диспетчер
                                      • UFO just landed and posted this here
                                          0
                                          1. Ну, БД бывают разные. В данном случае, их несколько, но в данном случае важна лишь одна — ин-мемори, без транзакций. Часть данных являются «высокоприоритетными» — при заполнении их определенными значениями, срабатывают определенные алгоритмы обратной связи(грубо говоря — скакнул ток — сработала защита).
                                          2. Странно, да. Но при сертификации этот фактор не учитывают. Есть отраслевые требования, задокументированные приказом таким-то, хотите получить сертификат — демонстрируйте соответствие.
                                          • UFO just landed and posted this here
                                              0
                                              Многовато вопросов задаете, мы уже в сторону ушли 8)
                                              Отвечаю: до индикации время измеряется другой софтиной — эмулятором индикатора, где записываются временные метки значений. Потом достаточно сравнить их с теми, что приходят со счетчика. Ну а время синхронизируется с GPS-приемником.

                                              Неточностей в связи со всем этим — море. Когда я ковырялся с GPS, вскрылись такие бездны «гуляния» системного таймера виндов, что хватались за голову — для обычного процесса там и речи нет даже о точности более чем в 0.1 с. При помощи таких вот статей все эти неточности можно несколько уменьшить. И иногда и это «несколько» может хорошо выручить — будь подобная(только более развернутая и с рассмотрением других вариантов) статья у меня под рукой пару-тройку лет назад — здорово бы сэкономил свое время. Умный, как известно, предпочитает учиться на чужих ошибках.

                                              Real-time систему не на спецжелезе со спецосью не построишь, это факт. Но далеко не всегда требуется настоящий real-time.
                                              • UFO just landed and posted this here
                                                  0
                                                  Ну, архитектуру мудрил не я. Разработчикам далеко не всегда дают требуемые сроки и возможность делать «как надо бы по-хорошему», вы не знали? 8)
                                                  Я еще раз говорю — вы слишком привязаны к теории. В жизни оно по-разному складывается.

                                                  >В чем проблема? Запросто!

                                                  AVR-ка, мигающая светодиодами? 8))

                                                  Проблем разных много, их сложно охватить в рамках дискуссии в комментариях, да и не силен я в матчасти.
                                                  • UFO just landed and posted this here
                                                      0
                                                      >Видел ПО

                                                      Я сам писал — чувствуете разницу?

                                                      >для управление производственной линией под DOS/4GW

                                                      DOS тоже отвечает не всем требованям стандартов Real-time ОС. Геморы с драйверами, отсутствие многозадачности, как следствие — отсутствие вменяемого IPC.
                                                      • UFO just landed and posted this here
                                                          0
                                                          Главное требование для hard real-time — гарантированный результат операции за четко определенный промежуток времени. DOS этого не гарантирует.
                                                          • UFO just landed and posted this here
                                                              0
                                                              С каких это пор там что-то гарантировано?
                                                              А если, например, возникнет прерывание?
                                                              Или CLI и побежали? Далеко не убежите 8)
                                                              • UFO just landed and posted this here
                                                                  0
                                                                  По-моему, вы несколько заблуждаетесь относительно DOS'а.
                                                                  • UFO just landed and posted this here
                                                                      0
                                                                      Ну на этом тогда и закончим.
                                                                      С вопросами веры — это к батюшкам в церковь 8)
                                                                      • UFO just landed and posted this here
                                                                          0
                                                                          Во-первых, RTKernel по сути практически заменяет DOS(чего разработчики и не отрицают, допуская установку на комп вообще без оси).
                                                                          Во-вторых, я же вроде изначально говорил о том, что реальная жизнь вносит коррективы. RTKernel, например, сам по себе стоит 1000 евро. А где вы найдете под него разработчика, если речь не про Москву? Сколько такой разработчик запросит денег? А как вы будете поддерживать свой продукт? А если требуется возможность багфикса через интернет?
                                                                          В-третьих, даже с RTKernel вы получаете soft real-time, а никак не hard real-time. Т.е. от простой программы под Windows оно будет отличаться только тем, что тайминги будут намного(но все же не бесконечно много) надежнее. Гарантии все равно не будет.
                                                                          • UFO just landed and posted this here
                                                                              0
                                                                              >Это не противоречит ни одной моей фразе, сказанной выше.

                                                                              Ну, например, он не совсем «поверх» DOS 8)
                                                                              Да и речь вроде шла на основе DOS'а а не спецсофте — в самом начале ветки я и писал, что спецжелезо и спецсофт требуются для Hard real-time.
                                                                              Кроме того, я не спец в архитектуре x86, вполне возможно, что и там есть затыки.

                                                                              >Это уже другой вопрос, не связанный с «можно ли для DOS сделать realtime-приложение»

                                                                              Не забывайте, что речь началась с вашего вопроса «для чего это нужно». Я объяснил, что soft real-time системы можно строить и на бытовых осях, при определенном подходе.
                                                                              Ну чего вы реально под DOS навертите(при разумных затратах), если, например, вам обрабатывать надо гигабайты данных в час, где-то их складировать, куда-то их пересылатьи т.п.? Десятком/сотней станков порулить — это одно, а вот тысячей управляемых источников данных?

                                                                              >Ниже некуда.

                                                                              А драйвера? А обработчики прерываний?

                                                                              Дискуссия, по-моему, переходит в затяжную. Если у вас есть аккаунт в Wave-предлагаю обсудить там. Если нет, могу прислать инвайт — пишите почту в личку.
                                                                              • UFO just landed and posted this here
                                                                0
                                                                P.S. минусы вам проставляю не я, это кто-то набежал со стороны в нашу ветку 8(
                                                                • UFO just landed and posted this here
                            0
                            Делфи… как давно это было…
                            Отвык уже от begin-end…
                              –1
                              Интересно, насколько он еще используется в реальных проектах?..
                                +1
                                Довольно часто. По ссылке показаны примеры популярных программ, написанных на Delphi: www.embarcadero.com/application-showcase
                                  0
                                  Исчезающе мало.

                                  Смотрим статистику вакансий на Dice.com:
                                  C# — 4800
                                  Java — 10086
                                  Delphi — 53
                                +1
                                Методология измерения не совсем правильная. NtDelayExecution переносит поток из списка плнируемых в список ожидания. Повторный запуск произойдет через заданный интервал + время до следующего планирования + время на смену контектов потока. Поэтому маленькие интервалы никогда не будут правильно измеряны в ОС с планировщиками общего назначения. А при больших интервалах кажущаяся точность появляется потому, что отношение необходимой задержки к накладным расходам становится больше — накладные расходы меньше влияют на результат измерения. При переключении задач может также происходить инвалидация кеша процессора, его заполнение требует время, вероятность велика, если код долго не получал управления из-за того, что был отложен — скорее всего неиспользуемый код был вытеснен.

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

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

                                Также желательно инвалидировать кеши 1 уровня перед замерами и очищать конвеер (например дальним вызовом, только тут может помешать предсказание переходов...).
                                  0
                                  xor eax, eax;
                                  cpuid;
                                  rdtsc;
                                  ;
                                  ;code under investigation
                                  ;
                                  xor eax, eax
                                  cpuid;
                                  rdtsc;
                                    0
                                    Спасибо за хинт :)
                                  +1
                                  1. «На некоторых версиях Delphi есть готовая команда rdtsc»
                                  Это не готовая команда на некоторых версиях Delphi, а наличие поддержки встроенным ассемблером оной команды. В Delphi6 уже есть, в старших тем более.

                                  2. Про разные архитектуры процессоров, многоядерность и нереалтаймность Win32 уже сказали.

                                  3. А какое практическое применение у этого метода? Т.е. когда он действительно дает принципиально лучший результат относительно известных стандартных? Ведь если мы имеем дело с получением некоторых отсчетов от некоторого железа, то удобнее всего (и так делается в реальной жизни) использовать метки времени от самого железа.
                                    –2
                                    Недавно нужна была подобная функциональность. За полчасика набросал базовый модуль для профайлинга, основываясь на QueryPerfomanceCounter. Получилась точность порядка 3 микросекунд (к сожалению, не помню, какой был процессор). Если бы использовать ассемблерные вставки, и дополнительные оптимизации, то, думаю, можно было бы догнать до 1 мкс (а может и еще точнее); но на тот момент мне данной точности было достаточно.
                                    • UFO just landed and posted this here
                                        –1
                                        хороша статья) в мемориз)
                                          0
                                          жаль голоса на сегодня закончились) автор, в случае чего — стучи, плюсануть карму и топик однозначно надо)

                                        Only users with full accounts can post comments. Log in, please.