The time in Sleep is wrong, + 1 lazarus / 64-FPC development ---------------------------------------------------------------------- zanden30@hetnet.nl · 2018-11-22 10:37 The implementation on Sleep ( t ) , t [mS] is wrong. t = 0 gives 0 mS sleep. OK. But any other value gives (t + 1) mS of sleep. Hence, it is impossible to Sleep 1 mS. By the way: on Windows 10 the sleep time is on average far within 5% precise, much better than suggested in the documentation. Not too bad…… ---------------------------------------------------------------------- tr_escape · 2018-11-22 10:48 I tested in windows 10 Lazarus 1.9.0 rFRET_UNKNOWN_REVISION FPC 3.1.1 i386-win32-win32/win64 By the code: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i program Project1;uses SysUtils;var  first,last : TDateTime;begin  first:=Now;  Sleep(1);  last:=Now;  WriteLn('Between : '+FormatDateTime('hh:nn:ss.zzz',last-first));  ReadLn; end.  It is gives 00:00:00.001 or 00:00:00.002, I think it is up to OS busyness time. ---------------------------------------------------------------------- marcov · 2018-11-22 10:48 So what OS is this? Afaik Sleep(x) sleeps AT LEAST x ms. Then it might take another context switch to activate the thread and get it running. Such latencies are often CPU dependent. ---------------------------------------------------------------------- 440bx · 2018-11-22 11:05 Quote from: marcov on November 22, 2018, 11:48:31 amSo what OS is this? Afaik Sleep(x) sleeps AT LEAST x ms. Then it might take another context switch to activate the thread and get it running. Such latencies are often CPU dependent. Not to mention highly OS dependent. ---------------------------------------------------------------------- Thaddy · 2018-11-22 11:12 T=0 is a special case on windows and means "give up timeslice". See msdn https://docs.microsoft.com/en-us/windows/desktop/api/synchapi/nf- synchapi-sleep And indeed, it is highly platform dependent. It requires a RTOS to reliably get the exact value. ---------------------------------------------------------------------- zanden30@hetnet.nl · 2018-11-22 13:45 No, no, it is really no latency. When I chose 1 mS ==> 2 mS When I chose 10 mS ==> 11 mS. When I chose 19 mS ==> 20 mS (corrected, excuse me). And the values measured are very precise. When I count 1.000 times, the deviation is only < 1% So I think there is a while loop coding error in this FPC procedure. Or in Windows, also possible, of course. Can I check that? The latency is only some uSeconds. ---------------------------------------------------------------------- rvk · 2018-11-22 13:52 Quote from: zanden30@hetnet.nl on November 22, 2018, 02:45:05 pmWhen I chose 1 mS ==> 2 mS When I chose 10 mS ==> 11 mS. When I chose 19 mS ==> 10 mS. I take it that last 10 mS is a typo. Anyway... how do you calculate those mS? Maybe the time for calculating the difference in time also takes 1 mS. So your result will always be 1 mS above the selected Sleep(). And as said. Sleep is not that accurate. This is what Microsoft says about it (for Windows): QuoteIf dwMilliseconds is greater than one tick but less than two, the wait can be anywhere between one and two ticks, and so on. ---------------------------------------------------------------------- lucamar · 2018-11-22 14:12 Quote from: zanden30@hetnet.nl on November 22, 2018, 02:45:05 pm[...] And the values measured are very precise. When I count 1.000 times, the deviation I only < 1% It can't be measured very precisely because the nature of Sleep. Think of it as if it where an abbreviation of SleepForAtLeast(ms); a small deviation of 1 to 5 ms (depending on the parameter and the system load) should be expected in almost all architectures. ---------------------------------------------------------------------- Martin_fr · 2018-11-22 14:44 Quote from: zanden30@hetnet.nl on November 22, 2018, 11:37:03 amBy the way: on Windows 10 the sleep time is on average far within 5% precise, much better than suggested in the documentation. Not too bad…… The doc may be worst case. On most modern computers the CPU is fast enough, and has enough cores, so an active (focused) user process will not get much interruption. If of course, I run any such code on my 12 year old (standby) 32bit system.... I bet the story changes. Anyway the conclusion is: - "sleep" is not meant to be exact. It is meant "at least the given time" - Even if "sleep" were exact, the OS does not guarantee your process will not be interrupted. Your app could at any time (including just before/after a sleep) be interrupted for any amount of time. ---------------------------------------------------------------------- rvk · 2018-11-22 15:02 And also when measuring elapsed time you shouldn't rely on Now or GetTickCounter or likewise. Those are not accurate within 1ms (although they result in 1ms values). You should use something like QueryPerformanceCounter (or equivalent for Linux). You might want to look into EpikTimer. ---------------------------------------------------------------------- zanden30@hetnet.nl · 2018-11-23 01:15 I have measured with the Now function. This is as far as I can see connected with the Win10 system clock, date time, which is very precise. Of course there is a standard deviation due to interrupts. But on average, when I measure 1.000 or even 10.000 times, it reproduces the same values. When I measured my mainloop with the Now function I could see that a heavy CPU load degraded, or course, the throughput; and also here I measured rather stable and logical values with the Now function with a low load of the CPU (4 cores). I have measured the loop and measurement itself. That takes only 1 uS per loop. So there is a negligible bias by the measurement (< 1 0/00). It is too accidentally, that the time is always exactly (exactly!!!) 1 mS too much. Must be a mistake; either in the FPC implementation or in the Win 10 system call. I have no idea how I can check both of them. But I am sure that guys in this forum can do….. ---------------------------------------------------------------------- zanden30@hetnet.nl · 2018-11-23 01:25 Quote from: tr_escape on November 22, 2018, 11:48:08 amI tested in windows 10 Lazarus 1.9.0 rFRET_UNKNOWN_REVISION FPC 3.1.1 i386-win32-win32/win64 By the code: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i program Project1;uses SysUtils;var  first,last : TDateTime;begin  first:=Now;  Sleep(1);  last:=Now;  WriteLn('Between : '+FormatDateTime('hh:nn:ss.zzz',last-first));  ReadLn; end.  It is gives 00:00:00.001 or 00:00:00.002, I think it is up to OS busyness time. I think one measurement does not indicate much. Especially not when the duration of the time equals the resolution. That is a "non- measurement". I repeat the measurement 1.000 and more times, which gives a valuable, accurate and reproducible result. ---------------------------------------------------------------------- tr_escape · 2018-11-23 05:06 Quote from: zanden30@hetnet.nl on November 23, 2018, 02:25:26 amI think one measurement does not indicate much. Especially not when the duration of the time equals the resolution. That is a "non- measurement". I repeat the measurement 1.000 and more times, which gives a valuable, accurate and reproducible result. I would like to look your measurement codes, could you please share with us? ---------------------------------------------------------------------- Thaddy · 2018-11-23 09:15 On most platforms including windows, the sleep call is a direct call to the OS, so it can't be a Freepascal issue anyway. Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i procedure Sleep(dwMilliseconds:DWORD); external 'kernel32' name 'Sleep'; ---------------------------------------------------------------------- User137 · 2018-11-23 09:18 That's a simple example to change: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i program Project1; uses SysUtils;Const TRIES = 1000;var first, last: TDateTime;    i: integer;begin  first:=Now;  for i:=1 to TRIES do Sleep(1);  last:=Now;  WriteLn('Between: '+FormatDateTime('hh:nn:ss.zzzz',(last-first)/TRIES));  WriteLn('Duration: '+FormatDateTime('hh:nn:ss.zzzz',last-first));  ReadLn;end.Gives this on Win10-64bit: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i Between: 00:00:00.002Duration: 00:00:01.904 ---------------------------------------------------------------------- rvk · 2018-11-23 09:25 O, wow, there must be something really wrong with my system  Code: [Select]Between: 00:00:00.011 Duration: 00:00:10.659 Edit: This was with debugging information on but running without debugger. Compiling with debugging information off gave more normal results. Didn't know that compiling with just debugger information on gave such slowdown (even without running in gdb). ---------------------------------------------------------------------- zanden30@hetnet.nl · 2018-11-23 11:02 @User137. That's exactly what I measure. 1 mS too much when I repeat measurements 1.000 times. My SW is a Finite State Machine experiment. It does nothing but printing 7 independently running tasks that switch continuously, and it counts also the time. I have a .py procedure to generate a flexible FSM with unlimited number of tasks. See attachment. Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i   Program FSMDemo7x10; Uses Sysutils, keyboard, typinfo; Type         t_State = (Start_State, S1, S2, S3, S4, S5, S6, S7, S8, S9, Delay_State, Exit_State);        t_Task  = (T1, T2, T3, T4, T5, T6, T7);  Var     //Required variables for the FSM and the Delay function        Task  : t_Task; // Represents the actual active FSM        State   : t_State; // Represents the actual state, most important variable of the FSM!!                States                          : Array[T1 .. T7] of t_State; //Store of state                Delay_Time            : Array[T1 .. T7] of longint; //Common Delay Time per FSM        Delay_States    : Array[T1 .. T7] of t_State; //Store of caller state is Delay is activated.         Millis, n, Loop_Time : longint;  //Time in milli seconds        Const        Loop_Count = 1000;              //Application variables; can be removedVar        X : Array[T1 .. T7] of integer;        Ch: String;        Delay_mS : longint;  // app variable (***************)Procedure Change_State(Target: t_State); Forward;Procedure Do_Delay_State; Forward;Procedure Delay(t: longint); Forward;(***************) //All writeln / output and input statements are for demo only. Can be omitted.(***************)Procedure PL(s : string);(***************)var         i : integer;        Begin        for i := 0 to Ord(Task) - 1 do                write('*************');        writeln(' T: ' + GetEnumName(TypeInfo(t_Task), integer(Task)) + ' ' + s)end; (***************)Procedure Print_Error(s : string);(***************)Begin        writeln('**************');      writeln(s);        writeln('**************');end; (***************)Procedure Do_S1;(***************)Begin        PL('Do_S1, # ' + IntToStr(X[Task]));         X[Task]:= X[Task] + 1;        if X[Task] >= Ord(High(t_Task)) + 5 - Ord(Task) then        begin                Change_State(S2);                PL('My Delay = ' + IntToStr(Delay_mS +  (1333 * Ord(Task))));        end        else        begin              Delay(Delay_mS +  (1333 * Ord(Task)));        end;end; (***************)Procedure Entry_S1;(***************)Begin      PL('Entry S1');        X[Task] := 0;end; (***************)Procedure Exit_S1;(***************)Begin      PL('Exit S1');end; (***************)Procedure Do_S2;(***************)Begin        PL('Do_S2, # ' + IntToStr(X[Task]));         X[Task]:= X[Task] + 1;        if X[Task] >= Ord(High(t_Task)) + 5 - Ord(Task) then        begin                Change_State(S3);                PL('My Delay = ' + IntToStr(Delay_mS +  (1333 * Ord(Task))));        end        else        begin              Delay(Delay_mS +  (1333 * Ord(Task)));        end;end; (***************)Procedure Entry_S2;(***************)Begin      PL('Entry S2');        X[Task] := 0;end; (***************)Procedure Exit_S2;(***************)Begin      PL('Exit S2');end; (***************)Procedure Do_S3;(***************)Begin        PL('Do_S3, # ' + IntToStr(X[Task]));         X[Task]:= X[Task] + 1;        if X[Task] >= Ord(High(t_Task)) + 5 - Ord(Task) then        begin                Change_State(S4);                PL('My Delay = ' + IntToStr(Delay_mS +  (1333 * Ord(Task))));        end        else        begin              Delay(Delay_mS +  (1333 * Ord(Task)));        end;end; (***************)Procedure Entry_S3;(***************)Begin      PL('Entry S3');        X[Task] := 0;end; (***************)Procedure Exit_S3;(***************)Begin      PL('Exit S3');end; (***************)Procedure Do_S4;(***************)Begin        PL('Do_S4, # ' + IntToStr(X[Task]));         X[Task]:= X[Task] + 1;        if X[Task] >= Ord(High(t_Task)) + 5 - Ord(Task) then        begin                Change_State(S5);                PL('My Delay = ' + IntToStr(Delay_mS +  (1333 * Ord(Task))));        end        else        begin              Delay(Delay_mS +  (1333 * Ord(Task)));        end;end; (***************)Procedure Entry_S4;(***************)Begin      PL('Entry S4');        X[Task] := 0;end; (***************)Procedure Exit_S4;(***************)Begin      PL('Exit S4');end; (***************)Procedure Do_S5;(***************)Begin        PL('Do_S5, # ' + IntToStr(X[Task]));         X[Task]:= X[Task] + 1;        if X[Task] >= Ord(High(t_Task)) + 5 - Ord(Task) then        begin                Change_State(S6);                PL('My Delay = ' + IntToStr(Delay_mS +  (1333 * Ord(Task))));        end        else        begin              Delay(Delay_mS +  (1333 * Ord(Task)));        end;end; (***************)Procedure Entry_S5;(***************)Begin      PL('Entry S5');        X[Task] := 0;end; (***************)Procedure Exit_S5;(***************)Begin      PL('Exit S5');end; (***************)Procedure Do_S6;(***************)Begin        PL('Do_S6, # ' + IntToStr(X[Task]));         X[Task]:= X[Task] + 1;        if X[Task] >= Ord(High(t_Task)) + 5 - Ord(Task) then        begin                Change_State(S7);                PL('My Delay = ' + IntToStr(Delay_mS +  (1333 * Ord(Task))));        end        else        begin              Delay(Delay_mS +  (1333 * Ord(Task)));        end;end; (***************)Procedure Entry_S6;(***************)Begin      PL('Entry S6');        X[Task] := 0;end; (***************)Procedure Exit_S6;(***************)Begin      PL('Exit S6');end; (***************)Procedure Do_S7;(***************)Begin        PL('Do_S7, # ' + IntToStr(X[Task]));         X[Task]:= X[Task] + 1;        if X[Task] >= Ord(High(t_Task)) + 5 - Ord(Task) then        begin                Change_State(S8);                PL('My Delay = ' + IntToStr(Delay_mS +  (1333 * Ord(Task))));        end        else        begin              Delay(Delay_mS +  (1333 * Ord(Task)));        end;end; (***************)Procedure Entry_S7;(***************)Begin      PL('Entry S7');        X[Task] := 0;end; (***************)Procedure Exit_S7;(***************)Begin      PL('Exit S7');end; (***************)Procedure Do_S8;(***************)Begin        PL('Do_S8, # ' + IntToStr(X[Task]));         X[Task]:= X[Task] + 1;        if X[Task] >= Ord(High(t_Task)) + 5 - Ord(Task) then        begin                Change_State(S9);                PL('My Delay = ' + IntToStr(Delay_mS +  (1333 * Ord(Task))));        end        else        begin              Delay(Delay_mS +  (1333 * Ord(Task)));        end;end; (***************)Procedure Entry_S8;(***************)Begin      PL('Entry S8');        X[Task] := 0;end; (***************)Procedure Exit_S8;(***************)Begin      PL('Exit S8');end; (***************)Procedure Do_S9;(***************)Begin        PL('Do_S9, # ' + IntToStr(X[Task]));         X[Task]:= X[Task] + 1;        if X[Task] >= Ord(High(t_Task)) + 5 - Ord(Task) then        begin                Change_State(S1);                PL('My Delay = ' + IntToStr(Delay_mS +  (1333 * Ord(Task))));        end        else        begin              Delay(Delay_mS +  (1333 * Ord(Task)));        end;end; (***************)Procedure Entry_S9;(***************)Begin      PL('Entry S9');        X[Task] := 0;end; (***************)Procedure Exit_S9;(***************)Begin      PL('Exit S9');end; (***************)Procedure Do_Delay_State__;(***************)Begin        PL('Do_Delay_State__, # ' + IntToStr(X[Task]));         X[Task]:= X[Task] + 1;        if X[Task] >= Ord(High(t_Task)) + 5 - Ord(Task) then        begin              Change_State(S2);                PL('My Delay = ' + IntToStr(Delay_mS +  (1333 * Ord(Task))));        end        else      begin                Delay(Delay_mS +  (1333 * Ord(Task)));        end;end; (***************)Procedure Entry_Delay_State;(***************)Begin        PL('Entry Delay_State');        X[Task] := 0;end; (***************)Procedure Exit_Delay_State;(***************)Begin        PL('Exit Delay_State');end; (***************)Procedure Entries;(***************)Begin        Case State of        S1 : Entry_S1;        S2 : Entry_S2;        S3 : Entry_S3;        S4 : Entry_S4;        S5 : Entry_S5;        S6 : Entry_S6;        S7 : Entry_S7;        S8 : Entry_S8;        S9 : Entry_S9;        Delay_State : Entry_Delay_State;         Otherwise                Write(State); PL(' = Illegal Entry state');        end;end; (***************)Procedure Exits;(***************)Begin      Case State of        S1 : Exit_S1;        S2 : Exit_S2;        S3 : Exit_S3;        S4 : Exit_S4;        S5 : Exit_S5;        S6 : Exit_S6;        S7 : Exit_S7;        S8 : Exit_S8;        S9 : Exit_S9;        Delay_State : Exit_Delay_State;                Otherwise                 Write(State); PL(' = Illegal Exit state');      end;end; (***************)Procedure Dispatch;(***************)Begin        Case State of                //Insert all possible states with Do-actions        S1 : Do_S1;      S2 : Do_S2;        S3 : Do_S3;        S4 : Do_S4;        S5 : Do_S5;      S6 : Do_S6;        S7 : Do_S7;        S8 : Do_S8;        S9 : Do_S9;        Delay_State : Do_Delay_State;         Otherwise              Print_Error('Illegal Dispatch state <' {+ Str(State)} + '>') ;      end;end; (***************)Procedure Init_FSM_Vars;(***************)Begin        Delay_mS := 2000;        States[ T1 ] := S1;        State := S1;        Task  := T1;        Entries;         States[ T2 ] := S2;        State := S2;        Task  := T2;        Entries;         States[ T3 ] := S3;        State := S3;        Task  := T3;        Entries;         States[ T4 ] := S4;      State := S4;        Task  := T4;        Entries;         States[ T5 ] := S5;        State := S5;        Task  := T5;        Entries;       States[ T6 ] := S6;        State := S6;        Task  := T6;      Entries;         States[ T7 ] := S7;        State := S7;        Task  := T7;        Entries;  End; (***************)Procedure Do_Delay_State; //This procedure will be generated double(***************)Var  KeyEvent: TKeyEvent; Begin        if millis >= Delay_Time[Task] then        begin                State := Delay_States[Task]; //Return to caller state. No exits & entries !!      end        else        begin   //This part is application dependent; in this case p resp m in/decreases the delay              if KeyPressed then                begin                        ch := KeyEventToString(TranslateKeyEvent(getkeyevent));                      PL('Key Char = <'+ ch + '>');                end                else                  ch := '';                                //write('<',ch,'>');                                //readln;              if (ch = 'p' ) then                begin                  Delay_mS := Delay_mS + 100;                  Writeln('Delay_mS = ', Delay_mS);                end                else                if (ch = 'm' ) then                begin                  Delay_mS := Delay_mS - 100;                  if Delay_mS <= 0 then                      Delay_mS := 100;                  Writeln('Delay_mS = ', Delay_mS);                end                else        if ch = 'q' then                  Change_State(Exit_State)                else      if ch = ' ' then        begin                  beep;                writeln('More :  ');                  readln;                end;              ch := '';                initkeyboard;        end;end; (***************)Procedure Delay(t : longint);(***************)Begin        Delay_States[Task] := State; //save this state        Delay_Time  [Task] := millis + t;        State := Delay_State;                                   // change to Do_Delay State.end; (***************)Procedure Change_State(Target: t_State);(***************)Begin                //      writeln('Change ', State, ' ====> ', Target);         Exits;        State := Target;              //Change the complete state        Entries;                                              //change to self-transition forces also an Exit & Entryend; (***************)//Mainloop(***************)Begin        Init_FSM_vars;                //      Change_state(Start_State);      // Enter and Initiate starting state correctly        Writeln('Demo of a Finite State Machine with 7 tasks and 10 states, independently running');        Writeln('with different delay times and # of Do_actions per thread');        Writeln('Type m = accelerating; p = decelerating; q = exit; Space = Pause');        Writeln('Type Enter to start : ');        Readln;        n := Loop_Count + 1;        Loop_Time := 0;        Repeat                millis := DateTimeToTimeStamp(Now).time;                if n > Loop_Count then              begin                        Writeln('====== Loop Time = ', (millis - Loop_Time),                                                       ' mS per ', Loop_Count, ' loops');                        Loop_Time := millis;                        n := 0;                end                else                begin                        n := n + 1;                end;                 //Do here some input action, that the FSM can react on                //Or let that action be done by interrupts of in other tasks in shared/static memory.               for Task := T1 to T7 do  //Start the FSM´s of this demo.              begin                        State := States[Task]; //restore the State                                                                                                                                            //Write('Task <', Task, '> ');                      Dispatch; //Start the FSM                        if State = Exit_State then Break;                        States[Task] := State; //save the (new) State.                 end;                sleep(9);                                                 //            Writeln('After Dispatch : ' ); Readln(Ch);                                              //Or do here some action (too)         until State = Exit_State;         Writeln('FSM Ready. Type Enter ');        Readln;end. ---------------------------------------------------------------------- 440bx · 2018-11-23 12:35 It is simply incorrect to expect a thread/task to be given CPU cycles just because it got out of a sleep state. The OS scheduler's priority is to keep the system as responsive to the user as possible.  The scheduler will prioritize a task based on what events need to be taken care of in order to keep the system running smoothly.  A process which is running at normal priority and has no events to take care of (such as pending keys, I/Os, etc) will not be scheduled ahead of other tasks which have events to service just because it came out of a sleep state. if someone wants to increase the likeliness of the scheduler giving some CPU time to their task(s), they should set the tasks priority to real time and, even that doesn't guarantee CPU time.  In addition to that, setting a process' priority to real time can easily have detrimental effects on the OS smooth operation. The sleep function isn't meant to control how the O/S scheduler decides which thread gets CPU time next.  All it does is tell the scheduler that it doesn't need attention for x amount of time.  That's all it does. Nothing else.  Just because a task could use some CPU cycles, it doesn't mean it's going to get them. HTH ---------------------------------------------------------------------- BrunoK · 2018-11-23 13:49 Intel only and maybe windows 32 only, never tested my function GetCPUTicks in 64 bit. Here after a little program that runs Sleep in a loop and pick Now / GetTickCount64 / RDTSC and displays the timings for each iteration. Conclusion : except for Sleep(0) the OS (Windows) always Sleeps IN multiple of ~14 ms, which is, if I remember well, the minimum time slice allocated to each thread in normal priority. There is nothing wrong  in Sleep(x) in FPC because FPC just calls the OS for this instruction. You'll have to use another strategy. Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i (* ======== =============================================================== *)(* prjTestSleep : This program catches timings of Sleep(x) instruction and *)(*                then prints them. Now()/GetTickCount64()/CPUTicks if     *)(*                available.                                             *)(* ==================== =================================================== *) program prjTestSleep; {$mode objfpc}{$H+} uses {$IFDEF UNIX} {$IFDEF UseCThreads}  cthreads, {$ENDIF} {$ENDIF}  SysUtils; // Utility function costs approx 26-28 Ticks{$IF DEFINED(CPUi386) or DEFINED(CPUX86_64)}{$ASMMODE Intel}  function GetCPUTicks: uint64; assembler; nostackframe;  asm    xor eax,eax    xor edx,edx    shl edx,1   // Make it a positive Int64    shr edx,1    rdtsc  {$IFDEF CPUX86_64} { NOT TESTED }    shl   rdx, 32    or    rax, rdx  {$ENDIF CPUX64}  end;{$ELSE}  function GetCPUTicks: uint64; inline;  begin    Result :=0;  end;{$ENDIF} type  RTiming = record    Last: TDateTime;  CPUTicks : int64;    LastTick64: QWord;  end; const  CMaxTestMs = 10000; // Limit for feed loop in msvar  lFirst: TDateTime;       // Now  lFirstCPUTick: int64;   // RDTSC  lFirstTick64: QWord;     // GetTickCount64  lTimeingAr: array [0..100] of RTiming;  lSleepStr: string;  lSleep: integer;  ix: integer;  lTopIx:integer;   lCPUMsPerCPU : Double;  lCPUTicksPerMsStr : string;  lDeltaCPU : int64;  procedure SetupFirstFrom(var aRTiming : RTiming);  begin    with aRTiming do begin      lFirstCPUTick := CPUTicks;         // RDTSC      lFirstTick64 := LastTick64;        // GetTickCount64      lFirst:= Last;                     // Now    end;  end; begin  // FormatSettings.ThousandSeparator:='''';  WriteLn('Maximum time allowed for FEED LOOP (ms)-> ',CMaxTestMs,' for ', High(lTimeingAr), ' items');  WriteLn('---------------------------------------');  WriteLn;  Write('Enter sleep delay ? ');  ReadLn(lSleepStr);  repeat  // Test / reenter    repeat      if TryStrToInt(lSleepStr, lSleep) then        break;      Write('Redo from start ? ');      ReadLn(lSleepStr);    until lSleepStr='';    if lSleepStr='' then    break;    // START ============ FEED DATA in Loop ============== START    lFirstTick64 := GetTickCount64; // Initialize for CMaxTestMs for loop    for ix := 0 to High(lTimeingAr) do begin      with lTimeingAr[ix] do begin        LastTick64 := GetTickCount64;        CPUTicks := GetCPUTicks;        Last := Now;        lTopIx:=ix; // To be strict on use of ix after loop        if LastTick64-lFirstTick64>CMaxTestMs then          Break;{SLEEP} Sleep(lSleep); {SLEEP}      end;    end;    // END ============== FEED DATA in Loop ================= END     SetupFirstFrom(lTimeingAr[0]);    // Adjust lTimeingAr data to store as offsets relative to start of test    for ix := 1 to lTopIx do      with lTimeingAr[ix] do begin      CPUTicks:=CPUTicks-lFirstCPUTick;        Last:=Last-lFirst;        LastTick64:=LastTick64-lFirstTick64;    end;     FillChar(lTimeingAr[0], SizeOf(RTiming), 0); // Zero base offset    SetupFirstFrom(lTimeingAr[0]);     // Estimate ratio of elapsed CPUTicks to GetTickCount64 + Some offset.    // Loop so lCPUMsPerCPU is in narrow range    with lTimeingAr[lTopIx] do begin      if LastTick64 - lFirstTick64 = 0 then        lFirstTick64 := 0      else repeat        lCPUMsPerCPU := 1 / ((CPUTicks - lFirstCPUTick) / (LastTick64 - lFirstTick64));        lDeltaCPU := Trunc((lTimeingAr[1].CPUTicks - lFirstCPUTick) * lCPUMsPerCPU);      lDeltaCPU := Trunc((lTimeingAr[1].LastTick64 - lFirstTick64) - lDeltaCPU);        // Correct start CPUTick offset (signed integer)      lDeltaCPU := Trunc(lDeltaCPU / lCPUMsPerCPU);        if lDeltaCPU=0 then          break;        lFirstCPUTick := lFirstCPUTick - lDeltaCPU;        WriteLn('CPU tick offset -> ', lFirstCPUTick);    until False;    end;     // Print results    WriteLn('Elapsed from start');    WriteLn('==================');    for ix := 1 to lTopIx do      with lTimeingAr[ix] do begin        lCPUTicksPerMsStr := FormatFloat(',.000',(CPUTicks - lFirstCPUTick)*lCPUMsPerCPU);        WriteLn(ix:4,' Now(s.)->', FormatDateTime('ss.zzz', last - lFirst): 7,                '  Tick64 ms->',(LastTick64 - lFirstTick64): 8,              '  From CPUTicks ms->',lCPUTicksPerMsStr:10);      end;    Write('Enter to exit or new sleep delay ? ');    ReadLn(lSleepStr);  until lSleepStr = '';end. ---------------------------------------------------------------------- BrunoK · 2018-11-27 10:01 A slightly corrected version in attachment.