wth -- Thread time .vs Subroutine time

General FreeBASIC programming questions.
Post Reply
integer
Posts: 408
Joined: Feb 01, 2007 16:54
Location: usa

wth -- Thread time .vs Subroutine time

Post by integer »

The initial thought: threads should run faster than the main program with no system interrupts.

The routine runs through about 10 million calcs in two versions. There should be several processor cycles difference
in the for/next loops. Thus I expected about half a millisecond (0.5) difference in the timing between the two calcs.
Then -- what would the time be for a thread?

It just seemed that threads would require less time.
HOWEVER!

The thread calls use approx 30 millisecs more than a subroutine call.
Therefore: 1st question, what am I doing wrong?

Any help/explanation is appreciated.

This is the snippet:

Code: Select all

''  ThreadExample_04f.bas

/'
    Are threads isolated from system interrupts?    
'/

Dim Shared terminate As Integer = 0
Dim Shared mutex As Any Ptr
dim shared ThreadTime1 as double = 0
dim shared ThreadTime2 as double = 0
dim shared MilliSecsThread1 as double = 1000.00#
dim shared MilliSecsThread2 as double = 1000.00#
dim shared SubRouteTime1 as double = 1234.00#
dim shared SubRouteTime2 as double = 4321.00#


SUB Routine1 (ByVal param As Any Ptr)
    dim as double t1, t0 = timer
    dim as longint i, j, k, sum
    for i = 1ul to 100ul
        for j = 1ul to 100ul
            for k = 1ul to 1000ul
                sum = sum + 1ul
            next k
        next j
    next i
    t1 = timer
    SubRouteTime1 = (t1 - t0) * 1000.0#
END SUB


Sub Routine2(ByVal param As Any Ptr)  
    dim as double t1, t0 = timer
    dim as longint i, sum
    for i = 1ul to 10000000ul
        sum = sum + 1ul
    next i
    t1 = timer
    SubRouteTime2 = (t1 - t0) * 1000.0#
end sub


Sub thread1(ByVal param As Any Ptr)  
    dim as double t1, t0=timer
    dim as longint i, j, k, sum
    for i = 1 to 100ul
        for j = 1 to 100ul
            for k = 1 to 1000ul
                sum = sum + 1ul
            next k
        next j
    next i
    t1 = timer
    MilliSecsThread1 = (t1 - t0) * 1000.0#
End Sub


Sub thread2 (ByVal param As Any Ptr)  
    dim as double t1, t0 = timer
    dim as longint i, j, k, sum
    for i = 1 to 10000000ul
        sum = sum + 1ul
    next i
    t1 = timer
    MilliSecsThread2 = (t1 - t0) * 1000.0#
End Sub


    'ScreenSet 1, 0
    dim as double t2, t1, t0=timer
    dim as longint i, j, k, sum, z
    dim p0 as any ptr

    for example as integer = 1 to 7
        print
        print "============================"

        mutex = MutexCreate
        Dim p1 As Any Ptr = ThreadCreate(@thread1)
        Dim p2 As Any Ptr = ThreadCreate(@thread2)
        ThreadWait (p1)
        ThreadWait (p2)
        MutexDestroy(mutex)
        print
        print "1st thread millisec: "; MilliSecsThread1
        print "2nd thread millisec: "; MilliSecsThread2
          
        Routine1(@thread1)
        Routine2(@thread1)
        print " "
        print "1st Subr.  millisec: "; SubRouteTime1
        print "2nd Subr.  millisec: "; SubRouteTime2
        
    next example
    
    print
    Sleep
And this is the result:
  • ============================

    1st thread millisec: 118.6101293133106
    2nd thread millisec: 108.4680745843798

    1st Subr. millisec: 84.03204906062456
    2nd Subr. millisec: 83.2177554621012

    ============================

    1st thread millisec: 113.362805925135
    2nd thread millisec: 108.5327140317531

    1st Subr. millisec: 83.11773875902873
    2nd Subr. millisec: 83.36668548872694

    ============================

    1st thread millisec: 112.129521148745
    2nd thread millisec: 108.6146588277188

    1st Subr. millisec: 82.2372716065729
    2nd Subr. millisec: 81.33534752414562

    ============================

    1st thread millisec: 111.3291545843822
    2nd thread millisec: 108.596812140604

    1st Subr. millisec: 80.87254828569712
    2nd Subr. millisec: 82.49425817484735

    ============================

    1st thread millisec: 109.790080532548
    2nd thread millisec: 110.5830765882274

    1st Subr. millisec: 81.50618391664466
    2nd Subr. millisec: 80.93303715577349

    ============================

    1st thread millisec: 108.683260041289
    2nd thread millisec: 112.9249508812791

    1st Subr. millisec: 106.5744509833166
    2nd Subr. millisec: 106.3130789261777

    ============================

    1st thread millisec: 113.0837071395945
    2nd thread millisec: 109.0830341490801

    1st Subr. millisec: 92.11460369260749
    2nd Subr. millisec: 80.87781627546065

    ============================
