Pass an array from one Gambas program to another?

Post

Posted
Rating:
#1 (In Topic #314)
Avatar
Regular
Godzilla is in the usergroup ‘Regular’
I've written a Gambas program that does four CPU-intensive tasks in sequence. One task can't begin until the previous one ends, etc.

Everything works well. But from start of the first task, till the end of the last task, takes about 2 minutes. I've considered buying a new computer with a cutting-edge processor that could do the same sequence of tasks in a fraction of the time.

But here's the thing. The computer I'm now using has a 4-core CPU. While these CPU-intensive tasks are being carried out, the CPU usage on one core remains at 100% while the others remain essentially idle. The overall CPU usage, of course, never goes above 25%, because each Gambas program only uses one thread.

It occurred to me that I could avoid having to buy a new computer if I could better utilize this CPU.

If I could create several "slave" programs, and communicate an array, or at least a huge block of text that can be Split() into an array by each slave, then all four CPU cores could be utilized at once. Done in parallel, the same work could in theory be done in 30 seconds.. Once finished, each slave could communicate its resulting arrays, or huge blocks of text, back to the main program in the same manner.

In my VB6 days, this kind of thing could be done fairly easily. I could find a slave program's hWnd with the FindWindow API call, find its child textbox hWnd with a nifty function FindChildObjectByClass. Then use

Code

Call SendMessageByString(ChildhWnd, WM_SETTEXT, 0, "This is the text to be sent.")

The slave could then Split() that text into an array, perform a task with it, and then send the result back to the main program's textbox in the same manner.

Does anyone know if Gambas can send arrays, or at least hugs blocks of text, between separate running programs?

If not, a workaround that should provide the same functionality would be to use the clipboard. But it would mean the clipboard would always be tied up and unusable, with data always being bounced around back and fourth in it. If I were to mindlessly copy something to the clipboard while these programs are operating, chaos could ensue. So I'd rather avoid using the clipboard if I can. But it may be my best option.

Anyways, thanks for reading. Keep on Gambasing.
Online now: No Back to the top

Post

Posted
Rating:
#2
Avatar
Expert
Quincunxian is in the usergroup ‘Expert’
Hi Godzilla,
You will also need to have a look at the Linux command taskset.
taskset is used to set or retrieve the CPU affinity of a running process given its PID or to launch a new COMMAND with a given CPU affinity. CPU affinity is a scheduler property that "bonds" a process to a given set of CPUs on the system. The Linux scheduler will honor the given CPU affinity and the process will not run on any other CPUs. Note that the Linux scheduler also supports natural CPU affinity: the scheduler attempts to keep processes on the same CPU as long as practical for performance reasons. Therefore, forcing a specific CPU affinity is useful only in certain applications.
https://linux.die.net/man/1/taskset

Not aware of any way to pass information from multiple child applications to a parent application.
Using Shell or Exec functions will require the use of a Wait statement so that negates the outcome you need.

I can think of only 2 ways to do this:
<LIST>
  • <LI>Each child application writes the data to a text file and once finished all of the text files from each child is collated by the parent.</LI>
</LIST>
<LIST>
  • <LI>Write the output data to a database and then collate. There may be more IO cycles with this so overall effect is a little slower but it would give the best design option if you wanted to compare individual runs</LI>
</LIST>

I'd just send a general email to the Gambas users email list and if there other solutions then someone will respond.

Cheers - Quin.
I code therefore I am
Online now: No Back to the top

Post

Posted
Rating:
#3
Avatar
Guru
cogier is in the usergroup ‘Guru’
Hi Godzilla. Your question got a friend and I working on a very interesting Gambas tool called 'Task'. You can find some details here. As usual the documentation could do with a little work and an example would also help. Experimentation produced the attached program that when run on an Intel NUC 4 Core i7 is able to max out all 4 cores, all within the one program, see the 'System Monitor' image below.

<IMG src="http://www.cogier.com/gambas/Monitor.png"> </IMG>

This offers the opportunity to do some really powerful stuff.

Have a look at the program, we have deliberately kept it as simple as possible to hopefully make it easy to understand. Reading, and then rereading, the documentation will also help. I don't pretend to understand it all yet but it is a start. You should be able to modify it to process 4 tasks at the same time. If you need more help let us know.

