Junior
Статус: Не в сети Регистрация: 24.06.2007 Откуда: Москва
Всем привет. На носу диплом.. в процессе разработки зашла в тупик. у меня есть функция для определения тактовой частоты процессора. Нужно с помощью нее получать конкретные временные задержки (привязку нужно сделать через системный таймер). Не могу придумать как это реализовать. Частота получается с помощью RdtSC(если нужно взглянуть на функцию, могу выложить исходник). Очень надеюсь на вашу помощь..
Варианты с функцией Sleep() и подобными не подходят. нужно использовать только системный таймер и частоту МП.
Программа пишется в Delphi7. ОС виста. проц 2 ядра.
Member
Статус: Не в сети Регистрация: 14.01.2004 Откуда: Киев, Украина
1. С помощью rdtsc частоту не посчитать впринципе, нужны показатели какой-нибудь таймера.
2. Что такое в вашем понимании системный таймер? У современных систем существует несколько таймеров: APIC таймер, ACPI таймер (он же PM таймер), HPET, и еще всякие рюшечки. И сказать, что один из них более системный, а другой нет - нельзя 3. Sleep, QueryPerformanceCounter и прочие ф-циюю опираются на HAL, который в свою очередь использует один из системный таймеров.
Сформулируйте вопрос собственно, пришли рассказали и "Очень надеюсь на вашу помощь.. ", извините, телепатов увезли...
type TForm1 = class(TForm) Button1: TButton; procedure Button1Click(Sender: TObject); private { Private declarations } public { Public declarations } end;
var Form1: TForm1;
implementation
{$R *.dfm} var hF, T, sc, et : int64;
function RdTSC : int64; begin asm db $0f, $31 end; end;
function GetCyclesPerSecond:int64; stdcall; begin QueryPerformanceFrequency(hF); QueryPerformanceCounter(T); et := T + hF; sc := RdTSC; repeat QueryPerformanceCounter(T); until (T >= et); Result := RdTSC - sc; end;
procedure TForm1.Button1Click(Sender: TObject); begin GetCyclesPerSecond; end;
end.
вот так я получаю тактовую частоту. как видно, здесь используется ф-ция QueryPerformanceCounter().
вопрос: вышеприведенный код нужно приспособить для формирования малых временных интервалов. Допустим нужно снимать показания какого-то прибора каждые 2 мс. Вот мне надо написать функцию, которая и позволит эти 2 мс отсчитать.
Member
Статус: Не в сети Регистрация: 14.01.2004 Откуда: Киев, Украина
1. rdtsc не нужна, достаточно только QueryPerformanceFrequncy, QueryPerformanceCounter.
2. Толково из юзер мода это не сделать, потому, что для такой задачи нужно ловить прерывания от таймера и смотреть на изменения каунтера, но можно выкрутится.
3. Стартует приложение, создает какой-нибудь Event сброшенный и запускает два потока:
- Один поток будет снимать данные с утройства. Он все время ждет (WaitForSingleObject) когда другой поток просигналит ивент.
- Второй поток запускает цикл, и в нем каждую итерацию проверяет с помошью QueryPerformanceCounter, не прошло ли 2 мс.
Получается очень точно и то, что вам нужно, но проц будет загружен под завязку, так как причина 2.
Запустишь где-нибудь этот таймер и будешь считывать себе каждые 2 мс что там тебе нужно. В моем примере использования этого класса я вывожу на экран "THE END" каждые 100мс.
У тебя проблем с переводом на делфи не будет? Winapi-то один и тот же и типы почти так же называются. Если нужно, то могу переделать.
Advanced member
Статус: Не в сети Регистрация: 10.04.2003 Откуда: Москва
Мысли вслух, из серии 'тихо сам с собою'... Зачем делать 2 треда и этот зоопарк с вызовом Win32? Это долго, в эти моменты вынь может отобрать время и привет родственникам. Я бы сделал 2 вещи: 1. откалибровал TSC 2. юзал один тред. Зная временные отметки TSC не трудно написать таск, который проверяет наступление времени и 'когда настало' выполнял бы свои действия. Т.к. точно известно отставание от сетки запуска, то это можно учесть при работе с устройством (если надо). И вообще, понятия 'запуск через 2 mS' и 'запуск каждые 2mS' принципиально различаются, вынь далеко не realtime система.
Само собой что realtime на windows никогда не будет, и никто не гарантирует, что таймер сработает через 2 мс.
Использование одного потока не дает возможности создания более менее точного таймера, становиться возможным лишь создание такой задержки. А это:
1) Сужает возможность применения.
2) У процесса с одним потоком точно так же могут отобрать процессорное время.
Поэтому не вижу причин не использовать два потока. По моим тестам, написанный мною таймер в обычных условиях всегда откликался в положенные ему 2 мс.
P.S>
Помоему твой пост относиться напрямую к теме и не стоит его помечать оффтопом. Я считаю никогда не лишним разобраться и найти верный путь, каждый может ошибаться.
Advanced member
Статус: Не в сети Регистрация: 10.04.2003 Откуда: Москва
1. ты никогда не сможешь отобрать у меня время внутри кванта. Запрашивая средства выни ты сам напрашиваешься на отбирание времени.
2. тред, который считает время загружает процессор на 100% и если процессоров всего один, то он будет мешать тому таску, который будет обрабатывать устройство.
Ну и где твоя экономия?
Junior
Статус: Не в сети Регистрация: 24.06.2007 Откуда: Москва
2 serj:
мысли кстати правильные. может в виде кода выразишь?
2 sashar2:
вот то что я смогла по образу и подобию твоего примера на делфи переделать. может комментарии какие добавишь? поправки?
Код:
var AWaitTime : Int64; ACurTime, AStartTime : Int64; CPUFrq : Int64; begin QueryPerformanceFrequency(CPUFreq); AWaitTime := CPUFreq / 1000 * Miliseconds; QueryPerformanceCounter(AStartTime); QueryPerformanceCounter(ACurTime); While Not Terminated Do Begin If (ACurTime - AStartTime) > AWaitTime Then Begin AStartTime := ACurTime; If Assigned(FCallBack) Then FCallBack; End; QueryPerformanceCounter(ACurTime); End; end;
тема еще очень актуальна, так что если у кого какие еще соображения, я буду учень рада!
Advanced member
Статус: Не в сети Регистрация: 09.06.2003 Откуда: USSR
Код:
var AWaitTime : Int64; ACurTime, AStartTime : Int64; CPUFrq : Int64; begin QueryPerformanceFrequency(CPUFreq); AWaitTime := CPUFreq / 1000 * Miliseconds; QueryPerformanceCounter(AStartTime); QueryPerformanceCounter(ACurTime); While Not Terminated Do Begin If (ACurTime - AStartTime) > AWaitTime Then Begin AStartTime := ACurTime; If Assigned(FCallBack) Then FCallBack; End; QueryPerformanceCounter(ACurTime); End; end;
Таки мобразом можно зарезать всю производительность. Windows настолько не реалтайм, чтобы точно именно в 2ms делать что-то в user mode. К примеру можно сделать тупо вот так
Код:
procedure TDoMyJob.Execute; begin { Place thread code here } hh:=CreateEvent(nil,false,false,'TEST2MS'); while not Terminated do begin WaitForSingleObject(hh,2); //wait 2ms Synchronize(DoSomtng); end; end;
По крайней мере процессор не будет загружен, но нет никакой уверенности что DoSomtng будет обрабатываться четко каждые 2ms, будут задержки. Я проверял, точность может быть только при ожидании не ниже 200ms
serj Я не говорил про экономию. Если ты можешь реализовать таймер, способный тикать в такие маленькие интвервалы времени, да еще и не загружая процессор под винь, то тебе все только спасибо скажут .
Ну и ресурсы между тредами можно разделить в нужной пропорции, если используется один процессор.
Ray Adams Это решение не подходит, точность невысокая.
ggKk Ммм, что-то код на мой не очень похож , будет время - на делфи перенесу...
Junior
Статус: Не в сети Регистрация: 24.06.2007 Откуда: Москва
sashar2
ну как поняла, так и перевела... жду оригинального перевода от автора
serj, Ray Adams и все все все в данном случае моя цель не экономия! мне не жалко и весь проц под завязку загрузить, главное - точность, и минимальность временного интервала!
вот-с.
Advanced member
Статус: Не в сети Регистрация: 10.04.2003 Откуда: Москва
sashar2 писал(а):
да еще и не загружая процессор под винь
"Нет, сынок, это фантастика" (с)
Если очень хочется, то можно залезть в performance counter's, но и там гиблое дело. Баловался этим хозяйством, но это было на собственном PM32 режиме, а потому не интересно.
Нулевую загрузку можно получить только сидя на аппаратном прерывании.
Если делать без извратов, то все равно выйдет 100% загрузки одного ядра. Если ядер больше одного, то можно поиграть. Еще можно поиграть, если надо получить время выборок порядка 30mS (или перестроить квант времени выни, а это уже не здорово)
ggKk, без ошибок я могу писать только на asm, а тебе это явно не поможет.
Алгоритм тривиальный:
A=rdtsc
Sleep(1000)
B=rdtsc
C=B-A
В "C" имеем скорость процессора. Кстати, хорошо-бы вначале подавить Q&Q.
Если нужна выдержка N mS, то считаем квант TSC: M=C*N/1000
Под ХХХ понимается:
если надо очень маленькое время, то ничего или процедура обработки результатов.
Если время обработки велико, то там можно поставить Sleep(0), а если больше 50mS, то даже Sleep(20). Естественно, что нам надо, чтоб вынь после выполнения других тасков отдала время нам, а не кому-то еще, потому перед этим надо поставить для этого треда realtime. Т.к. мы будем делиться временем, то вынь не зависьнет. (детально на всех OS не проверял)
Для справки - команда Sleep(0) просто переключает таски ... но т.к. у нас очень высокий приоритет, то блуждать она особо не будет.
Под [вызвать обработку] понимается или запуск процедуры (ЧЕРЕЗ ПРЯМОЙ CALL!) или установка какого-либо семоформа.
Если аппаратура требует достаточно четкой реакции, то процедуру съема данных и их обработки необходимо разделить. А именно, по [вызвать обработку] только снимать данные и помещать их в буфер, а обработку вести в цикле ХХХ, делая это маленькими порциями. Т.е. на одном процессоре получается и таймер и исполнительный таск и фоновый процесс обсчета.
p.s.
Т.к. это не реалтайм, то в снятые данные обязательно надо класть и их таймшифт.
serj Ну вот и получается я предложил вполне себе вариант . Я его предлагал как раз для точных задержек, где может быть время даже менее мс.
Я использовал в своем коде кол-во тиков процессора, точно так же рассчитывая их количество в заданный интервал времени. И по окончанию вызывал нужную процедуру через указатель на нее.
Advanced member
Статус: Не в сети Регистрация: 10.04.2003 Откуда: Москва
sashar2, в моем коде нет вызовов Win32, по которым вынь пытается запустить _другие_ таски. Я отдаю время контролируемо. Кроме того, функции Win32 выполняются не в твоем контексте (не помню, распространяется ли это на QueryPerformance*) и вызов потребует перегрузки контекста, что еще время... хоть и мелочь. Убирай вызовы QueryPerformance*.
serj На одном ядре моего процессора я могу вызывать функцию QueryPerformance примерно тысячу раз в одну миллисекунду. А вызов винапи для создания потока всеравно неплохо иметь, чтобы иметь возможность создавать полноценный таймер, как я уже говорил.
Ты прав в том, что лучше использовать ассемблерную вставку для чтения rdtsc, это очевидно будет быстрее. Если не считать того минуса, что могут помешать энергосберегающие технологии, например как ты упомянул Q&Q. И еще не могу сказать точно хорошо ли использовать ассемблер, если программа может компилироваться под х64 (msvs например придется повоевать ). Возможно эти минусы и не значительны, но если подумать, что QueryPerformance вполне хватает, то зачем бороться с чем-то?
Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 30
Вы не можете начинать темы Вы не можете отвечать на сообщения Вы не можете редактировать свои сообщения Вы не можете удалять свои сообщения Вы не можете добавлять вложения