Next: , Previous: , Up: Backend creation   [Contents][Index]

6.2.3 Generated code generates thoroughly commented C code. However, the overall structure of the generated code may be somewhat unintuitive, as it does not resemble the code that a human would write to accomplish a similar task. The basic idea behind the generated C code is that it expands the entire program into a list of “events”, then starts the clock, then executes all of the events in a single loop. Regardless of the coNCePTuaL program being compiled, the body of the generated C code will look like this:

for (i=0; i<numevents; i++) {
  CONC_EVENT *thisev = &eventlist[i];

  switch (thisev->type) {
    case event_1:

    case event_2:


Programs generated by define a number of event types that are summarized in Event types. The EV_CODE event is used, for example, by the BACKEND EXECUTES (see Injecting arbitrary code), LOGS (see Writing to a log file), and OUTPUTS (see Writing to standard output) statements. Note that there are no loop events—in fact, there are no complex statements (see Complex statements) whatsoever. Complex statements are expanded into multiple simple statements at initialization time.

The advantage of completely expanding a coNCePTuaL program during the initialization phase—essentially, “pre-executing” the entire program—is that that enables all of the expensive, non-communication-related setup to be hoisted out of the timing loop, which is how a human would normally express a network benchmark. Pre-execution is possible because the coNCePTuaL language is not a Turing machine; infinite loops are not expressible by the language and message contents and timings cannot affect program behavior, for instance. During its initialization phase, the generated C code allocates memory for message buffers, evaluates numerical expressions, verifies program assertions, unrolls loops, and does everything else that’s not directly relevant to communication performance. For instance, the coNCePTuaL program ‘TASK tx SUCH THAT tx>4 SENDS 10 1 MEGABYTE MESSAGES TO TASK tx/2’ would cause each task to perform the following steps during initialization:

The final two of those steps repeat as necessary. For example, task 3 receives 10 messages from each of task 6 and task 7.

Note that each task’s receive events (if any) are allocated before its send events (if any), as described Sending. Also, note that only a single message buffer is allocated because the coNCePTuaL source did not specify the UNIQUE keyword (see Unique messages).

An event is implemented as a C struct that contains all of the state needed to perform a particular operation. For example, an event corresponding to a synchronous or asynchronous send operation ( CONC_SEND_EVENT) stores the destination task ID, the number of bytes to send, the message alignment, the number of outstanding asynchronous sends and receives, a flag indicating whether the data is to be touched, and a flag indicating that the message should be filled with data the receiver can verify. In addition, the code_declare_datatypes_SEND_STATE hook (see Hook methods) enables a backend to include additional, backend-specific state in the ( CONC_SEND_EVENT) data structure.

Next: , Previous: , Up: Backend creation   [Contents][Index]

Scott Pakin,