The program was run on Linux Mint 19.2 with the Cinnamon Desktop. If you are running an Ubuntu derived distro then you can install System Monitor with 'sudo apt-get -y install gnome-system-monitor'.

Attachment

EDIT: -
Program modified to use 8 cores.
<IMG src="http://www.cogier.com/gambas/Monitor1.png"> </IMG>
Online now: Yes Back to the top

Post

Posted
Rating:
#4
Avatar
Regular
Godzilla is in the usergroup ‘Regular’

Quincunxian said

Hi Godzilla,
You will also need to have a look at the Linux command taskset.
taskset is used to set or retrieve the CPU affinity of a running process given its PID or to launch a new COMMAND with a given CPU affinity. CPU affinity is a scheduler property that "bonds" a process to a given set of CPUs on the system. The Linux scheduler will honor the given CPU affinity and the process will not run on any other CPUs. Note that the Linux scheduler also supports natural CPU affinity: the scheduler attempts to keep processes on the same CPU as long as practical for performance reasons. Therefore, forcing a specific CPU affinity is useful only in certain applications.
https://linux.die.net/man/1/taskset

Not aware of any way to pass information from multiple child applications to a parent application.
Using Shell or Exec functions will require the use of a Wait statement so that negates the outcome you need.

I can think of only 2 ways to do this:
<LIST>
  • <LI>Each child application writes the data to a text file and once finished all of the text files from each child is collated by the parent.</LI>
</LIST>
<LIST>
  • <LI>Write the output data to a database and then collate. There may be more IO cycles with this so overall effect is a little slower but it would give the best design option if you wanted to compare individual runs</LI>
</LIST>

I'd just send a general email to the Gambas users email list and if there other solutions then someone will respond.

Hey Quin, thanks for your reply.

Using taskset with the launch of slave programs, to ensure each program uses a separate CPU core, would be a great idea. Its something I didn't think of.

Using text files or a database would be a viable option. Reading and writing from a ram drive would have several benefits also.  It would save wear & tear on the disk, and the speed would be unmatched.

Thank you again for your reply, Quin
Online now: No Back to the top

Post

Posted
Rating:
#5
Avatar
Regular
Godzilla is in the usergroup ‘Regular’

cogier said

Hi Godzilla. Your question got a friend and I working on a very interesting Gambas tool called 'Task'. You can find some details here. As usual the documentation could do with a little work and an example would also help. Experimentation produced the attached program that when run on an Intel NUC 4 Core i7 is able to max out all 4 cores, all within the one program, see the 'System Monitor' image below.

<IMG src="http://www.cogier.com/gambas/Monitor.png"> </IMG>

This offers the opportunity to do some really powerful stuff.

Have a look at the program, we have deliberately kept it as simple as possible to hopefully make it easy to understand. Reading, and then rereading, the documentation will also help. I don't pretend to understand it all yet but it is a start. You should be able to modify it to process 4 tasks at the same time. If you need more help let us know.

The program was run on Linux Mint 19.2 with the Cinnamon Desktop. If you are running an Ubuntu derived distro then you can install System Monitor with 'sudo apt-get -y install gnome-system-monitor'.

TaskCPUMax-0.0.1.tar.gz

EDIT: -
Program modified to use 8 cores.
<IMG src="http://www.cogier.com/gambas/Monitor1.png"> </IMG>

Hello cogier, thank you for your reply.

I'm really amazed by this project created by you and your friend. I appreciate the time and thought you both put into this.

Your project works on both my computers (both having Intel). The single task of counting from 1 to 5000000000000 maxes out all cores at once. I'm very impressed!

Using this method, separate CPU-intensive tasks would still be performed sequentially. But each one would be performed in parallel on 4 cores, instead of a single core. That has to be a significant performance gain.

I wanted to see exactly how much of a performance gain that 10.4 GHz (collectively) gives over 2.6 GHz.

I added another button which simply performs

Code

For a As Integer = 1 To 5000000000000
Next

I added a timer to this task, as well as adding a timer the method provided in your project.  

To my dismay, however, there was no gain. In every test, the times were essentially the same.

This leads me to wonder if the Task method is actually only performing copies of the same task on each core at once? Each task unrelated to the other, with no collating?

I don't know. But this is a very promising thing that warrants further looking into. I hope those who have downloaded this project can report back as to whether they get a performance gain using this method vs the conventional method. This is something that has great potential.