speedfixer
Posts: 606
Joined: Nov 28, 2012 1:27
Location: CA, USA moving to WA, USA
Contact:

Re: wth -- Thread time .vs Subroutine time

Post by speedfixer »

Creating a thread and creating/calling mutex routines in most languages usually use OS services - and then must incur an extra overhead.

david
fxm
Moderator
Posts: 12081
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: wth -- Thread time .vs Subroutine time

Post by fxm »

Running main code or running an explicit thread from ThreadCreate() is equivalent because the main code is already an implicit thread created on startup.
Two equivalent tasks executed by the main code and an explicit thread will take equivalent times.
The only advantage of multi-threading is to be able to parallelize tasks (using a multi-core processor), so that the total time is no longer equal to the sum of the times of each task.

In your example, you have to compare the total time taken to execute the two Threads with the total time taken to execute the two Subs:

Code: Select all

SUB Routine1 (ByVal param As Any Ptr)
    dim as longint i, j, k, sum
    for i = 1ul to 100ul
        for j = 1ul to 100ul
            for k = 1ul to 1000ul
                sum = sum + 1ul
            next k
        next j
    next i
END SUB

Sub Routine2(ByVal param As Any Ptr)
    dim as longint i, sum
    for i = 1ul to 10000000ul
        sum = sum + 1ul
    next i
end sub

Sub thread1(ByVal param As Any Ptr) 
    dim as longint i, j, k, sum
    for i = 1 to 100ul
        for j = 1 to 100ul
            for k = 1 to 1000ul
                sum = sum + 1ul
            next k
        next j
    next i
End Sub

Sub thread2 (ByVal param As Any Ptr) 
    dim as longint i, j, k, sum
    for i = 1 to 10000000ul
        sum = sum + 1ul
    next i
End Sub


for example as integer = 1 to 7
    Dim As Any Ptr p1, p2
    Dim As Double t

    print
    print "========================================="

    t = Timer
    p1 = ThreadCreate(@thread1)
    p2 = ThreadCreate(@thread2)
    ThreadWait (p1)
    ThreadWait (p2)
    t = Timer - t
    print
    print "thread1&2 (millisec): "; t * 1000

    t = Timer
    Routine1(@thread1)
    Routine2(@thread1)
    t = Timer - t
    print
    print "Routine1+2 (millisec): "; t * 1000

next example

print
Sleep
deltarho[1859]
Posts: 4292
Joined: Jan 02, 2017 0:34
Location: UK
Contact:

Re: wth -- Thread time .vs Subroutine time

Post by deltarho[1859] »

The overhead of Threadcreate is expensive when the thread is short-lived. In this case, it is worth looking at thread pooling.

Here is fxm's code with thread pooling added.

I won't publish my results as my machine is clearly much faster than integer's machine save to say the average PoolThread1&2 is about seven times faster than the average thread1&2. [ gcc 8.3/-O3 ]

The value of sum was 10,000,000 for the six subs. I checked that to make sure that the work done in the pool threads was the same as the other subs.

I used thread pooling in CryptoRndII. CryptoRnd was slower than Merseene Twister with thread creation. CryptoRndII left Mersenne twister standing. Why threads? Whilst one buffer was being exhausted another was being populated in another thread of execution. Actually, the buffers were split in two and populated by two threads of execution.

Code: Select all

Const _WIN32_WINNT = &h0600
#include once "windows.bi"

SUB Routine1 (ByVal param As Any Ptr)
    dim as longint i, j, k, sum
    for i = 1ul to 100ul
        for j = 1ul to 100ul
            for k = 1ul to 1000ul
                sum = sum + 1ul
            next k
        next j
    next i
END SUB

