программируемый интервальный windows




Отсчет времени в операционных системах Windows ведется со старта самой операционной системы и обнуляется при каждой перезагрузке. Существует специальная 64 разрядная системная переменная, хранящая стандартное время в миллисекундах. Доступ к ней может осуществляться через целый ряд функций:

GetCurrentTime()

timeGetTime()

GetTickCount()

Все они возвращают время со старта Windows в миллисекундах.

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

Использование функций возможно только при работе «опросом», посылка сообщений невозможна, поэтому для определения интервалов времени необходимо вызывать одну из функций до и после выполнения исследуемого участка программы (или в начале/конце цикла работы программы).

Функции, возвращающие стандартное время Windows, используются, главным образом, в мультимедиа приложениях (в особенности в играх), где критическим параметром является быстродействие, а не точность.

 

Функции точного определения времени

 

Для измерения отрезков времени с точностью, превышающей интервал системного таймера, используются иные функции:()()

Функции являются функциями ядра Windows и позволяют измерять отрезки времени с точностью выше 1 миллисекунды. Аппаратно они опираются на дополнительный счетчик, расположенный на материнской плате.

В качестве параметров функциям передаются указатели на 64 разрядные целочисленные переменные, в которые функции помещают результат своей работы. Для функции QueryPerformanceCounter() это время с начала работы Windows в относительных единицах, а для QueryPerformanceFrequency() - количество относительных единиц в миллисекунде.

Для получения точного стандартного времени Windows необходимо разделить результат работы функции QueryPerformanceCounter() на результат QueryPerformanceFrequency().

Использование указанных процедур дает результат на порядки точнее, чем функции, опирающиеся на системный таймер. Это позволяет использовать их в задачах, критичных к точности и разрешающей способности таймера (даже в задачах профилирования программ), о чем заявляет сама Microsoft. Однако надо иметь в виду, что выполнение функций QueryPerformanceFrequency() и QueryPerformanceCounter() занимает достаточно большое время и количество их вызовов необходимо свести к минимуму или вообще отказаться по возможности в пользу менее точных методов.

 

Описание алгоритмов программ

 

В ходе курсовой работы написаны две программы, реализующие измерение интервалов времени с использованием процедур GetTickCount() и QueryPerformanceCounter().

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

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

Для простоты написания второй программы на основе функций QueryPerformanceCounter() и QueryPerformanceFrequency() написана процедура с интерфейсом, аналогичным интерфейсу GetTickCount(), что позволило почти не изменять исходной программы, лишь изменив название процедур и добавив необходимые фрагменты. В связи с этим в листинге вторая программа не приводится, показаны лишь её фрагменты, отличные от первой.

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

Для расчетов интервалов времени созданы 2 глобальные переменные: время старта программы и время последнего цикла. Первая переменная заполняется при старте и далее не изменяется. Вторая переменная заменяется в каждом цикле работы программы на текущее время, что позволяет на следующем цикле сравнивать его со временем со старта Windows и получать длину одного цикла программы.

 

Листинг программ

 

Текст первой программы.

386flat,stdcallcasemap:none\masm32\include\windows.inc\masm32\include\user32.inc\masm32\lib\user32.lib\masm32\include\kernel32.inc\masm32\lib\kernel32.lib\masm32\include\winmm.inc\masm32\lib\winmm.lib

Прототипы некоторых функцийproto:DWORD,:DWORD,:DWORD,:DWORD;Гланая процедура

StrConv proto:DWORD

;Конвертирование числа в строку

Delay proto

;Задержкаdb "SimpleWinClass",0

;Имя класса окнаdb "timeGetTime",0

;Имя окна db "╘шъёрЎш тЁхьхэш т√яюыэхэш яЁюуЁрьь√.",0;Строки текста, db "╠хЄюф timeGetTime",0;выводимого в окнеS db "┬Ёхь ёю ёЄрЁЄр Windows",0S db "┬Ёхь ёю ёЄрЁЄр яЁюуЁрьь√",0

Time3S db "┬Ёхь Ўшъыр",0" ",0;Строка, в которую записывается

;число при конвертированииDWORD 0;;Время старта программы DWORD 0;;Время выполнения предыдущего цикла

.DATA?; Hеиницилизиpуемые данныеHINSTANCE?; Хэндл нашей пpогpаммыLPSTR?; Командная строка

;---------------------------------------------------------------------------

;Кодовый сегмент-

;----------------

.CODE; Сегмент кода:GetModuleHandle, NULL;Взять хэндл пpогpаммы

mov hInstance,eax

invoke GetCommandLine;Взять командную стpоку.CommandLine,eax

;вызвать основную функцию

