Часовой пояс: UTC + 3 часа




Начать новую тему Новая тема / Ответить на тему Ответить  Сообщений: 27 • Страница 1 из 21  2  >
  Пред. тема | След. тема 
В случае проблем с отображением форума, отключите блокировщик рекламы
Автор Сообщение
 

Junior
Статус: Не в сети
Регистрация: 24.06.2007
Откуда: Москва
Всем привет. На носу диплом.. в процессе разработки зашла в тупик. у меня есть функция для определения тактовой частоты процессора. Нужно с помощью нее получать конкретные временные задержки (привязку нужно сделать через системный таймер). Не могу придумать как это реализовать. Частота получается с помощью RdtSC(если нужно взглянуть на функцию, могу выложить исходник). Очень надеюсь на вашу помощь..

Варианты с функцией Sleep() и подобными не подходят. нужно использовать только системный таймер и частоту МП.
Программа пишется в Delphi7. ОС виста. проц 2 ядра.



Партнер
 

Member
Статус: Не в сети
Регистрация: 14.01.2004
Откуда: Киев, Украина
1. С помощью rdtsc частоту не посчитать впринципе, нужны показатели какой-нибудь таймера.
2. Что такое в вашем понимании системный таймер? У современных систем существует несколько таймеров: APIC таймер, ACPI таймер (он же PM таймер), HPET, и еще всякие рюшечки. И сказать, что один из них более системный, а другой нет - нельзя :-D
3. Sleep, QueryPerformanceCounter и прочие ф-циюю опираются на HAL, который в свою очередь использует один из системный таймеров.

Сформулируйте вопрос собственно, пришли рассказали и "Очень надеюсь на вашу помощь.. ", извините, телепатов увезли...

_________________
Ку ку


 

Junior
Статус: Не в сети
Регистрация: 24.06.2007
Откуда: Москва
Код:
unit Unit1;

interface

uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls;

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.

_________________
Ку ку


 

Junior
Статус: Не в сети
Регистрация: 24.06.2007
Откуда: Москва
суть понятна, не будет ли Вам сложно написать код? отношения с потоками у меня не очень..:oops:


 

Member
Статус: Не в сети
Регистрация: 05.12.2005
ggKk
Еще актуально?


 

Junior
Статус: Не в сети
Регистрация: 24.06.2007
Откуда: Москва
актуально!


 

Member
Статус: Не в сети
Регистрация: 05.12.2005
Вобщем после ужина что-то такое написал, надеюсь подойдет:
Код:
#include <windows.h>
#include <iostream>
#include <conio.h>

using namespace std;

class ExactTimer
{
public:
    typedef void (*CallbackProc)(void);

    ExactTimer()
    {
      Callback = NULL;
      StartThreadHandle = NULL;
      RepeatOnce = false;
      Milliseconds = 1000;
    }
   
    ~ExactTimer()
    {
      Stop();
    }

    void Start()
    {
      Stop();
      StartThreadHandle = CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)(StartThread),(void*)this,NULL,0);
    }

    void Stop()
    {
      if (StartThreadHandle != NULL)
      {
        TerminateThread(StartThreadHandle, 0);
        CloseHandle(StartThreadHandle);
        StartThreadHandle = NULL;
      }
    }
   
    void SetMilliseconds(long milliseconds)
    {
      Milliseconds = milliseconds;
    }

    long GetMilliseconds() const
    {
      return Milliseconds;
    }

    void SetCallbackProcedure(CallbackProc callback)
    {
      Callback = callback;
    }

    void SetRepeatOnce(bool repeatOnce)
    {
      RepeatOnce = repeatOnce;
    }