Sub Routine2(ByVal param As Any Ptr)
    dim as longint i, sum
    for i = 1ul to 10000000ul
        sum = sum + 1ul
    next i
end sub

Sub thread1(ByVal param As Any Ptr)
    dim as longint i, j, k, sum
    for i = 1 to 100ul
        for j = 1 to 100ul
            for k = 1 to 1000ul
                sum = sum + 1ul
            next k
        next j
    next i
End Sub

Sub thread2 (ByVal param As Any Ptr)
    dim as longint i, j, k, sum
    for i = 1 to 10000000ul
        sum = sum + 1ul
    next i
End Sub

Sub PoolThread1(Byval Instance As PTP_CALLBACK_INSTANCE, Byval Context As PVOID Ptr, Byval Work As PTP_WORK)
dim as longint i, j, k, sum
for i = 1 to 100ul
  for j = 1 to 100ul
    for k = 1 to 1000ul
      sum = sum + 1ul
    next k
  next j
next i
End Sub

Sub PoolThread2(Byval Instance As PTP_CALLBACK_INSTANCE, Byval Context As PVOID Ptr, Byval Work As PTP_WORK)
dim as longint i, j, k, sum
for i = 1 to 10000000ul
  sum = sum + 1ul
next i
End Sub

' Thread pooling. This takes very little time to set up - about 30 micro-seconds on my machine.
Dim Pool As PTP_POOL
Dim As PTP_WORK Work1, Work2
Pool = CreateThreadpool(Null)
Work1 = CreateThreadpoolWork(Cast(PTP_WORK_CALLBACK, @PoolThread1), Null, Null)
Work2 = CreateThreadpoolWork(Cast(PTP_WORK_CALLBACK,@PoolThread2), Null, Null)

for example as integer = 1 to 7
    Dim As Any Ptr p1, p2
    Dim As Double t

    print
    print "========================================="

    t = Timer
    p1 = ThreadCreate(@thread1)
    p2 = ThreadCreate(@thread2)
    ThreadWait (p1)
    ThreadWait (p2)
    t = Timer - t
    print
    print "thread1&2 (millisec): "; t * 1000

    t = Timer
    Routine1(@thread1)
    Routine2(@thread2)
    t = Timer - t
    print
    print "Routine1+2 (millisec): "; t * 1000
    
    t = Timer
    SubmitThreadpoolWork(Work1)
    SubmitThreadpoolWork(Work2)
    WaitForThreadpoolWorkCallbacks(Work1,FALSE)
    WaitForThreadpoolWorkCallbacks(Work2,FALSE)
    t = Timer - t
    Print
    Print "PoolThread1&2 (millisec): "; t * 1000
next example

' Tidyup thread pooling
CloseThreadpoolWork(Work1)
CloseThreadpoolWork(Work2)
CloseThreadpool(Pool)

Sleep
fxm
Moderator
Posts: 12081
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: wth -- Thread time .vs Subroutine time

Post by fxm »

deltarho[1859] wrote:The overhead of Threadcreate is expensive when the thread is short-lived. In this case, it is worth looking at thread pooling.
3 months ago, I proposed a pure FreeBASIC structure (a TYPE) to separate the initialization part of a user thread from the start part of the user thread:

Code: Select all

Type ThreadInitThenStart
    Public:
        Declare Function ThreadInit(Byval pThread As Sub(Byval As Any Ptr), Byval p As Any Ptr = 0) As Any Ptr
        Declare Sub ThreadStart()
    Private:
        Dim As Sub(Byval p As Any Ptr) _pThread
        Dim As Any Ptr _p
        Dim As Any Ptr _mutex
        Declare Static Sub _Thread(Byval p As Any Ptr)
End Type

Function ThreadInitThenStart.ThreadInit(Byval pThread As Sub(Byval As Any Ptr), Byval p As Any Ptr = 0) As Any Ptr
    If This._mutex = 0 Then
        This._mutex = Mutexcreate()
        Mutexlock(This._mutex)
    End If
    This._pThread = pThread
    This._p = p
    Return Threadcreate(@ThreadInitThenStart._Thread, @This)
End Function

Sub ThreadInitThenStart.ThreadStart()
    If This._mutex > 0 Then
        Mutexunlock(This._mutex)
    End If
End Sub