Thank you once again cogier for your reply, and your fascinating project.
Online now: No Back to the top

Post

Posted
Rating:
#6
Avatar
Guru
cogier is in the usergroup ‘Guru’
I have managed to get the 'Kill' event working which is raised when the background task is complete. Using different values in the loop you can see the different processes finish at different times.

<IMG src="http://www.cogier.com/gambas/Monitor2.png"> </IMG>

Attachment
Online now: Yes Back to the top

Post

Posted
Rating:
#7
Avatar
Regular
stevedee is in the usergroup ‘Regular’

Godzilla said


…Using text files or a database would be a viable option. Reading and writing from a ram drive would have several benefits also.  It would save wear & tear on the disk, and the speed would be unmatched….


I made my bat logger run faster by splitting the original program into two; one to record to a RAM drive file every 10 seconds, and a second program to analyse each file (the RAM drive could hold maybe 4 or 5 files which were analysed in date/time order).

The second (analysis) program would check for suitable recording files on the RAM drive, and either delete them (if they did not contain a bat call) or process them, write out a new file, and delete the original.

If the second program could not keep up with the first, then the first (recording) program would have to wait until there was enough space on the RAM drive to continue.

I know nothing about your application, but you may be able to write data to text (CSV) files and pass it around in a similar fashion, e.g. the master program could delete the data files when it has taken what it needs from them. Each file would have a suitable ID so the master knows which child program it came from.
Online now: No Back to the top

Post

Posted
Rating:
#8
Avatar
Regular
Godzilla is in the usergroup ‘Regular’

cogier said

I have managed to get the 'Kill' event working which is raised when the background task is complete. Using different values in the loop you can see the different processes finish at different times.

<IMG src="http://www.cogier.com/gambas/Monitor2.png"> </IMG>

Hey cogier,

Thank you again for your reply to this thread, and for modifying your awesome code.

It actually doing different tasks in parallel. Amazing! Check out these time benchmarks.

First is a time benchmark result of a sequence of four loops done in the conventional way, using your exact set of numbers:

Code

00d 00h 00m 41s for task "Using only 1 core"

Next is a time benchmark result using your Task method, included in your latest project upload:

Code

00d 00h 00m 13s for task "Using all 4 cores"

I would call that a great success! You're really onto something very cool here, and I hope you will continue to develop it and improve on it.

This is absolutely something I can use as a solution to my problem. Its just a question of having each New CPU call separate subroutines, instead of passing numbers. I think the subroutines would need to be relocated into the CPU.class file for them to utilize the Task method? I don't know, it will take some trial and error. But I'm very excited about the possibilities this opens up for those of us whose code requires multiple CPU-intensive tasks.

Once again, thank you cogier for further developing your code and sharing the results of your efforts here.
Online now: No Back to the top

Post

Posted
Rating:
#9
Avatar
Guru
cogier is in the usergroup ‘Guru’
Thanks Godzilla. I am also excited by the possibilities this feature opens up.