protected:
    CallbackProc Callback;
    bool RepeatOnce;
    bool Repeat;
    long Milliseconds;
    HANDLE StartThreadHandle;

    static DWORD WINAPI StartThread(void *pExactTimer)
    {
      ExactTimer exactTimer = *((ExactTimer*)pExactTimer);
      LARGE_INTEGER liFrequency, liStartTime, liCurrent;
      QueryPerformanceFrequency(&liFrequency);
      __int64 llWaitTime = ((liFrequency.QuadPart / 1000) * exactTimer.Milliseconds);
      while (true)
      {
        QueryPerformanceCounter(&liStartTime);
        QueryPerformanceCounter(&liCurrent);
        while ((liCurrent.QuadPart - liStartTime.QuadPart) < llWaitTime)
          QueryPerformanceCounter(&liCurrent);
        if (exactTimer.Callback != NULL)
          exactTimer.Callback();
        if (exactTimer.RepeatOnce)
          exactTimer.Stop();
      }
      return 0;
    } 
};

void PrintTheEnd()
{
  cout << " THE END " << endl;
}

int main()
{
  ExactTimer t;
  t.SetRepeatOnce(false);
  t.SetMilliseconds(100);
  t.SetCallbackProcedure(PrintTheEnd);
  t.Start();
  _getch();
  t.Stop();
  _getch();
  return 0;
}


Запустишь где-нибудь этот таймер и будешь считывать себе каждые 2 мс что там тебе нужно. В моем примере использования этого класса я вывожу на экран "THE END" каждые 100мс.

У тебя проблем с переводом на делфи не будет? Winapi-то один и тот же и типы почти так же называются. Если нужно, то могу переделать.


 

Junior
Статус: Не в сети
Регистрация: 24.06.2007
Откуда: Москва
я конечно думаю смогу переписать на делфи, но если будет возможность, то можешь переделать (лучше перебдеть, чем недобдеть))) спасибо огромное!!


 

Advanced member
Статус: Не в сети
Регистрация: 10.04.2003
Откуда: Москва
Мысли вслух, из серии 'тихо сам с собою'...
Зачем делать 2 треда и этот зоопарк с вызовом Win32? Это долго, в эти моменты вынь может отобрать время и привет родственникам.
Я бы сделал 2 вещи:
1. откалибровал TSC
2. юзал один тред.
Зная временные отметки TSC не трудно написать таск, который проверяет наступление времени и 'когда настало' выполнял бы свои действия. Т.к. точно известно отставание от сетки запуска, то это можно учесть при работе с устройством (если надо).
И вообще, понятия 'запуск через 2 mS' и 'запуск каждые 2mS' принципиально различаются, вынь далеко не realtime система.


 

Member
Статус: Не в сети
Регистрация: 05.12.2005
Само собой что realtime на windows никогда не будет, и никто не гарантирует, что таймер сработает через 2 мс.
Использование одного потока не дает возможности создания более менее точного таймера, становиться возможным лишь создание такой задержки. А это:
1) Сужает возможность применения.
2) У процесса с одним потоком точно так же могут отобрать процессорное время.

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

P.S>
Помоему твой пост относиться напрямую к теме и не стоит его помечать оффтопом. Я считаю никогда не лишним разобраться и найти верный путь, каждый может ошибаться.


 

Advanced member
Статус: Не в сети
Регистрация: 10.04.2003
Откуда: Москва
1. ты никогда не сможешь отобрать у меня время внутри кванта. :)
Запрашивая средства выни ты сам напрашиваешься на отбирание времени.
2. тред, который считает время загружает процессор на 100% и если процессоров всего один, то он будет мешать тому таску, который будет обрабатывать устройство.
Ну и где твоя экономия? ;)


 

Junior
Статус: Не в сети
Регистрация: 24.06.2007
Откуда: Москва
2 serj:
мысли кстати правильные. может в виде кода выразишь? :oops:

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


 

Member
Статус: Не в сети
Регистрация: 05.12.2005
serj
Я не говорил про экономию. Если ты можешь реализовать таймер, способный тикать в такие маленькие интвервалы времени, да еще и не загружая процессор под винь, то тебе все только спасибо скажут :).
Ну и ресурсы между тредами можно разделить в нужной пропорции, если используется один процессор.

Ray Adams
Это решение не подходит, точность невысокая.

ggKk
Ммм, что-то код на мой не очень похож ;), будет время - на делфи перенесу...


 

Junior
Статус: Не в сети
Регистрация: 24.06.2007
Откуда: Москва
sashar2

ну как поняла, так и перевела... жду оригинального перевода от автора :wink:

serj, Ray Adams и все все все
в данном случае моя цель не экономия! мне не жалко и весь проц под завязку загрузить, главное - точность, и минимальность временного интервала!
вот-с.

Кстати, всех с праздничками!:beer:


 

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

B=rdtsc
Цикл:
---------------------------
rdtsc > B ?
{Больше - [вызвать обработку] B+M}
{Меньше - ХХХ}
---------------------------

Под ХХХ понимается:
если надо очень маленькое время, то ничего или процедура обработки результатов.
Если время обработки велико, то там можно поставить Sleep(0), а если больше 50mS, то даже Sleep(20). Естественно, что нам надо, чтоб вынь после выполнения других тасков отдала время нам, а не кому-то еще, потому перед этим надо поставить для этого треда realtime. Т.к. мы будем делиться временем, то вынь не зависьнет. :) (детально на всех OS не проверял)
Для справки - команда Sleep(0) просто переключает таски ... но т.к. у нас очень высокий приоритет, то блуждать она особо не будет.

Под [вызвать обработку] понимается или запуск процедуры (ЧЕРЕЗ ПРЯМОЙ CALL!) или установка какого-либо семоформа.

Если аппаратура требует достаточно четкой реакции, то процедуру съема данных и их обработки необходимо разделить. А именно, по [вызвать обработку] только снимать данные и помещать их в буфер, а обработку вести в цикле ХХХ, делая это маленькими порциями. Т.е. на одном процессоре получается и таймер и исполнительный таск и фоновый процесс обсчета. :)

p.s.
Т.к. это не реалтайм, то в снятые данные обязательно надо класть и их таймшифт.


 

Member
Статус: Не в сети
Регистрация: 05.12.2005
serj
Ну вот и получается я предложил вполне себе вариант :). Я его предлагал как раз для точных задержек, где может быть время даже менее мс.
Я использовал в своем коде кол-во тиков процессора, точно так же рассчитывая их количество в заданный интервал времени. И по окончанию вызывал нужную процедуру через указатель на нее.


 

Advanced member
Статус: Не в сети
Регистрация: 10.04.2003
Откуда: Москва
sashar2, в моем коде нет вызовов Win32, по которым вынь пытается запустить _другие_ таски. Я отдаю время контролируемо. Кроме того, функции Win32 выполняются не в твоем контексте (не помню, распространяется ли это на QueryPerformance*) и вызов потребует перегрузки контекста, что еще время... хоть и мелочь.
Убирай вызовы QueryPerformance*. ;)


 

Member
Статус: Не в сети
Регистрация: 05.12.2005
serj
На одном ядре моего процессора я могу вызывать функцию QueryPerformance примерно тысячу раз в одну миллисекунду. А вызов винапи для создания потока всеравно неплохо иметь, чтобы иметь возможность создавать полноценный таймер, как я уже говорил.
Ты прав в том, что лучше использовать ассемблерную вставку для чтения rdtsc, это очевидно будет быстрее. Если не считать того минуса, что могут помешать энергосберегающие технологии, например как ты упомянул Q&Q. И еще не могу сказать точно хорошо ли использовать ассемблер, если программа может компилироваться под х64 (msvs например придется повоевать :) ). Возможно эти минусы и не значительны, но если подумать, что QueryPerformance вполне хватает, то зачем бороться с чем-то?

P.S>
Про "в моем коде" ты сильно сказал ;).


Показать сообщения за:  Поле сортировки  
Начать новую тему Новая тема / Ответить на тему Ответить  Сообщений: 27 • Страница 1 из 21  2  >
-

Часовой пояс: UTC + 3 часа


Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 30


Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете добавлять вложения

Перейти:  
cron
Создано на основе phpBB® Forum Software © phpBB Group
Русская поддержка phpBB | Kolobok smiles © Aiwan