Sub ThreadInitThenStart._Thread(Byval p As Any Ptr)
    Dim As ThreadInitThenStart Ptr pThis = p
    Mutexlock(pThis->_mutex)
    Mutexunlock(pThis->_mutex)
    Mutexdestroy(pThis->_mutex)
    pThis->_mutex = 0
    pThis->_pThread(pThis->_p)
End Sub

'--------------------------------------------------

Sub UserThread (Byval p As Any Ptr)
    Dim As Zstring Ptr pz = p
    Print *pz
End Sub

Dim As ThreadInitThenStart t1
Dim As Any Ptr pt1 = t1.ThreadInit(@UserThread, @"user thread code body starts")  '' initializes the user thread

Sleep
t1.ThreadStart()  '' starts the user thread code body

Threadwait(pt1)   '' waits for the thread end
Sleep
[edit]
See the principle in my post below.
Last edited by fxm on Jan 28, 2021 22:14, edited 1 time in total.
deltarho[1859]
Posts: 4292
Joined: Jan 02, 2017 0:34
Location: UK
Contact:

Re: wth -- Thread time .vs Subroutine time

Post by deltarho[1859] »

Blimey, fxm, that is heavy stuff.

I hit a hurdle with 'Dim As Sub(Byval p As Any Ptr) _pThread'. 'Dim As Sub' is not used that often and 'Sub Pointer' in the manual has an optional [= initializer]. Where is the '=' I thought.

It then dawned on me that the statement could have been written as: 'Dim _pThread As Sub(Byval p As Any Ptr)' and there is no initializer. Image Sometimes FreeBASIC is too flexible for its own good.

Eventually, I got to 'pThis->_pThread(pThis->_p)'. Now, I'm OK with (*p).member = <whatever> but I will not use p->member = <whatever> even if it is two characters less. '->' is straight out of Kernighan and Ritchie, and it can stay there as far as I am concerned. Mind you, so is *p.

Anyway I finally got to the end of the code in one piece. All I have to do now is fathom out what the code does. I think when I have finished the mug of Yorkshire Gold in front of me I will have to put the kettle on again and get a teapot out.

I hope that fxm does not think that I am 'having a go', I am not. He writes code using FreeBASIC in all its glory - and if anyone can do that he can, being the Manual's editor-in-chief so to speak. I am certain that some, if not many, members will see those 54 lines as perfectly readable code. I don't, perhaps I will eventually. Some members will look at those 54 lines and think: "Oh dear, I don't think so." That is a pity because they may miss out on something useful to them.

fxm wrote "3 months ago". I found it here. Only Roland Chastain responded to it.

So, what to do?

fxm and others could 'dumb down' their code. "Off with his head!" I hear someone cry. A few years ago I read a thought experiment by Albert Einstein. I understood every word. After that, I saw a mathematical proof in one of his papers and I did not understand a word of that. Einstein was a bright spark and got quite a few awards including a Nobel Prize for Physics. Richard Feynman was renowned for his entertaining lectures and ability to describe issues that the 'great unwashed' could understand. He also got a Nobel Prize for Physics. So 'dumbing down' can be done.

Obviously, it may not be possible to dumb down some code. A question a poster must ask themselves is: "Will folk understand what I have just done?". If the answer is perhaps not then commenting should be employed. For a large comment, we could use something like [1] in the code with [1] at the end of the post with a hefty explanation.

Food for thought?
fxm
Moderator
Posts: 12081
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: wth -- Thread time .vs Subroutine time

Post by fxm »

deltarho[1859] wrote:A question a poster must ask themselves is: "Will folk understand what I have just done?". If the answer is perhaps not then commenting should be employed.
' ThreadInitThenStart ' UDT (see my post above)

Using the 'ThreadInitThenStart' UDT (the two public member procedures):
- The first procedure ('ThreadInit()') allows the user to first initialize a thread for a subroutine.
- The second procedure ('ThreadStart()') allows the user to then start this subroutine execution.

Main principle:
- 'ThreadInit()' locks a mutex ('_mutex') and starts a hidden thread ('_Thread()' subroutine).
- This hidden thread waits for mutex unlocking before calling the user subroutine.
- 'ThreadStart()' unlocks the mutex, and so authorizes the hidden thread to call the user subroutine.
- The 'Sub pointer' passed to 'ThreadInit()' (first argument) is memorized ('_pThread') in order to be used (by the hidden thread) to call the user subroutine.
- The 'any pointer' passed to 'ThreadInit()' (second argument) is memorized ('_p') in order to be passed (by the hidden thread) to the user subroutine called.
- As the hidden thread subroutine ('_Thread()') must be a static procedure, a pointer to the UDT instance is passed to it so that it can access the memorized member data for calling the user subroutine.
deltarho[1859]
Posts: 4292
Joined: Jan 02, 2017 0:34
Location: UK
Contact:

