VSs Coding Overview

VSs (StarSs) is based on tasks, however it mixes in classic thread concepts, so the mental model ends up being a hybrid. A task is seen as a self-contained unit of work that runs to completion, while a virtual processor (VP) (AKA thread) is seen as long-running and able to suspend and resume in order to synchronize the use of shared variables with other VPs. However, in VSs, tasks have been given the ability to use synchronization constructs in order to add flexibility to the language.

A VSs (StarSs) program has a main thread which creates task-stubs (meta-tasks) via the submit-task construct. The creation states the constraints on when those tasks will be free to execute. The language tracks the constraints and, when satisfied, places the stubs into a queue of ready task-stubs. From there, they are handed out to free cores and animated by a virtual processor (thread) on that core.

The constraints on a task-stub are implied by both the sequence within the main thread (virtual processor) that they get created, and by the shared variables that the task reads and writes. Each task must wait for all preceding writers of its shared variables to complete, and if it writes, it must also wait for all preceding readers of the written-to variable to complete. It may overlap reads with preceding tasks that also read its variables. The runtime tracks the order of task-stub creation and tracks completion of readers and writers. During this tracking, it automatically frees task-stubs, placing them into the ready-to-animate queue, from whence they are taken and assigned to virtual processors on free cores and then the actual task is created from the stub and animated.

VSs constructs

The main construct used in a VSs program is VSs__submit_task. This is given a VSsTaskType construct and an argument structure that holds the values passed in to the task.

VSsTaskType holds information about a VSs task. One of these must be created and filled in for each kind of task created within a VSs (StarSs) program. It says the function that represents the task's behavior, how many arguments the task needs passed to it, how many of those are shared variables involved in dependencies with other tasks, the type of each variable (input, output, both, or not constrained), the size of each argument, and the size of the argument-structure that holds the actual argument values.

The function executed as the behavior of a task must have the VSsTaskFnPtr signature, which returns void, and takes a void *data and a SlaveVP * as inputs. The task casts the void * to an application-defined structure in order to access the inputs.

 VSs__submit_task( VSsTaskType *taskType, void *args, SlaveVP *animSlv);

A task is ended by the VSs__end_task( SlaveVP *animSlv ) construct. Every path by which a task can end must call this.

Full list of VSs constructs available

VSs program structure

Converting a StarSs program to VSs

Each OMPSs pragma has an equivalent VSs call. However, an OMPSs pragma implies many different kinds of information. To capture this, VSs defines standard structures that have to be populated and passed to the VSs calls. One is the VSsTaskType construct, which holds the function that represents the behavior of a task, all the information about which variables are shared, pointers to them, whether they are read or written by the task, their sizes, and so on.

A VSs program has it's own main thread, which exists inside the VSs runtime. It is separate from the C main() thread. So in the code, the C main() function calls a VSs construct which then causes the VSs language to be initialized and started up. When the main invokes this, via the VSs__create_seed_slave_and_do_work call, it indicates the function that is to act as the main thread of the VSs program.

To convert from OMPSs to VSs, a dummy main() must be created that only call the construct to start up VSs. Also, the actual OMPSs main must be wrapped in a function that has the standard VSs top-level-function signature (see the Hello World sample code). After this modification is made to the OMPSs program, then each OMPSs pragma is converted in the same way. First, wrap the pragma's function with the VSsTaskFnPtr signature, and add a VSs__end_task at the end. Then, make an array of the types (IN, OUT, INOUT, NODEP) of the arguments the pragma's function takes, and an array of the sizes of those arguments (int32 is 4, sizeof(AppStruct), etc), then make a VSsTaskType and populate it. Next, create a struct to hold the arguments to the pragma's function, and populate it with values and pointers. Finally, place a VSs__submit_task call where the pragma is, and pass the task-type, argument values array, and the slave VP animating the code.

Some important notes: all shared variables must be passed by pointer, and in the array of arguments, all the shared variables that have dependencies on them must be grouped together as the first arguments, with the NODEP variables at the end.