I'd go for option two with a hybrid shared/distributed memory twist... I'm assuming that the 13 machines in your cluster all access the same shared file system and that each computer has enough memory to load 8 problems.
Your code could then look something like this:
int fd, next_task = 0, my_task, nr_tasks;
/* Open the file you want to use to synchronize the nodes. */
if ( ( fd = open( "sharedlockfile" , O_CREAT ) ) < 0 ) {
printf( "failed to create lock file.\n" );
abort();
}
/* Initialize the curr_task in the file. */
if ( <I am the first node in this cluster!> )
if ( write( fd , &next_task , sizeof(int) ) < 0 ) {
printf( "failed to write to lock file." );
abort();
}
/* Main parallel loop. */
#pragma omp parallel shared(fd,next_task), private(mytask)
while ( next_task < nr_tasks ) {
/* Try to get a hold of the lock file, grab a task, and write the new counter. */
#pragma omp critical
{
/* Get a hold of the file lock (this will block until the file is free). */
if ( flock( fd , LOCK_EX ) < 0 ) {
printf( "file locking failed.\n" );
abort(); // not sure I'm allowed to abort in a parallel block...
}
/* Read the index of the next task. */
if ( lseek( fd , 0 , SEEK_SET ) < 0 || read( fd , &next_task , sizeof(int) ) < 0 ) {
printf( "error reading next task id.\n" );
abort();
}
/* Remember my task and update the counter. */
my_task = next_task;
next_task += 1;
/* Write the index of the next task back to the file. */
if ( lseek( fd , 0 , SEEK_SET ) < 0 || write( fd , &next_task , sizeof(int) ) < 0 ) {
printf( "error writing next task id.\n" );
abort();
}
/* Let go of the file. */
if ( flock( fd , LOCK_UN ) < 0 ) {
printf( "file un-locking failed.\n" );
abort();
}
} /* end of critical section */
/* Did we get a valid task ID? */
if ( my_task < nr_tasks ) {
/*
* Do whatever this task implies.
*/
} /* check if valid task ID. */
} /* Main loop. */
Please note that I haven't tested the above code! I've done similar things before and most of this is off the top of my head... Check out the manpage for flock
to be sure.
If all the nodes in your cluster execute this, they should each spawn as many threads as they have cores (or as many as you specified in OMP_NUM_THREADS) and each core will try to acquire a task id. Access to next_task
is controlled on two levels:
- By the
#pragma omp critical
so that no two threads on the same node try to mess with the next_task
variable at the same time.
- By the exclusive file lock so that no two nodes read/update the
next_task
variable stored in the file at the same time.
This should give you a good, adaptive, dynamic scheduling of your computations.
Edit
Uhm, may not be as easy as I thought with the locking as it seems to be fd-dependent and thus may not work accross nodes. There seems to be a good solution here though.
Addendum
If solving each system on a separate core is too much of a strain for your memory bus, e.g. you notice that it's not scaling well at all, you might want to try MAGMA, which is like LAPACK, but scales very nicely on multicore architectures.