Re: wth -- Thread time .vs Subroutine time

Post by deltarho[1859] »

Great stuff, fxm.

If my understanding is correct does the following pseudocode make sense?

Code: Select all

PSEUDOCODE
 
Primary thread of execution
 
This will be used in a secondary thread of execution
Sub UserThread
  This sub hashes a ciphertext buffer
End Sub
 
Dim As ThreadInitThenStart t1
Dim As Any Ptr pt1 = t1.ThreadInit()
 
Do
  Fill buffer with ciphertext (from file)
  Decrypt ciphertext buffer
  t1.ThreadStart()  '' starts the user thread code body
  Threadwait(pt1)   '' waits for the thread end
  If hashing finishes first then we fall through else we wait until hashing completes
Until EOF

Print "Job done"
Sleep
fxm
Moderator
Posts: 12081
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: wth -- Thread time .vs Subroutine time

Post by fxm »

As my present 'ThreadInitThenStart' TYPE is designed, it does not allow you to restart the same user subroutine with only 'ThreadStart()' without go back first to 'ThreadInit()'.
deltarho[1859]
Posts: 4292
Joined: Jan 02, 2017 0:34
Location: UK
Contact:

Re: wth -- Thread time .vs Subroutine time

Post by deltarho[1859] »

fxm wrote:without go back first to 'ThreadInit()'.
Had the pseudocode been OK then we would have only incurred one Threadcreate and that would have been 'Breaking News'. Image
fxm
Moderator
Posts: 12081
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: wth -- Thread time .vs Subroutine time

Post by fxm »

deltarho[1859] wrote:Had the pseudocode been OK then we would have only incurred one Threadcreate and that would have been 'Breaking News'. Image
Another TYPE version with multiple start possible: ' ThreadInitThenMultiStart '

- A ThreadWait() member is added to wait for the end of the user subroutine code.
- 3 mutexes are now implemented (by self lock and mutual unlock) to impose the ordered sequence (for the 3 methods): ThreadInit, [user code,] ThreadStart, [user code,] ThreadWait, [user code,] ThreadStart, [user code,] ThreadWait, [user code,] .....
- A constructor is added to create the 3 mutexes.
- A destructor is added to stop the hidden thread (only one used per instance), then destroy the 3 mutexes.

- For each 'ThreadInitThenMultiStart' instance, a hidden thread is started on the 'ThreadInit()' method (calling the ThreadCreate keyword), then the user thread subroutine is started on the 'ThreadStart()' method.
- After a 'ThreadStart...ThreadWait' sequence, a new user thread subroutine can be initialized by calling the 'ThreadInit()' method again on the same instance (without calling the ThreadCreate keyword again) then started using always the same hidden thread.
- On the other hand, ThreadStart...ThreadWait sequences can also be chained on different instances already initialized.
- If using several instances (so several hidden threads), the ordered sequences on each instance can be interlaced between instances because executing code on different instances.
- By creating several instances each associated with a hidden thread, we can obtain a kind of thread pooling feature.

Code: Select all

Type ThreadInitThenMultiStart
    Public:
        Declare Constructor()
        Declare Sub ThreadInit(Byval pThread As Sub(Byval As Any Ptr), Byval p As Any Ptr = 0)
        Declare Sub ThreadStart()
        Declare Sub ThreadWait()
        Declare Destructor()
    Private:
        Dim As Sub(Byval p As Any Ptr) _pThread
        Dim As Any Ptr _p
        Dim As Any Ptr _mutex1
        Dim As Any Ptr _mutex2
        Dim As Any Ptr _mutex3
        Dim As Any Ptr _pt
        Dim As Byte _end
        Declare Static Sub _Thread(Byval p As Any Ptr)
End Type

Constructor ThreadInitThenMultiStart()
    This._mutex1 = Mutexcreate()
    Mutexlock(This._mutex1)
    This._mutex2 = Mutexcreate()
    Mutexlock(This._mutex2)
    This._mutex3 = Mutexcreate()
    Mutexlock(This._mutex3)
