Skip to content

How to write a parallel Macro

Dimitrios Stefanos Velissariou edited this page May 12, 2022 · 17 revisions

Reminder

  • Start Fiji.
  • Start the HPC Workflow Manager. (Plugins > HPC-ParallelTools > HPC Workflow Manager).
  • Create a new directory on your local machine called "greetings".
  • Create an empty macro script called "greetings.ijm" inside the empty directory you created.
  • Create a new job with three (3) compute node selecting the macro script. (Right-click inside the table of the "HPC Workflow Manager" window and click the "Create a new job" context menu item. Select the macro script. Set three (3) compute nodes. Press the "Create" button.)

Simple greeting example

Let's write a simple “greeting” macro script where each node will greet the rest with a print message and wait for the rest to greet it as well. Then it will announce its departure and end. No node should finish before all of them have introduced themselves. Let’s start:

First, we can write a serial version:

print("The greeting program.");
print("Hello I am a single node.");
print("Bye, from the only node.");

If you run this script using your local Fiji you will get the following output:

The greeting program.
Hello I am a single node.
Bye, from the only node.

To run this script in parallel, all we need to do, is to run it with a Fiji that has the Parallel Macro plugin installed. Parallel Macro will automatically start the parallel execution of the macro and stop it when it ends. If you upload the maco and run it though the HPC Workflow Manager, the output will look like this:

The greeting program.
Hello I am a single node.
Bye, from the only node.
The greeting program.
Hello I am a single node.
Bye, from the only node.
The greeting program.
Hello I am a single node.
Bye, from the only node.

Very well, our program is now parallelized. However, the messages no longer make sense as they will be printed tree times.

We should get the id of the node and print it instead as well as the total number of the nodes (just for fun).

To get the id of the node (also called its rank) we must call parGetRank().

myRank = parGetRank();
if (myRank == 0){
    print("The greeting program.");
}
print("Hello I am node number: "+myRank);
print("Bye, from node number: "+myRank);

Notice that we also nested the first print() in an if statement comparing the rank with the first one (0), this is done in order to print this message only once.

You may choose any rank of the available nodes, it is not necessary to use the first one although it is the convention. You may run the script with a different amount of nodes in the future and the only rank that is guaranteed to exist is zero (0), this is because there will always be at least one node executing the script and it will have rank zero (0) assigned to it. Thus, the code surrounded in the if statement with rank zero will always be executed and executed by only one node.

To greet all of the nodes let’s add the total number of nodes used to run the script (size) as well by calling get size parGetSize(). Add the following line after getting the rank to get the size:

size = parGetSize();

And modify the first print to read:

print("The greeting program. Welcome to all "+size+" nodes.);

Now, the program is much better! Unfortunately, it is still incorrect.

If you run it enough times you will notice that sometimes a node will “depart” before all of them give their greetings. This is because some nodes may execute their code faster or slower, there is no guaranty that each line will execute at the same time, or which one will execute first between nodes.

For example, if there are two (2) nodes the redirected output in the “Other output” tab could look like this:

The greeting program.
Hello I am node number: 1
Bye, form node number: 1
Hello I am node number: 0
Bye, from node number: 0

To correct this we will put a barrier to the flow of the execution of the code.

Any node that calls this function will stop until every node has also called this function.

Do this by adding calling parBarrier() below the greeting and above the announcement of the departure of the node.

print("Hello I am node number: "+myRank);
parBarrier();
print("Bye, from node number: "+myRank);

The script will run correctly now, for example for three (3) nodes the following output may be printed:

The greeting program. Welcome to all 3 nodes.
Hello I am node number: 1
Hello I am node number: 3
Hello I am node number: 0
Bye, form node number: 3
Bye, from node number: 0
Bye, from node number: 1

Which is correct.

The final script should look like this:

myRank = parGetRank();
size = parGetSize();
if (myRank == 0){
    print("The greeting program. Welcome to all "+size+" nodes.");
}
print("Hello I am node number: "+myRank);
parBarrier();
print("Bye, from node number: "+myRank);

Reminder

  • Save the final macro, or copy-paste the last code snippet and save the macro.
  • Upload the data before starting the job. (Right-click the row of the job you created in the "HPC Workflow Manager" window and click the "Upload data" context menu item.)
  • Start the job.

Congratulations, you have learned the basics of parallelizing a macro.

For future reference a table with all of the parallel functions can be found here.

video version 📺

main hub:house: previous 👈 next 👉

Clone this wiki locally