Design patterns for parallel computing in Xojo

There’s been a lot of discussion about how to get a Xojo app to use multiple CPU threads or cores. I’d like to tell you what I ended up doing some years ago, and ask for your ideas/suggestions/wishes.

What I did:

  • Create two Xojo Desktop app projects (the Main app, and a Helper app)
  • The two apps communicate over IPCSockets using a command/data protocol that I created
  • The Helper apps are full Xojo Desktop app, but on WindowsOS have no Windows (so are hidden) and on macOS run as LSBackgroundOnly apps (but can, if needed, transform themselves to a full GUI app to show windows)
  • When the Main app launches, it figures out how many CPU cores (or hyperthreaded cores) there are and launches an appropriate number or Helper apps. For example, if the CPU has 8 hyperthreaded cores, it would launch 6 helper apps.

Pros:

  • since the Helper app is a real app, it can do all the things an app can do, and does not suffer the limitations of Console apps. For example, it can open a Video file, search to a keyframe, and get the frame as Xojo Picture.
  • As a real app, upon hitting an error, the Helper app can open a debug window with a log
  • As a real app, it’s easy to both apps (Main, Helper) in the IDE and debug both simultaneously (although you can only run 1 helper app at a time)

Cons:

  • Helper app startup is not very fast. This is OK since my use case was “lauch N Helper apps, which will sit idle waitng for commands”
  • Writing my own client/server communication protocol was hard. I probably should have not invented my own
4 Likes

By using LSUIElement rather than LSBackgroundOnly, you can show windows and other UI items while keeping the app hidden from the user (no dock icon, not in app switcher, etc.).

I usually do as you did: one extra helper app is always launched and idling, waiting for a connection when it’s use has come. When an helper starts a task, another one is open to idle, avoiding the delay when it should run.

I guess it’s about habits. I find it not that hard.
My approach to this is to send each command separated with chr(0)+CommandName, so the other end just uses NthField to find them back.
Then, I use chr(1) to separate parameters, which each command has a different set, as needed.

E.g.: for sending:
MySocket.Write Encodings.UTF8.Chr(0)+"ShowMessage"+Encodings.UTF8.Chr(1)+"Hello world!"

For receiving:

Var Cmds() as string=me.readall.split(Encodings.UTF8.Chr(0))
Cmds.Remove 0 'First field is always empty

for i as integer=0 to Cmds.LastIndex
select case Cmds(i).NthField(Encodings.UTF8.Chr(1),1) 'Command name
case "ShowMessage"
MessageBox Cmds(i).NthField(Encodings.UTF8.Chr(1),2)
end select
next

For sending binary data, like pictures or files (which may contain chr(0) or chr(1)), I do like PStrings: the command sends the receiver the amount of bytes that will be sent, with the type of data (picture, sound, whatever). The receiver stores that amount in a property and sends back an acknowledgment. The sender then sends the data. The receiver adds the data to a variable until its length equals the stored amount. Then it proceed on saving the data and sets back the amount to 0 (telling the socket to come back to “commands handling” mode). If the length of data exceeds the expected amount, or doesn’t reach it after a timeout, an error is displayed and an action is taken (reset the states or disconnect).
That’s not so hard.

Thanks for your posted hints.

1 Like