End Constructor

Sub ThreadInitThenMultiStart.ThreadInit(Byval pThread As Sub(Byval As Any Ptr), Byval p As Any Ptr = 0)
    This._pThread = pThread
    This._p = p
    If This._pt = 0 Then
        This._pt = Threadcreate(@ThreadInitThenMultiStart._Thread, @This)
        Mutexunlock(This._mutex3)
    End If
End Sub

Sub ThreadInitThenMultiStart.ThreadStart()
    Mutexlock(This._mutex3)
    Mutexunlock(This._mutex1)
End Sub

Sub ThreadInitThenMultiStart.ThreadWait()
    Mutexlock(This._mutex2)
    Mutexunlock(This._mutex3)
End Sub

Sub ThreadInitThenMultiStart._Thread(Byval p As Any Ptr)
    Dim As ThreadInitThenMultiStart Ptr pThis = p
    Do
        Mutexlock(pThis->_mutex1)
        If pThis->_end = 1 Then Exit Sub
        pThis->_pThread(pThis->_p)
        Mutexunlock(pThis->_mutex2)
    Loop
End Sub

Destructor ThreadInitThenMultiStart()
    If This._pt > 0 Then
        This._end  = 1
        Mutexunlock(This._mutex1)
        .ThreadWait(This._pt)
    End If
    Mutexdestroy(This._mutex1)
    Mutexdestroy(This._mutex2)
    Mutexdestroy(This._mutex3)
End Destructor    

'--------------------------------------------------

Sub UserThread (Byval p As Any Ptr)
    Dim As Zstring Ptr pz = p
    Print *pz
End Sub

Dim As ThreadInitThenMultiStart t1
t1.ThreadInit(@UserThread, @"user thread code body multi-start")  '' initializes the user thread
Print "Waiting for any key"
Print
Sleep                                                             '' waits for user inkey

For I As Integer = 1 To 8
    Print I & ": ";
    t1.ThreadStart()  '' starts the user thread code body
    t1.Threadwait()   '' waits for the user thread code end
Next I

Print
Print "Sequence completed"
Sleep
[edit]
'ThreadInitThenMultiStart' UDT improved.
Last edited by fxm on Jan 30, 2021 9:30, edited 8 times in total.
deltarho[1859]
Posts: 4292
Joined: Jan 02, 2017 0:34
Location: UK
Contact:

Re: wth -- Thread time .vs Subroutine time

Post by deltarho[1859] »

Now that is 'Breaking News'. Image

ThreadStart is now similar to thread pooling's Submit.

With regard the pseudocode we should be able to process, for example, 512 x 128KB buffers incurring just one ThreadCreate as opposed to 512. Potentially that should give quite a performance boost.

Of course 'proof of the pudding is in the eating' so extensive tests are now required.

Your original code, fxm, is useful where, for example, we have two tasks, one taking four seconds and another taking two seconds. With synchronous coding, we are looking at six seconds in total but only four seconds plus the ThreadCreate time with asynchronous coding. Your new code comes into its own where we are hashing a 4GB, say, file using a 128KB buffer, for example.

I may be wrong but as far as I know what you have done here has never been done before using ThreadCreate. Microsoft, for one, would be more than interested. Image

What we could with is a group of folk to test your latest code. However, I cannot see that happening on this forum. If this was the PowerBASIC forum I could reel off quite a few names who would be very interested in participating.
deltarho[1859]
Posts: 4292
Joined: Jan 02, 2017 0:34
Location: UK
Contact:

Re: wth -- Thread time .vs Subroutine time

Post by deltarho[1859] »

Here is a simple test. UserThread is invoked eight times but CreateThread is executed only once. Amazing. Image

Code: Select all

Sub UserThread (Byval p As Any Ptr)
Dim As Ulong i
Dim as Double t, x
t = Timer
For i = 1 to 10*10^6
  x = Rnd
Next
t = Timer - t
Print t
End Sub
and I get this

Code: Select all

Waiting for any key
 
1:  0.1138386999955401
2:  0.1138629000633955
3:  0.1138572000199929
4:  0.1138538999948651
5:  0.1138182999566197
6:  0.1140423999167979
7:  0.1140621999511495
8:  0.1138270000228658
 
Sequence completed
fxm
Moderator
Posts: 12081
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: wth -- Thread time .vs Subroutine time

