Agents

Initial implementation description

Our first implementation of agents was based on a library implementation, with minimum compiler support. We only needed to store the address of an agent dispatching routine that was taking an array of arguments (open + close one merged in the library) and was performing the call to the actual corresponding Eiffel routine:

a := agent f (x, ?, y)
f.call ([z])

was generated as

create a.make ($disp_f, $disp_f_with_arg, [Current, x, y], [])
prepare arguments array [Current, x, z, y] in library 
$disp_f_with_arg([Current, x, z, y])

and body of `disp_f' and `disp_f_with_arg' looked like:

void disp_f (EIF_REFERENCE Current, EIF_XX arg1, EIF_XX arg2, EIF_XX arg3) {
	<dynamic_dispatch_to f> (Current, arg1, arg2, arg3);
}
void disp_f_with_arg (ARG *args) {
	<dynamic_dispatch_to f> (args[0], args[1], args[2], args[3]);
}

Performance wise it was not really optimal in the library part as building the arguments array is costly (iterations over closed and open arguments to build the final resulting array), plus this double indirection to actually perform the call.

Improving performance

Agent creation might be improved, but I think it is not the slow part. Slow part is the calling one. Let's have a look at all possible cases.

Agent call with manifest tuple

Call is done this way:

my_agent.call ([x, y, z])

Solution we generate this pseudo code:

{
	Compute `x', `y' and `z' expressions
	if (my_agent.has_only_current)
		my_agent.$disp_f (my_agent.closed[0], x, y, z);
	} elseif my_agent.has_no_closed_arguments) {
		my_agent.$disp_f (x, y, z);
	} else {
			// Fill open position in closed one.
		my_agent.closed[my_agent.open_pos[0]] = x;
		my_agent.closed[my_agent.open_pos[1]] = y;
		my_agent.closed[my_agent.open_pos[2]] = z;
		my_agent.$disp_f_with_arg (my_agent.closed);
	
			// Clean open position.
		my_agent.closed[my_agent.open_pos[0]] = NULL;
		my_agent.closed[my_agent.open_pos[1]] = NULL;
		my_agent.closed[my_agent.open_pos[2]] = NULL;
	}
}

No need to create the manifest tuple. Of course when call is done with `disp_f', `disp_f' can be either the dispatching routine when `f' is known to be redefined, but if it is not redefined or frozen, then `disp_f' should be replace by `f' itself. This will remove an indirection and will be much more efficient.

Agent call with tuple

Call is done this way:

my_agent.call (t)

Solution we generate this pseudo code:

	// Initialize open positions.
for (int i = 0; i < t.count; i++ {
	my_agent.closed[my_agent.open_pos[i]] = t.item (i);
}

my_agent.$disp_f_with_arg (my_agent.closed);

	// Reset open positions for next agent calls
for (int i = 0; i < t.count; i++ {
	my_agent.closed[my_agent.open_pos[i]] = NULL;
}

No need to create the manifest tuple.