Next: Internals, Previous: A minimal C-based backend, Up: Backend creation [Contents][Index]
codegen_c_generic.py 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
codegen_c_generic.py 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: Internals, Previous: A minimal C-based backend, Up: Backend creation [Contents][Index]