invoke WinMain, hInstance,NULL,CommandLine, SW_SHOWDEFAULTExitProcess, eax; Выйти из пpогpаммы.

;---------------------------------------------------------------------------

;Основной цикл программы-

;------------------------proc hInst:HINSTANCE,hPrevInst:HINSTANCE,CmdLine:LPSTR,CmdShow:DWORDwc:WNDCLASSEX;Класс окнаmsg:MSG;Сообщениеhwnd:HWND;Хэндл окнаtimeGetTime;Получаем время старта программы

mov StartT,eax;и запоминаем его LastT,eax wc.cbSize,SIZEOF WNDCLASSEX;заполнение стpуктуpы

mov wc.style, CS_HREDRAW or CS_VREDRAW;класса окна

mov wc.lpfnWndProc, OFFSET WndProc

mov wc.cbClsExtra,NULL

mov wc.cbWndExtra,NULL hInstance wc.hInstance wc.hbrBackground,COLOR_WINDOW+1 wc.lpszMenuName,NULL wc.lpszClassName,OFFSET ClassNameLoadIcon,NULL,IDI_APPLICATION wc.hIcon,eax wc.hIconSm,eaxLoadCursor,NULL,IDC_ARROW wc.hCursor,eaxRegisterClassEx, addr wc

;pегистpация класса окнаCreateWindowEx,NULL,\ClassName,\AppName,\_OVERLAPPEDWINDOW,\_USEDEFAULT,\_USEDEFAULT,\

,\

,\,\,\,\ hwnd,eaxShowWindow, hwnd,CmdShow;отобpазить окно

invoke UpdateWindow, hwnd;обновить клиентскую область

.WHILE TRUE;Основной циклGetMessage, ADDR msg,NULL,0,0;получить сообщение

.BREAK.IF (!eax);выходTranslateMessage, ADDR msg;трансляцияDispatchMessage, ADDR msg;сообщенияDelay;задержкаInvalidateRect, hwnd, 0, TRUE;обновление окнаeax,msg.wParam;передаем код окончания

WinMain endp

----------------------------

Оконная процедура-

------------------proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAMhdc:HDC;контекст экранаps:PAINTSTRUCT;структура рисования

LOCAL rect:RECT;прямоугольник вывода текстаTime:DWORD;Текущее времяFStT:DWORD;Время со старта программыDeltaT:DWORD;Время одного цикла

IF uMsg==WM_DESTROY;Закрыть окноPostQuitMessage,NULLuMsg==WM_PAINT;Нарисовать содержимоеBeginPaint,hWnd, ADDR ps;получить дескриптор

mov hdc,eax

mov rect.left,10;Устанавливаем область rect.right,400;вывода текста rect.top,0 rect.bottom,300

;----------------------------

;Выводим простые строки

invoke DrawText, hdc,ADDR Str1,-1, ADDR rect, DT_SINGLELINE rect.top,16DrawText, hdc,ADDR Str2,-1, ADDR rect, DT_SINGLELINE rect.top,32DrawText, hdc,ADDR Time1S,-1, ADDR rect, DT_SINGLELINE rect.top,48DrawText, hdc,ADDR Time2S,-1, ADDR rect, DT_SINGLELINE rect.top,64DrawText, hdc,ADDR Time3S,-1, ADDR rect, DT_SINGLELINEtimeGetTime;Получаем текущее время Time,eax

mov rect.top,32;переходим на второй столбец rect.left,250StrConv, Time;конвертируем и выводим время

invoke DrawText, hdc,ADDR OutStr,-1, ADDR rect, DT_SINGLELINE

mov eax,Time;Вычисляем время со старта ebx,StartT;для этого вычитаем из времени со eax,ebx;старта Windows время старта FStT,eax;программы rect.top,48StrConv, FStT;конвертируем и выводим время

invoke DrawText, hdc,ADDR OutStr,-1, ADDR rect, DT_SINGLELINE

mov eax,Time;Вычисляем длительность одного цикла ebx,LastT;для этого вычитаем из времени со eax,ebx;старта Windows время FStT,eax;предыдущего цикла rect.top,64

invoke StrConv, FStTDrawText, hdc,ADDR OutStr,-1, ADDR rect, DT_SINGLELINE eax,Time; LastT,eax;Сохраняем время текущего циклаEndPaint,hWnd, ADDR ps

.ELSEDefWindowProc,hWnd,uMsg,wParam,lParam

.ENDIFeax, eax

WndProc endp

;-------------------------------

;Конвертирование числа в строку-

