October 21, 2021, 15:36:47

Author Topic: [Solved] BlitzMax microseconds functions  (Read 695 times)

Offline Midimaster

  • Sr. Member
  • ****
  • Posts: 363
    • Midimaster Music Education Software
[Solved] BlitzMax microseconds functions
« on: August 04, 2021, 07:38:18 »
*** EDIT ***

there is a ready to use code in post #9:


  • Enables UDelay() in BlitzMax 1.50 too
  • Enables MicroTime-stamps in both BlitzMax
  • Enables microsecond exact wake up in both BlitzMax
  • Enables measurements accurate to the microsecond in both BlitzMax



In BlitzMax NG there are is a  functions UDelay, which enable microsecond waiting.  Now I would like to find out how this is made. My idea is: Perhaps it is a Windows API function and can be enabled also in old BlitzMax 1.50

I search the files and folders inside C:\BlitzMaxNG/mod/brl.mod/blitz.mod and inside C:\BlitzMaxNG\MinGW32x64\x86_64-w64-mingw32\include\sys to find the real (deepest) source code of this function.

But I'm not very used in C and GNU and now struggle with the structure of the source files *.bmx or *.h or *.c and so on. It looks like they all only reference to a still deeper function.

Does somebody know where to search? Or does somebody know how USleep()  is made?


In blitz.bmx there is only a:
Code: [Select]
Function UDelay( microseconds:Int )="void bbUDelay(int)!"

...and over 40 Import "xxx.c" lines

In blitz_app.c I found:
Code: [Select]
void bbDelay( int millis ){
if (millis<0) return;
usleep( millis*1000 );
}

#if __STDC_VERSION__ >= 199901L
extern void bbUDelay( int microseconds );
#else
void bbUDelay( int microseconds ) {
if (microseconds <0) return
usleep( microseconds );
}
#endif

in unstd.h I found:
Code: [Select]
#if !defined __NO_ISOCEXT
#include <sys/types.h> /* For useconds_t. */

int __cdecl __MINGW_NOTHROW usleep(useconds_t);
#endif  /* Not __NO_ISOCEXT */

so I got finally "lost in sourcefiles"....




« Last Edit: August 05, 2021, 09:43:38 by Midimaster »
See my current project on PlayStore: 20Tracks-Audio-Player https://play.google.com/store/apps/details?id=midimaster.twentytrackd

Offline Henri

  • Sr. Member
  • ****
  • Posts: 355
Re: USleep()
« Reply #1 on: August 04, 2021, 15:28:21 »
Hi,

it is easy to get lost in the source :-)

This should work for both. Note that usleep has maximum parameter value limit of 999 999 microseconds, which after that it doesn't work.

Code: BlitzMax
  1.  
  2. Extern "C"
  3.         Function usleep:Int(microsecs:Int)
  4. EndExtern
  5.  
  6. Print "Waiting..."
  7.  
  8. Local start:Int = MilliSecs()
  9.  
  10. 'Delay for 5000 microsecs = 5 milliseconds
  11. usleep(5000)
  12.  
  13. Print "The End. Waiting took " + (MilliSecs()-start) + " milliseconds."
  14.  

-Henri
- Got 01100011 problems, but the bit ain't 00000001

Offline Midimaster

  • Sr. Member
  • ****
  • Posts: 363
    • Midimaster Music Education Software
Re: USleep()
« Reply #2 on: August 04, 2021, 17:10:31 »
sorry but it look like this code is not working as expected

Code: [Select]
Extern "C"
        Function usleep:Int(microsecs:Int)
EndExtern
 
Print "Waiting..."
 
Local start:Int = MilliSecs()

For Local i%=0 To 9
usleep(500)
  Print "tick"
Next
Print "The End. Waiting took " + (MilliSecs()-start) + " milliseconds."

Returns 20msec on a BlitzMax 1.50 as result. I would have expected 5msec.

Even on BlitzMax NG is seems not to work. Here it returns 0msec.

When I replace usleep with UDelay on BlitzMax NG ist return correct 5msec


I also already know the QueryPerformanceTimer(). It can be used as a workaround for pauses of microseconds. But this approach burns the time instead of sleeping and returning to the system.
« Last Edit: August 04, 2021, 17:17:24 by Midimaster »
See my current project on PlayStore: 20Tracks-Audio-Player https://play.google.com/store/apps/details?id=midimaster.twentytrackd

Offline Henri

  • Sr. Member
  • ****
  • Posts: 355