Post by fxm »

Another example with 2 call sequences of 2 different user thread subroutines, but using a single hidden thread (started with a single ThreadCreate):

Code: Select all

Type ThreadInitThenMultiStart
    Public:
        Declare Constructor()
        Declare Sub ThreadInit(Byval pThread As Sub(Byval As Any Ptr), Byval p As Any Ptr = 0)
        Declare Sub ThreadStart()
        Declare Sub ThreadWait()
        Declare Destructor()
    Private:
        Dim As Sub(Byval p As Any Ptr) _pThread
        Dim As Any Ptr _p
        Dim As Any Ptr _mutex1
        Dim As Any Ptr _mutex2
        Dim As Any Ptr _mutex3
        Dim As Any Ptr _pt
        Dim As Byte _end
        Declare Static Sub _Thread(Byval p As Any Ptr)
End Type

Constructor ThreadInitThenMultiStart()
    This._mutex1 = Mutexcreate()
    Mutexlock(This._mutex1)
    This._mutex2 = Mutexcreate()
    Mutexlock(This._mutex2)
    This._mutex3 = Mutexcreate()
    Mutexlock(This._mutex3)
End Constructor

Sub ThreadInitThenMultiStart.ThreadInit(Byval pThread As Sub(Byval As Any Ptr), Byval p As Any Ptr = 0)
    This._pThread = pThread
    This._p = p
    If This._pt = 0 Then
        This._pt = Threadcreate(@ThreadInitThenMultiStart._Thread, @This)
        Mutexunlock(This._mutex3)
    End If
End Sub

Sub ThreadInitThenMultiStart.ThreadStart()
    Mutexlock(This._mutex3)
    Mutexunlock(This._mutex1)
End Sub

Sub ThreadInitThenMultiStart.ThreadWait()
    Mutexlock(This._mutex2)
    Mutexunlock(This._mutex3)
End Sub

Sub ThreadInitThenMultiStart._Thread(Byval p As Any Ptr)
    Dim As ThreadInitThenMultiStart Ptr pThis = p
    Do
        Mutexlock(pThis->_mutex1)
        If pThis->_end = 1 Then Exit Sub
        pThis->_pThread(pThis->_p)
        Mutexunlock(pThis->_mutex2)
    Loop
End Sub

Destructor ThreadInitThenMultiStart()
    If This._pt > 0 Then
        This._end  = 1
        Mutexunlock(This._mutex1)
        .ThreadWait(This._pt)
    End If
    Mutexdestroy(This._mutex1)
    Mutexdestroy(This._mutex2)
    Mutexdestroy(This._mutex3)
End Destructor    

'--------------------------------------------------

Sub UserThread1 (Byval p As Any Ptr)
    Dim As Zstring Ptr pz = p
    Print "Sub A - " & *pz
End Sub

Sub UserThread2 (Byval p As Any Ptr)
    Dim As Zstring Ptr pz = p
    Print "Sub B - " & *pz
End Sub

Dim As ThreadInitThenMultiStart t1

t1.ThreadInit(@UserThread1, @"user thread code body multi-start 1")  '' initializes the user thread
Print "Waiting for any key for sequence 1"
Print
Sleep                                                             '' waits for user inkey

For I As Integer = 1 To 8
    Print I & ": ";
    t1.ThreadStart()  '' starts the user thread code body
    t1.Threadwait()   '' waits for the user thread code end
Next I

Print
Print "Sequence 1 completed"
Print

t1.ThreadInit(@UserThread2, @"user thread code body multi-start 2")  '' initializes the user thread
Print "Waiting for any key for sequence 2"
Print
Sleep                                                             '' waits for user inkey

For I As Integer = 1 To 5
    Print I & ": ";
    t1.ThreadStart()  '' starts the user thread code body
    t1.Threadwait()   '' waits for the user thread code end
Next I

Print
Print "Sequence 2 completed"
Print
Sleep
Last edited by fxm on Jan 29, 2021 21:08, edited 2 times in total.
fxm
Moderator
Posts: 12081
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: wth -- Thread time .vs Subroutine time

Post by fxm »

I think such a feature could maybe be added in the Programmer's Guide / Critical Sections FAQ:
13. Can we emulate a kind of thread pooling feature with FreeBASIC?
Last edited by fxm on Jan 30, 2021 8:35, edited 1 time in total.
Post Reply