;-------------------------------proc Chislo:DWORD ebx, 7;заносим счетчик символов eax, Chislo;конвертируемое число ecx, 8;счетчик итераций: edx,eax;копируем текущее число eax,4;Смещаем число edx,15;;Выделяем младшую цифру edx,48;Переводим в ASCII edx,57;Если цифра больше 9 NoA edx,7;то корректируем символ: OutStr[ebx],dl;Записываем символ ebx;Смещаем счетчикFCiclendp

;------------------------------

;Процедура задержки-

;-------------------proc;Простой циклecx,4000000; на большое количество

DCycl: loop DCycl;итерацийendp start

Текст второй программы:

.386

.model flat,stdcallcasemap:none\masm32\include\windows.inc\masm32\include\user32.inc\masm32\lib\user32.lib\masm32\include\kernel32.inc\masm32\lib\kernel32.lib\masm32\include\winmm.inc\masm32\lib\winmm.lib

;Прототипы некоторых функцийproto:DWORD,:DWORD,:DWORD,:DWORD;Гланая процедура

StrConv proto:DWORD;Конвертирование числа в строку

Delay proto;Задержкаproto

.DATAdb "SimpleWinClass",0;Имя класса окнаdb "QueryPerformenceCounter",0;Имя окна db "╘шъёрЎш тЁхьхэш т√яюыэхэш яЁюуЁрьь√.",0;Строки текста, db "QueryPerformenceCounter",0;выводимого в окнеS db "┬Ёхь ёю ёЄрЁЄр Windows",0S db "┬Ёхь ёю ёЄрЁЄр яЁюуЁрьь√",0

Time3S db "┬Ёхь Ўшъыр",0 db " ",0;Строка, в которую записывается

;число при конвертированииDWORD 0;;Время старта программы DWORD 0;;Время выполнения предыдущего цикла DWORD 0;;Временная переменная.

.DATA?; Hеиницилизиpуемые данныеHINSTANCE?; Хэндл нашей пpогpаммыLPSTR?; Командная строка

;---------------------------------------------------------------------------

;Кодовый сегмент-

;----------------

.CODE; Сегмент кода:GetModuleHandle, NULL;Взять хэндл пpогpаммы

mov hInstance,eax

invoke GetCommandLine;Взять командную стpоку.CommandLine,eax

;вызвать основную функцию

invoke WinMain, hInstance,NULL,CommandLine, SW_SHOWDEFAULTExitProcess, eax; Выйти из пpогpаммы.

;---------------------------------------------------------------------------

;Основной цикл программы-

;------------------------proc hInst:HINSTANCE,hPrevInst:HINSTANCE,CmdLine:LPSTR,CmdShow:DWORDwc:WNDCLASSEX;Класс окнаmsg:MSG;Сообщениеhwnd:HWND;Хэндл окнаMyTimer;Получаем время старта программы

mov StartT,eax;и запоминаем его LastT,eax wc.cbSize,SIZEOF WNDCLASSEX;заполнение стpуктуpы

mov wc.style, CS_HREDRAW or CS_VREDRAW;класса окна

mov wc.lpfnWndProc, OFFSET WndProc

mov wc.cbClsExtra,NULL

mov wc.cbWndExtra,NULL hInstance wc.hInstance wc.hbrBackground,COLOR_WINDOW+1 wc.lpszMenuName,NULL wc.lpszClassName,OFFSET ClassNameLoadIcon,NULL,IDI_APPLICATION wc.hIcon,eax wc.hIconSm,eaxLoadCursor,NULL,IDC_ARROW wc.hCursor,eaxRegisterClassEx, addr wc;pегистpация класса окнаCreateWindowEx,NULL,\ClassName,\AppName,\_OVERLAPPEDWINDOW,\_USEDEFAULT,\_USEDEFAULT,\

,\

,\,\,\,\ hwnd,eaxShowWindow, hwnd,CmdShow;отобpазить окно

invoke UpdateWindow, hwnd;обновить клиентскую область

.WHILE TRUE;Основной циклGetMessage, ADDR msg,NULL,0,0;получить сообщение

.BREAK.IF (!eax);выходTranslateMessage, ADDR msg;трансляцияDispatchMessage, ADDR msg;сообщенияDelay;задержкаInvalidateRect, hwnd, 0, TRUE;обновление окна

.ENDWeax,msg.wParam;передаем код окончания

WinMain endp

;-----------------------------

;Оконная процедура-

;------------------proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM

LOCAL hdc:HDC;контекст экрана

LOCAL ps:PAINTSTRUCT;структура рисования

LOCAL rect:RECT;прямоугольник вывода текстаTime:DWORD;Текущее времяFStT:DWORD;Время со старта программыDeltaT:DWORD;Время одного цикла

.IF uMsg==WM_DESTROY;Закрыть окноPostQuitMessage,NULL