Re: USleep()
« Reply #3 on: August 04, 2021, 17:43:20 »
It is hard to measure microseconds accurately. In your loop there are additional components that have to be taken into account, like the print function and the loop itself.

Also, the system timer resolution is not accurate enough to produce exact delay, but in most case it's accurate enough.

By the way, usleep is standard C function sense C99 and is crossplatform (although there are mentions that it's obsolete, even though it functions perfectly)

-Henri
- Got 01100011 problems, but the bit ain't 00000001

Offline Midimaster

  • Sr. Member
  • ****
  • Posts: 363
    • Midimaster Music Education Software
Re: USleep()
« Reply #4 on: August 04, 2021, 17:59:05 »
The loop...
Code: [Select]
Local start:Int = MilliSecs()
For Local i%=0 To 9
Print "tick"
Next
Print "The End. Waiting took " + (MilliSecs()-start) + " milliseconds."
shows as result 0. So it needs below 1 msec. I can write For  i%=0 to 299 and it still needs below 1 msec.

If I write this:
Code: [Select]
Local start:Int = MilliSecs()
usleep(1)
Print "The End. Waiting took " + (MilliSecs()-start) + " milliseconds."
it show 2msec. It looks like every call of the usleep() needs 2msec. Instead of value=1 I can use any value from 1 to 1999 it shows always 2msec.

It looks like the function is existing in BlitzMax 1.50 but not implemented correct. Remember that BlitzMax 1.50 does not compile with GCC but FASM!!!

I think I need to find the code of the C-function and some H-files to get this working.
« Last Edit: August 04, 2021, 18:01:43 by Midimaster »
See my current project on PlayStore: 20Tracks-Audio-Player https://play.google.com/store/apps/details?id=midimaster.twentytrackd

Offline Henri

  • Sr. Member
  • ****
  • Posts: 355
Re: USleep()
« Reply #5 on: August 04, 2021, 18:28:44 »
I don't think the issue is with usleep. The issue is with millisecs that is not accurate enough to measure fraction of a milliseconds. It rounds those up or down.

If I run this and change the delay on 1000 microsecond intervals (that is 1 millisecond) I get exactly 1 millisecond intervals. If I change the delay to 2000 microseconds I get 2 milliseconds and so on in my Bmax ng:

Code: BlitzMax
  1. Extern "C"
  2.         Function usleep:Int(microsecs:Int)
  3. EndExtern
  4.  
  5. Print "Waiting..."
  6.  
  7. Local ar:Int[10]
  8.  
  9. Local now:Int
  10. Local start:Int = MilliSecs()
  11.  
  12. For Local i:Int = 0 To 9
  13.         usleep(1000)
  14.         ar[i] = MilliSecs()
  15. Next
  16.  
  17. For Local t:Int = EachIn ar
  18.         Print t + " milliseconds"
  19. Next
  20.  
  21. Print "The End. Waiting took " + (MilliSecs()-start) + " milliseconds."
  22.  

-Henri
- Got 01100011 problems, but the bit ain't 00000001

Offline Midimaster

  • Sr. Member
  • ****
  • Posts: 363
    • Midimaster Music Education Software
Re: USleep()
« Reply #6 on: August 04, 2021, 18:51:59 »
But we are talking about BlitzMax 1.50! Do you really test on 1.50?

In BlitzMax NG I dont need this function, because UDelay works perfect!!!

To eliminate Millisecs()-Timer inaccuracy, I tested with a FOR/NEXT-loop.

100 times a usleep of 50 should result in 5msec. But it gives 200msec!!!
100 times a usleep of 500 should result in 50msec. But it gives 200msec!!!
100 times a usleep of 1000 should result in 100msec. But it gives 200msec!!!

And testing it with a parameter of 1000 or 2000 is not usefull to prove working MicroSeconds. It looks like the usleep() is internaly replaced by a simple Millisecs() call.

Exact your code (parameter=1000) results at my computer in:
Code: [Select]
45983103 milliseconds
45983105 milliseconds
45983107 milliseconds
45983109 milliseconds
45983111 milliseconds
45983113 milliseconds
45983115 milliseconds
45983117 milliseconds
45983119 milliseconds
45983121 milliseconds
The End. Waiting took 20 milliseconds.

Can you please tell me, what your code example returns when parameter=500?



« Last Edit: August 04, 2021, 18:55:30 by Midimaster »
See my current project on PlayStore: 20Tracks-Audio-Player https://play.google.com/store/apps/details?id=midimaster.twentytrackd

Offline Henri

  • Sr. Member
  • ****
  • Posts: 355
Re: USleep()
« Reply #7 on: August 04, 2021, 20:07:46 »
I think I know where the confusion is coming from.

NG is not using standard library usleep in Windows environment for UDelay, only in Linux and Mac.

Instead, it uses a counter:

Code: [Select]
void bbUDelay( int microseconds ) {
__int64 time1 = 0;
__int64 time2 = 0;
__int64 freq = 0;

QueryPerformanceCounter((LARGE_INTEGER *) &time1);
QueryPerformanceFrequency(&freq);

do {
Sleep(0);
QueryPerformanceCounter((LARGE_INTEGER *) &time2);
} while(time2-time1 < microseconds*freq/1000000);
}

You would only need to adopt this code to make it work in 1.50 for Windows (and use usleep for others). Sounds reasonable ?

-Henri
« Last Edit: August 04, 2021, 20:10:18 by Henri »
- Got 01100011 problems, but the bit ain't 00000001

Offline Midimaster

  • Sr. Member
  • ****
  • Posts: 363
    • Midimaster Music Education Software
Re: USleep()
« Reply #8 on: August 04, 2021, 23:39:48 »
Ah! Thanks a lot. That was what I was searching for. I did not found it.

I knew already the QueryPerformanceCounter, but I did not know that I can use it in combination with a Sleep(0).

Sleep(0) could be a solution. I will make some test how exact this is for times below 1msec.
« Last Edit: August 05, 2021, 02:21:25 by Midimaster »
See my current project on PlayStore: 20Tracks-Audio-Player https://play.google.com/store/apps/details?id=midimaster.twentytrackd

Offline Midimaster

  • Sr. Member
  • ****
  • Posts: 363
    • Midimaster Music Education Software
Re: USleep()
« Reply #9 on: August 05, 2021, 02:17:30 »
Microseconds in BlitzMax

This code enable micro second access in both BlitzMax versions. Similar to functions like UDelay(). I added code examples for each function.

Code: BlitzMax
  1.        
  2. Type MicroTimer
  3.         ' enables micro-second access in BlitzMax
  4.         '
  5.         ' Functions
  6.         ' ---------------------------------------------------------------------------------
  7.         '     Now:Long()                  returns a timestamp in Microseconds
  8.         '
  9.         '     SleepUntil(MicroTime:Long)  sleeps until the current time MicroTime is reached
  10.         '
  11.         '     GetDelta:Long()             returns the MicroSeconds since last GetDelta()-Call
  12.         '
  13.         '     UDelay(MicroSeconds:Int)     pauses for n MicroSeconds
  14.         '
  15.         ' -------------------------------------------------------------------------------
  16.         Global FREQUENCE:Long=0
  17.         Global Last:Long=0
  18.  
  19.        
  20.         Function Now:Long()
  21.                 ' PUBLIC:
  22.                 ' returns a timestamp in Microseconds (1000000 equals 1sec)
  23.                 '         since the computer has started
  24.                 '
  25.                 '         example:
  26.                 '         ******************************************
  27.                 '            WhatsTheTime:Long = MicroTimer.Now()
  28.                 '            PRINT "time is : " + WhatsTheTime
  29.                 '         ******************************************
  30.                 '
  31.                 Local locNow:Long = 0
  32.                 If FREQUENCE = 0
  33.                         If Not QueryPerformanceFrequency(Varptr(FREQUENCE)) End
  34.                 EndIf
  35.                 QueryPerformanceCounter(Varptr(locNow))
  36.                 locNow = locNow*1000000/FREQUENCE
  37.                 Return locNow
  38.         End Function
  39.  
  40.  
  41.  
  42.         Function SleepUntil(MicroTime:Long)
  43.                 ' PUBLIC:
  44.                 ' sleeps until the current time MicroTime is reached
  45.                 '
  46.                 '         example:
  47.                 '         ******************************************
  48.                 '            local Later:Long = MicroTimer.Now()+500   ' +0.5msec
  49.                 '            ' do some code....
  50.                 '            PRINT "hello"
  51.                 '            MicroTimer.SleepUntil(Later)              ' now wait
  52.                 '         ******************************************  
  53.                 '
  54.                 While Now() < MicroTime
  55.                         If Now()< MicroTime-1000
  56.                                 Delay 1
  57.                         EndIf
  58.                 Wend   
  59.         End Function
  60.  
  61.  
  62.         Function GetDelta:Long()
  63.                 ' PUBLIC:
  64.                 ' returns the MicroSeconds since last GetDelta()-Call
  65.                 '
  66.                 '         example:
  67.                 '         ******************************************
  68.                 '            MicroTimer.GetDelta()    ' reset
  69.                 '            ' now lets check the performace of this code lines....
  70.                 '            For i=0 to 999
  71.                 '                PRINT "hello"
  72.                 '            Next
  73.                 '            PRINT "code needed " + MicroTimer.GetDelta() + "microseconds"
  74.                 '         ******************************************
  75.                 '  
  76.                 Local locNow:Long = now()
  77.                 Local Delta:Long  = locNow-Last
  78.                 Last = locNow
  79.                 Return Delta
  80.         End Function
  81.  
  82.        
  83.         Function UDelay(MicroSeconds:Int)
  84.                 ' PUBLIC:
  85.                 ' pauses for n MicroSeconds
  86.                 '
  87.                 '         example:
  88.                 '         ******************************************
  89.                 '            For i=0 to 9
  90.                 '                PRINT "hello"
  91.                 '                MicroTimer.UDelay(50)   ' pauses for 0.05msec
  92.                 '            Next
  93.                 '         ******************************************
  94.                 '  
  95.                 Local locNow:Long = now() + MicroSeconds
  96.                 SleepUntil locNow      
  97.         End Function
  98. End Type        
  99.  
  100. Extern "win32"
  101.                 Function QueryPerformanceCounter:Int(out:Long Ptr)
  102.                 Function QueryPerformanceFrequency:Int(out:Long Ptr)
  103. End Extern
  104.  
  105.  
  106.        
  107.  
« Last Edit: August 05, 2021, 09:57:20 by Midimaster »
See my current project on PlayStore: 20Tracks-Audio-Player https://play.google.com/store/apps/details?id=midimaster.twentytrackd

Offline Henri

  • Sr. Member
  • ****
  • Posts: 355
Good job.

One observation:

It looks that calling Delay(0) in SleepUntil function will not free system resources as it just seems to return immediately if zero value was passed. You might need to add sleep function and call that instead (sleep(0) or usleep(1) which is the minimum value)

-Henri
- Got 01100011 problems, but the bit ain't 00000001

Offline Midimaster

  • Sr. Member
  • ****
  • Posts: 363
    • Midimaster Music Education Software
Oh yes, you are right! It makes no difference wether I use...

Code: [Select]
While Now() < MicroTime
Delay 0
Wend
or
Code: [Select]
While Now() < MicroTime
sleep 0
Wend
or
Code: [Select]
While Now() < MicroTime
' do nothing
Wend

In all three cases the processor goes upto 23%. But the exactness is 100% as expected.

Using a sleep(1) or usleep(1) makes no sense, because then the exactness gets lost.

So it looks like a delay call with value 0 does not really help to save resources. So we still search for a solution below Milliseconds. Maybe that the same happens on BlitzMax NG?


It's the same! Also on BlitzMax NG the processor goes at 25% with this code:

Code: BlitzMax
  1. Graphics 800,600
  2.  
  3. Repeat
  4.         Local start:Int = MilliSecs()
  5.         For Local i%=0 To 99
  6.                 UDelay 50
  7.         Next
  8.         Print "The End. Waiting took " + (MilliSecs()-start) + " milliseconds."
  9. Until AppTerminate()


***EDIT***

I updated the code in post #9. Now at least it saves performance when the delaytime is bigger than 1000 microseconds.




« Last Edit: August 05, 2021, 10:01:23 by Midimaster »
See my current project on PlayStore: 20Tracks-Audio-Player https://play.google.com/store/apps/details?id=midimaster.twentytrackd

Offline Baggey

  • Full Member
  • ***
  • Posts: 183
Re: [Solved] BlitzMax microseconds functions
« Reply #12 on: August 06, 2021, 20:29:05 »
Cool keep up the good work! I hope this microseconds function will work. I Think this maybe usefull for my SpecBlitz emulator and the RingBuffer!  :)

Kind Regards Baggey
Currently Running a PC that just Aint fast enough!?
ZX Spectrum 48k, NEXT, C64, ORIC Atmos 48K, Enterprise 128K, The SID chip.

Jesus was only famous because of his DAD.

 

SimplePortal 2.3.6 © 2008-2014, SimplePortal