I was also interested in your original question and came up with these 2 programs named 'One' and 'Two' (original naming don't you think! ;)). They will pass a string between themselves, but could be altered to send arrays.

<IMG src="http://www.cogier.com/gambas/one%20and%20two.png"> </IMG>

Attachment
Online now: Yes Back to the top

Post

Posted
Rating:
#10
Regular
vuott is in the usergroup ‘Regular’

Europaeus sum !

<COLOR color="#FF8000">Amare memorentes atque deflentes ad mortem silenter labimur.</COLOR>
Online now: No Back to the top

Post

Posted
Rating:
#11
Avatar
Regular
Godzilla is in the usergroup ‘Regular’

cogier said

Thanks Godzilla. I am also excited by the possibilities this feature opens up.

I was also interested in your original question and came up with these 2 programs named 'One' and 'Two' (original naming don't you think! ;)). They will pass a string between themselves, but could be altered to send arrays.

<IMG src="http://www.cogier.com/gambas/one%20and%20two.png"> </IMG>

One.tar.gz

cogier, you nailed it! I had a lot of fun experimenting with this very simple method of program communication you demonstrated.

I've combined all your code examples and I hope you'll find alterations I've done interesting. I've changed things around a bit, for the sake of bench marking. We know this is a great leap forward in computation. I just wanted to get a feel for how much so.

I've put 3 buttons on your project.

The first button I've renamed "Task / Multi-Core" which uses your Task method to crunch the 4 sets of numbers, which I've put into additional text boxes on the form. The numbers are easily changed, to assist in testing. The time to complete is bench marked.

The middle button I've named "Satellites / Parallel" which activates the four running "satellite" programs (I decided "satellite" is a better terminology than "slave"). Using your method of file communication, each satellite is activated to crunch those same 4 sets of numbers in parallel. The time to complete is bench marked.

The third button "One Core" simply crunches those same 4 sets of numbers in the conventional sequential manner, using a single core. The time to complete is bench marked.

Here's my bench mark results with the default 4 sets of numbers on one of my computers:

Code

00d 00h 00m 15s for task Using all 4 cores
00d 00h 00m 16s for task Satellite / Parallel
00d 00h 00m 38s for task Using only 1 core

As you can see, your Task method is faster than my satellites method. I think its due to the extra overhead of the text file communication, not to mention the four other running programs. However, either method leaves the sequential number crunching in the dust. Revolutionary!

As a side note, I got some mismatch errors using CInt with some of the text communication files. At first I changed all Integers in the programs to Long. This eliminated the errors. But it turned out that Longs take twice the time to calculate. So I went back and changed everything to Integer, and kept the numbers within limits. And everything now functions happily.

Thanks again cogier for your latest code example. And I hope you'll find what I've done with your examples interesting.

Edit: the two additional satellite programs are attached in the next message, four satellites total.

Attachment
Online now: No Back to the top

Post

Posted
Rating:
#12
Avatar
Regular
Godzilla is in the usergroup ‘Regular’
 Attempting to post the rest of the satellites. The names of the complete set of satellites are:

Two_Godzilla
Three_Godzilla
Four_Godzilla
Five_Godzilla.

Attempting to post Four and Five here.

Attachment
Online now: No Back to the top

Post

Posted
Rating:
#13
Avatar
Guru
cogier is in the usergroup ‘Guru’
I'm not sure I understand what I am supposed to do. I don't seem to have the extra buttons all I have is as below. Did I do something wrong?

<IMG src="http://www.cogier.com/gambas/2345.png"> </IMG>
Online now: Yes Back to the top

Post

Posted
Rating:
#14
Avatar
Regular
Godzilla is in the usergroup ‘Regular’

cogier said

I'm not sure I understand what I am supposed to do. I don't seem to have the extra buttons all I have is as below. Did I do something wrong?

<IMG src="http://www.cogier.com/gambas/2345.png"> </IMG>

Hey cogier,

With "TaskCPUMax_Godzilla", its all been altered slightly, for the sake of benchmarking. We're using 3 approaches to crunching four sets of numbers. There's your Task method invoking four CPU cores, my method using four satellites that work in parallel, and the traditional method of using 4 loops one after the other in sequence.

We want to compare which of these 3 methods completes a counting task, using the same sets of numbers, in the least amount of time. As such, we're not particularly interested in the results of the counting, but simply the time it takes for each method to complete, for the sake of a benchmark. The lowest time will be better.

How the satellites work is, they only need to be running, in conjunction with "TaskCPUMax_Godzilla" also running. I used multiple instances of the Gambas IDE to allow this.

The satellites are set up so that they're dormant and waiting on you to press the "Satellites / Parallel" button on the "TaskCPUMax_Godzilla" form. Doing so creates files in the /tmp folder, which the satellites will detect using timers. Each satellite will then acquire a specific and different number to crunch, and will begin crunching automatically, without user input. Once "TaskCPUMax_Godzilla" has detected that all four satellite tasks have been completed (reporting back is automated, requiring no user input), then "TaskCPUMax_Godzilla" will give a benchmark for overall time it took for all satellites to complete this task of crunching 4 sets of numbers.

After reporting back, the satellites reset themselves and go back into a dormant state, awaiting for you to press the  "Satellites / Parallel" button again, Maybe this time with a different set of numbers, for testing purposes. Again, in this case, "TaskCPUMax_Godzilla" is not at all interested in receiving specific data back from the satellites. Only an indicator of when each task is completed, for the sake of producing a time  benchmark for this parallel / satellite method of crunching sets of numbers vs other methods.

cogier I'm sorry I wasn't clearer in my last post. I hope this helps you and anyone else who's been following this thread.
Online now: No Back to the top

Post

Posted
Rating:
#15
Avatar
Guru
cogier is in the usergroup ‘Guru’
No it was me :? . I did not run 'TaskCPUMax_Godzilla'. I got very similar results as you 15, 16, 36 secs which shows that the 'Task' method is the easiest and the best.

You could also make a Class for each of the tasks you need to complete if the tasks are not the same.

Tip: - If you want to put Gambas code on the Forum use the 'gb' button and you will get: -

Code (gambas)

  1. For a As Integer = 1 To 5000000000000

which looks better than:-

Code

For a As Integer = 1 To 5000000000000
Next
Online now: Yes Back to the top

Post

Posted
Rating:
#16
Avatar
Regular
Godzilla is in the usergroup ‘Regular’

cogier said

No it was me :? . I did not run 'TaskCPUMax_Godzilla'. I got very similar results as you 15, 16, 36 secs which shows that the 'Task' method is the easiest and the best.

You could also make a Class for each of the tasks you need to complete if the tasks are not the same.

Tip: - If you want to put Gambas code on the Forum use the 'gb' button and you will get: -

Code (gambas)

  1. For a As Integer = 1 To 5000000000000

which looks better than:-

Code

For a As Integer = 1 To 5000000000000
Next

Hey cogier, I'm glad you got the code working. I should have put all 5 project folders into the same archive, since they're closely related. But I didn't know if that was permitted.

Definitely the Gambas code button looks much better. From now on.  :D

I'm very much looking forward to utilizing your Task method with calling and running different subs in parallel. Thank you for your advice to put them into classes. That's where I'll begin. I'll have time over the weekend to lose myself in it.

And even though satellite parallel processing is now obsolete, I'm looking forward to learning the methods of program communication given in vuott's link. Just because I find it fascinating.

Cheers, my friend.
Online now: No Back to the top

Post

Posted
Rating:
#17
Avatar
Regular
Godzilla is in the usergroup ‘Regular’
 Hey cogier,

So I figured out a way to get the Task method to call 4 subroutines in parallel. It works astonishingly well. I couldn't have asked for a better solution to my original post.

However, there's one problem I can't figure out. The subroutines called by the Task process simply sort various fields of public class arrays, and put the results into other receiving public class arrays. It works completely as expected. Except, once all the parallel Task operations are completed, the receiving class arrays are inexplicably redimmed to empty arrays.

I'm sure its something scope-related. But  for the life of me, I can't figure it out.

All you have to do with this program is press the "Go" button and watch the printed output in the console. All it does is, on Form_Open it generates a ton of random data (5,000 records) into a main class array. And then splits that data into 4 sub-category class arrays. The "Go" button initiates the Task processes, and calls subroutines that simply sort by the various fields of those 4 sub-category class arrays, and puts the sorting results into yet other class arrays.

Its all more complex than it needs to be. And its all database stuff, better handled by SQLite. But its intentional, to give the CPU something to briefly crunch on.

In the console, I have it set up to print a 4-record sample of each resulting class array, post-sort. The samples indicate that the data is there, and the sorting of various fields have been done correctly. But once the Task process has completed, those very same arrays immediately become inexplicably redimmed and empty.

Whatever is going on here is beyond me. Any help is appreciated. And thank you for your time.

Attachment
Online now: No Back to the top

Post

Posted
Rating:
#18
Avatar
Guru
cogier is in the usergroup ‘Guru’
I have spent an hour or so on this and can't find the answer. You need to make a simpler program.  I think that you can only return items from the 'Task' and not try to use FMain in the 'Task'.

On another note you can change this routine to: -

Code (gambas)

  1. Public Sub cmdGo_Click()
  2.  
  3.   GetTickCount("start", "Using all 4 cores")
  4.   CPU1 = New CPU(1, 1) As "CPUs"
  5.   CPU2 = New CPU(2, 2) As "CPUs"
  6.   CPU3 = New CPU(3, 3) As "CPUs"
  7.   CPU4 = New CPU(4, 4) As "CPUs"
  8.  

Then you can change Public Sub CPU1_Kill() to: -

Code (gambas)

  1. Public Sub CPUs_Kill() '********************************************
  2. 'Public Sub CPU1_Kill()
  3. Dim TextBoxes As TextBox[] = [TextBox1, TextBox2, TextBox3, TextBox4]
  4.  
  5.   TextBoxes[Last.Value - 1].Text = "CPU task " & Last.Value & " Finished" '********************************************
  6.   'TextBox1.Text = "CPU task " & CPU1.Value & " Finished"
  7.   If TextBox1.Length > 0 And TextBox2.Length > 0 And TextBox3.Length > 0 And TextBox4.Length > 0 Then
  8.     GetTickCount("finish", "Using all 4 cores")
  9.    
  10.     Print "** FINAL A_Sub **"
  11.     Print "A_SubCategory_ByDate.Max = " & A_SubCategory_ByDate.Max & " << Why have these class arrays been erased? Scroll up to see that they were full of records and data a millisecond ago. =/ A scope issue?"
  12.     Print "A_SubCategory_ByNumber.Max = " & A_SubCategory_ByNumber.Max
  13.     Print "A_SubCategory_ByTime.Max = " & A_SubCategory_ByTime.Max
  14.     Print "A_SubCategory_ByName.Max = " & A_SubCategory_ByName.Max
  15.     Print "** FINAL B_Sub **"
  16.     Print "B_SubCategory_ByDate.Max = " & B_SubCategory_ByDate.Max
  17.     Print "B_SubCategory_ByNumber.Max = " & B_SubCategory_ByNumber.Max
  18.     Print "B_SubCategory_ByTime.Max = " & B_SubCategory_ByTime.Max
  19.     Print "B_SubCategory_ByName.Max = " & B_SubCategory_ByName.Max
  20.     Print "** FINAL C_Sub **"
  21.     Print "C_SubCategory_ByDate.Max = " & C_SubCategory_ByDate.Max
  22.     Print "C_SubCategory_ByNumber.Max = " & C_SubCategory_ByNumber.Max
  23.     Print "C_SubCategory_ByTime.Max = " & C_SubCategory_ByTime.Max
  24.     Print "C_SubCategory_ByName.Max = " & C_SubCategory_ByName.Max
  25.     Print "** FINAL D_Sub **"
  26.     Print "D_SubCategory_ByDate.Max = " & D_SubCategory_ByDate.Max
  27.     Print "D_SubCategory_ByNumber.Max = " & D_SubCategory_ByNumber.Max
  28.     Print "D_SubCategory_ByTime.Max = " & D_SubCategory_ByTime.Max
  29.     Print "D_SubCategory_ByName.Max = " & D_SubCategory_ByName.Max
  30.    
  31.  

Now you can get rid of: -
Public Sub CPU2_Kill()
Public Sub CPU3_Kill()
Public Sub CPU4_Kill()
Online now: Yes Back to the top

Post

Posted
Rating:
#19
Avatar
Regular
Godzilla is in the usergroup ‘Regular’
cogier,

I really appreciate the time you took to look into this and search for a solution. I also appreciate the enhancements to the code to simplify the code. Tonight I'll add those modifications to the code and re-upload it for the benefit of others, but in a rush at the moment to get to work.

Yes, all I can imagine is FMain can't be called from the Task method. Maybe I have the right idea, but I'm approaching it incorrectly. There is a solution to this, and I'll figure it out if someone doesn't beat me to it.

The subroutines being called by the Task method are actually based on an old simple Visual Basic routine, posted in some forum, on how to sort a simple array. I believe someone asked why they were getting an error using that routine to sort a structured array (where each array index has a set of fields instead of a single value). A reply was "that can't be done." When it comes to programming, I've never taken "no" for an answer LOL. I worked on that routine, and got it working beautifully for sorting structured arrays, as demonstrated in the code. Even if it is somewhat CPU-intensive (which is exactly what i was looking for ).

If someone else doesn't beat me to it, in coming up with a working method of running subroutines in parallel using the Task method, I'll post it here when i figure it out. We're so close. :D
Online now: No Back to the top

Post

Posted
Rating:
#20
Avatar
Regular
Godzilla is in the usergroup ‘Regular’
Ok, so I got this working 100%. Quite a feat!

And I must give credit to cogier and his friend for letting me know that the Task method even existed, along with an example project to work with.

FYI, in this post I use Task, Fork, parallel processing, multi-processing interchangeably. Its all referring to the same thing.

In my earlier experimental project Task_Test, everything worked great. Except for one huge problem.

Public/Global arrays that were filled with hundreds of sorted records (proven with Print statements), called from within the Task/Fork processes, became inexplicably reset to Null, the instant the Task/Fork processes completed.

After looking into this matter, it turns out that Fork processes are copies of parts of the parent Gambas program, which are given over to the system, thereby allowing multi-processing to occur. The problem with this is, these Forked processes don't appear to have any way to communicate the results of their work back to the parent Gambas program. The Forks complete their duties, then POOF they're gone. Along with any variables containing post-processed data your Gambas program may have been expecting.

Well, if we can't get any post-processed work out of the Fork processes, then what good are they? (I know, right?) But, I've come up with a working solution, thanks to cogier. One simply needs to File.Save the post-processed resulting variable from within the Forked process, and then File.Load that saved variable into your Gambas program. This gives you all the benefits of multi-processing without Gambas needing to strictly have built-in support for it.

All fine and good. But what if the resulting variable is an array? What if its a monster structured class array from h*ll, like Godzilla has a fondness for working with, in his Task_Test project? Something like that is beyond the capabilities of File.Save and File.Load.

I thought I was out of luck. However, I've found a solution thanks to code posted by Jussi Lahtinen in 2013. Object serialization, which is just a fancy term for a way of saving and loading variables of essentially infinite complexity, using very fast binary files (the routines are SaveValues and LoadValues). So thanks to Jussi, Task_Test is now a 100% working project, updated here as Task_Test_Working.

Jussi's SaveValues and LoadValues routines were, however, incomplete. In that they were missing support for the variable types: Single, Float, Variant, Object, and Pointer. I've completed his routines to include all that were left out. I haven't strictly tested each of these additional variable types. But in theory, any variable type of any level of complexity you can throw at SaveValues and LoadValues should work.

Instructions for the Task_Test_Working project:

When you run the program, it will tell you how many threads your CPU is capable of using. "Available CPU threads: 8" for me, though 4 are hyper-threads which is fine. Pressing the Go button will generate a structured array containing 5000 (by default) records generated at random. You can change it to whatever number you like in the textbox. Higher numbers increase the duration of CPU load (exponentially) and lower numbers decrease the duration of CPU load. Once you decide on a new number, simply press Go again (pressing Go additional times has been fixed).

Assuming your CPU has 4 cores or hyper-threads, you can watch all the various subroutines being computed in parallel on your System Monitor > Resources Tab > CPU History graph. Fantastic!

If you tick the Parallel vs. Sequential checkbox, then press the Go button, it will run a normal multi-process Task, and print a time benchmark in the console on completion. Immediately after, it runs the exact same set of data using the exact same subroutines, but in sequence, and also giving a time benchmark in the console on completion.

Parallel vs. Sequential's two time benchmarks allow you to see and appreciate how much time you save using parallel processing over sequential processing, when it comes to CPU-intensive processes. I didn't do extensive tests, but it seems the time saved using multi-processing seems to increase as the number of records you choose to process increases. But I don't really know. Play around with it if you like. Run a controlled series of tests to find out whatever there is to find out.

You can, of course, call any set of subroutines to run in parallel that you want. Your subroutines don't have to use Public/Global variables (scope is immaterial in Forked processes), they don't have to return complex variables or arrays, and you don't have to use the SaveValues and LoadValues routines. You may not want any information returned from Fork processes at all. Just use it however it would be beneficial to you, if you need powerful computing done as quickly as possible.

As it is in this project, the Task method is hard-coded to use 4 threads, regardless of your CPU capability. It would be a nice addition to this code if the number of threads could be assigned dynamically according to how many are available to the CPU.

Also, let's say you have 16 CPU-intensive subroutines to call. I'm not sure how one could "queue" subroutines and have them be executed as CPU threads become available. It would be very interesting to be able to do this.

So there's much more to multi-processing in Gambas to think about. But at least we have our foot in the door now. This thread is very happily [SOLVED] thanks to cogier and his friend. But lets continue to develop and build on ideas for Tasking/Forking. Its something I couldn't be happier with or excited about.

Feel free to ask questions if you're trying to implement any of this into your own project. We're here to help.

Attachment
Online now: No Back to the top
1 guest and 0 members have just viewed this.