.ELSEIF uMsg==WM_PAINT;Нарисовать содержимоеBeginPaint,hWnd, ADDR ps;получить дескриптор

mov hdc,eax rect.left,10;Устанавливаем область rect.right,400;вывода текста rect.top,0 rect.bottom,300

;----------------------------

;Выводим простые строки

invoke DrawText, hdc,ADDR Str1,-1, ADDR rect, DT_SINGLELINE rect.top,16DrawText, hdc,ADDR Str2,-1, ADDR rect, DT_SINGLELINE rect.top,32DrawText, hdc,ADDR Time1S,-1, ADDR rect, DT_SINGLELINE rect.top,48DrawText, hdc,ADDR Time2S,-1, ADDR rect, DT_SINGLELINE rect.top,64DrawText, hdc,ADDR Time3S,-1, ADDR rect, DT_SINGLELINEMyTimer;Получаем текущее время Time,eax

mov rect.top,32;переходим на второй столбец rect.left,250StrConv, Time;конвертируем и выводим время

invoke DrawText, hdc,ADDR OutStr,-1, ADDR rect, DT_SINGLELINE

mov eax,Time;Вычисляем время со старта ebx,StartT;для этого вычитаем из времени со eax,ebx;старта Windows время старта FStT,eax;программы rect.top,48StrConv, FStT;конвертируем и выводим время

invoke DrawText, hdc,ADDR OutStr,-1, ADDR rect, DT_SINGLELINE

mov eax,Time;Вычисляем длительность одного цикла ebx,LastT;для этого вычитаем из времени со eax,ebx;старта Windows время FStT,eax;предыдущего цикла rect.top,64

invoke StrConv, FStTDrawText, hdc,ADDR OutStr,-1, ADDR rect, DT_SINGLELINE eax,Time; LastT,eax;Сохраняем время текущего циклаEndPaint,hWnd, ADDR ps

.ELSEDefWindowProc,hWnd,uMsg,wParam,lParam

.ENDIFeax, eax

WndProc endp

;-------------------------------

;Конвертирование числа в строку-

;-------------------------------proc Chislo:DWORD ebx, 7;заносим счетчик символов eax, Chislo;конвертируемое число ecx, 8;счетчик итераций: edx,eax;копируем текущее число eax,4;Смещаем число edx,15;; Выделяем младшую цифру edx,48;Переводим в ASCII edx,57;Если цифра больше 9 NoA edx,7;то корректируем символ: OutStr[ebx],dl;Записываем символ ebx;Смещаем счетчикFCiclendp

;------------------------------

;Процедура задержки-

;-------------------proc;Простой циклecx,4000000;на большое количество

DCycl: loop DCycl;итерацийendp

;------------------------------

;Процедура таймера

;------------------procTimeH:DWORDTimeL:DWORDFrecH:DWORDFrecL:DWORDQueryPerformanceFrequency, ADDR FrecLQueryPerformanceCounter, ADDR TimeL ebx,FrecL;Заполняем регистры перед делением ecx,FrecH;Frquency ebx,10 edx,TimeH;Время eax,TimeL

pushad

; деление 64-битного числа в EDX:EAX на 64-битное число в ЕСХ:ЕВХ.

; Частное помещается в EDX:EAX, и остаток - в ESI:EDIebp,64; счетчик битesi,esi

xor edi,edi; остаток = 0:

shl eax,1edx,1edi,1; сдвиг на 1 бит влево 128-битного числа

rcl esi,1; ESI:EDI:EDX:EAX

cmp esi,ecx; сравнить старшие двойные словаdividenextedi,ebx; сравнить младшие двойные слова

jb next:edi,ebxesi,ecx; ESI:EDI = EBX:ECX

inc eax; установить младший бит в ЕАХ:ebp; повторить цикл 64 раза

jne bitloop Temp,eax eax,Tempendp

end start

Результаты работы программ:

 

Рис.


Рис.

 

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

 


Вывод

 

В ходе курсовой работы были разобраны основные методы измерения интервалов времени на PC. Были показаны основные аппаратные и программные реализации решения рассматриваемой задачи. Составлены две программы на языке ассемблера для 32 разрядных микропроцессоров семейства Intel, демонстрирующие методы измерения времени и показывающие отличия методов для Windows. К сожалению операционная система Windows не позволяет измерять время с точностью существенно превышающей 1 миллисекунду, что является внутренним ограничением архитектуры системы.



Поделиться:




Поиск по сайту

©2015-2024 poisk-ru.ru
Все права принадлежать их авторам. Данный сайт не претендует на авторства, а предоставляет бесплатное использование.
Дата создания страницы: 2020-03-12 Нарушение авторских прав и Нарушение персональных данных


Поиск по сайту: