Ну во первых это не класс, а запись.
А во вторых: есть, что нибудь с rdtsc ?
Автор не перестает удивлять, штампуя статейки для первоклашек )
Пользуюсь тоже таким таймером. Но для начала попробуй своим таймером замерить скорость со вставками и без :)
Мне кажется экономия на стекфрейме тут лишняя, сама функция вроде бы в syscall улетает, а там накладные расходы в десятки раз больше чем стек. Особенно для queryfrequency ассемблер - мёртвому припарка.
Это gettickcount шустрый, тупо переменную читает. Но ее своевременное обновление ядром не гарантировано.
nes
Ну-ну, а на гд все древние бородатые программисты, писавшие ещё на Спектрум. В машинных кодах без иде :>
Ну, не спектрум, и не совсем в кодах...
+ Показать
− Скрыть
{$ifdef cpuarm}
function UsecDelta (ptsc: pqword): double; cdecl;
var v: timeval;
begin
v:= ptimeval(ptsc)^;
fpgettimeofday(ptimeval(ptsc), NIL);
{$push}
{$overflowchecks off}
{$rangechecks off}
Result:=
1000000.0 * longint(ptimeval(ptsc)^.tv_sec - v.tv_sec)
+ 1.0 * longint(ptimeval(ptsc)^.tv_usec - v.tv_usec);
{$pop}
if Result < 0.0 then Result:= 0.0;
end;
{$endif}
{$ifdef CPUX86_64}
function UsecDelta (ptsc: pqword): double; cdecl;
var q: qword;
begin
q:= ptsc^;
asm
rdtsc
shl rdx, 32
or rdx, rax
mov rax, qword[ptsc]
mov qword[rax], rdx
end ['rax', 'rdx'];
if q >= ptsc^ then q:=ptsc^ - 1;
Result:= (ptsc^ - q) * Mother^.Timer.RDTSCFactor;
end;
{$endif}
{$ifdef CPU386}
function UsecDelta (ptsc: pqword): double; cdecl;
var q: qword;
begin
q:= ptsc^;
asm
mov ecx, ptsc
rdtsc
mov [ecx], eax
mov [ecx + 4], edx
end ['ecx', 'eax', 'edx'];
if q >= ptsc^ then q:=ptsc^ - 1;
Result:= (ptsc^ - q) * Mother^.Timer.RDTSCFactor;
end;
{$endif}
Cheb
Норм, выкладывай целиком)
Все это ассемблерное кунг-фу не нужно нихрена, потому что есть готовое API в виде QPF/QPC и clock_gettime.
PS. И более того, в юзерспейсе использование подобных выкрутасов - это не портируемо даже внутри одной ОС.
Всё не выкладу, оно вшито в движок, но что могу - отрежу.
+ Показать
− Скрыть
const
TSCMeasuringInterval = 10.0;
{$macro on}
{$ifdef FPC_REQUIRES_PROPER_ALIGNMENT}
{$define maybepacked:=}
{$ifdef cpu32}
{$packrecords 4}
{$else}
{$fatal WAS NOT DESIGNED FOR THIS!}
{$endif}
{$else}
{$define maybepacked:=packed}
{$endif}
TMotherTimer = maybepacked record
FPS,
QF: single;
StartupDateTime: TDateTime;
RdtscFrequency,
RdtscFactor,
LastSwapBuffersDuration: double;
FpsDisplayingParams: TFpsCounterParams;
AverageSwapBuffersDuration: double;
QF_ManualOverride: boolean;
SuspendSwapBuffersMeasurement: boolean; { This is set by the module:
signaling the mother to suspend measurement
during potentially costly operations like creating FBOs
to avoid creating a feedback loop}
CullFrameRate: integer; {
This field gets set to 30 before each call to the module pulse function
(or to 60 if a mouse movement event was received).
Unless the module sets it back to a higher value, there will be Sleep() called
by the mother module after return from the module pulse function, to cull
frame rate at the value determined here. (implemented in cl_window)
This way only high-performance 3d rendering modules would hog
all the video card & cpu resources,
and only when they specifically request it.
Setting it higher than 300 disables culling.
}
Second: function (): double; cdecl;
UsecDelta: function (ptsc: pqword): double; cdecl;
{$ifdef windows}
ActiveTBP1: boolean;
{$endif}
HiResSleep: procedure(ms: longint); cdecl;
ReservedStub: array[0..15] of ptruint;
end;
{$ifndef cpuarm}
{$asmmode intel}
const
ctRDTSCFreqEstimationPeriod
= {$ifdef windows} 5.0 {$else} 20.0 {$endif};
ctFreqCheckPeriod
= {$ifdef windows} 2.0 {$else} 5.0 {$endif};
ctFreqDeviationTolerancy
= {$ifdef windows} 0.05 {$else} 0.2 {$endif};
{$endif}
var
_startTSC: qword;
{$ifdef windows}
_starttick: int64;
function Second: double; cdecl;
begin
Result:= (GetTickCount() - _starttick) / 1000.0;
end;
{$else}
_StartNow: TDateTime;
function Second(): double; cdecl;
begin
Result:= (Now() - _StartNow) * SecondsPerDay;
end;
{$endif}
function GetTimeStamp: qword; forward;
{$ifndef cpuarm}
var
_PrevTscCheckSec: double = 0;
_PrevTsc: qword;
_tsc: qword;
function RecheckTSCFreq(period: double = TSCMeasuringInterval): longbool;
var
sec: double;
begin
try
sec:= Second();
if (sec - _PrevTscCheckSec) < period then Exit(false);
_tsc:= GetTimeStamp();
Result:= true;
if _tsc <= _prevTsc then Result:= false;
if Result then begin
Mother^.Timer.RDTSCFrequency:= (_tsc - _prevTSC) / (sec - _prevTscCheckSec);
Mother^.Timer.RdtscFactor:= 1000000.0 / Math.max(1.0, Mother^.Timer.RdtscFrequency);
end;
_prevTSC:= _tsc;
_PrevTscCheckSec:= sec;
except end;
end;
{$endif}
{$ifdef cpuarm}
function UsecDelta (ptsc: pqword): double; cdecl;
var v: timeval;
begin
v:= ptimeval(ptsc)^;
fpgettimeofday(ptimeval(ptsc), NIL);
{$push}
{$overflowchecks off}
{$rangechecks off}
Result:=
1000000.0 * longint(ptimeval(ptsc)^.tv_sec - v.tv_sec)
+ 1.0 * longint(ptimeval(ptsc)^.tv_usec - v.tv_usec);
{$pop}
if Result < 0.0 then Result:= 0.0;
end;
{$endif}
{$ifdef CPUX86_64}
function UsecDelta (ptsc: pqword): double; cdecl;
var q: qword;
begin
q:= ptsc^;
asm
rdtsc
shl rdx, 32
or rdx, rax
mov rax, qword[ptsc]
mov qword[rax], rdx
end ['rax', 'rdx'];
if q >= ptsc^ then q:=ptsc^ - 1;
Result:= (ptsc^ - q) * Mother^.Timer.RDTSCFactor;
end;
{$endif}
{$ifdef CPU386}
function UsecDelta (ptsc: pqword): double; cdecl;
var q: qword;
begin
q:= ptsc^;
asm
mov ecx, ptsc
rdtsc
mov [ecx], eax
mov [ecx + 4], edx
end ['ecx', 'eax', 'edx'];
if q >= ptsc^ then q:=ptsc^ - 1;
Result:= (ptsc^ - q) * Mother^.Timer.RDTSCFactor;
end;
{$endif}
var
PrevTimeStamp: qword = 0;
function GetTimeStamp: qword;
{$ifdef cpuarm}
var v: timeval;
begin
{$push}
{$overflowchecks off}
{$rangechecks off}
fpgettimeofday(@v, NIL);
Result:= qword((int64(1000000) * int64(v.tv_sec)) + int64(v.tv_usec));
{$pop}
{$else cpuarm}
begin
{$ifdef cpu64}
asm
rdtsc
shl rdx, 32
or rax, rdx
mov qword[Result], rax
end ['rax', 'rdx'];
{$else}
asm
rdtsc
mov dword[Result], eax
mov dword[Result + 4], edx
end ['eax', 'edx'];
{$endif}
{$endif cpuarm}
if Result <= PrevTimeStamp then begin
Result:=PrevTimeStamp + 1;
inc(PrevTimeStamp);
end
else
PrevTimeStamp:=Result;
end;
procedure InitCTimer;
begin
{$ifdef cpuarm}
Assert(sizeof(timeval) = 8, 'The timer routines are not compatible with 64-bit architecture.');
Mother^.Timer.RDTSCFrequency:= 1000000;
Mother^.Timer.RdtscFactor:= 1000000.0 / Mother^.Timer.RdtscFrequency;
_startnow:= Now();
{$else cpuarm}
{$ifdef windows}
{$ifdef cpu32}
if Mother^.Debug.Verbose then AddLog('Calling timeBeginPeriod(1)...');
if timeBeginPeriod(1) = TIMERR_NOERROR
then begin
if Mother^.Debug.Verbose then AddLogOk;
Mother^.Timer.ActiveTBP1:= true;
end
else Die(RuEn(
'Не удалось переключить системный таймер на разрешение в 1 мс.',
'Unable to switch the system timer to 1 milisecond precision.'));
SetThreadPriority(GetCurrentThread, THREAD_PRIORITY_TIME_CRITICAL);
{$endif}
_starttick:= GetTickCount();
{$else}
_startnow:= Now();
{$endif}
if Mother^.Debug.Verbose then AddLog('Measuring TSC frequency...');
Mother^.Timer.RDTSCFrequency:= 1;
Mother^.Timer.RdtscFactor:= 1000000.0 / Mother^.Timer.RdtscFrequency;
_startTSC:= GetTimeStamp();
_PrevTSC:= _startTSC;
Mother^.Timer.StartupDateTime:= Now();
repeat until RecheckTscFreq(0.5);
{$if defined(windows) and defined(cpu32)}
SetThreadPriority(GetCurrentThread, THREAD_PRIORITY_NORMAL);
{$endif}
if Mother^.Debug.Verbose then AddLog(' ..is %0',[Mother^.Timer.RDTSCFrequency]);
{$endif cpuarm}
LastHeartbeatMoment:= Mother^.Timer.StartupDateTime;
end;
>, потому что есть готовое API в виде QPF/QPC
Вызов которого на порядок медленнее, чем RDTSC, даже со всеми свистелками и перделками, что я ея обвесил.
Кроме того, они дают разрешение *ниже*, чем RDTSC. Я стабильно наблюдаю, как они берут тот же TSC и делят его на 1000.0 - полагаю, для совместимости, чтобы старый софт не подавился большими значениями.
По крайней мере, QueryPerformanceFrequency(), или как её там, в Windows 7 возвращает частоту TSC, делённую на 1000. На нескольких процах пробовал.
То есть, получаем то же самое, только дороже и искуственно огрублённое с потерей разрядности.
TL;DR TSC даёт реально в наносекундах и сам вызов занимает наносекунд сорок. QPC даёт в микросекундах и вызов занимает... Полторы сотни наносекунд, ЕМНИП.
Mira
> Мне кажется экономия на стекфрейме тут лишняя
Какая нафиг экономия?Миллисекунда разрешения этого «таймера» - вечность относительно количества тактов, которое успевает натикать тот самый счётчик, который опрашивается в статье.
Ghost2
дак вот.
я просто думаю какой бы счетчик для просчета движения в сервере сделать.
например известна стартовая позиция персонажа, его скорость и направление. надо точно просчитать его текущее положение на данный момент.
часто дергать QPC слишком накладно.
GTC - при сильной загрузке лажает.
магия с RDTSC - еще не пробовал, но говорят могут быть нюансы тоже.
Cheb
> Вызов которого на порядок медленнее, чем RDTSC
Какая прости разница, быстрый это вызов или нет?
> Кроме того, они дают разрешение *ниже*, чем RDTSC
RDTSC даёт тебе попугаи. Те люди, которые пишут операционные системы под 350 архитектур уже озаботились тем, чтобы хоть как-то этих попугаев можно было перевести в секунды.
> в Windows 7 возвращает частоту TSC, делённую на 1000
Она что угодно может возвращать. От частоты ФАПЧ клока процессора, через частоту PIT, HPET, APIC и до 32768 Гц в RTC.
>RDTSC даёт тебе попугаи.
Меня трудностями не испугаешь.
>уже озаботились тем, чтобы хоть как-то этих попугаев можно было перевести в секунды.
"как-то" - конкретно, через анус.
>Она что угодно может возвращать.
Вот именно. А TSC - гарантированно гигагерцы, в весьма узких пределах на современных (Не старше 10 лет) цпу. От 2.5 до 4.5 миллиардов попугаев в секунду. Неизменность частоты гарантируется: последние, кто не поддерживал TSC invariancy, были ранние четвёртые пни.
З.Ы. От таймера часто требуется померить "интервал от и до". В подобных случаях, релятивистский подход, как у меня, без перевода в секунды и обратно - даёт наименьшие потери точности. И RDTSC тут - самый оптимальный. Замучила паранойя - делай fallback версию на QPC с переключением на лету.