Profiling programsï
In this example, we will illustrate the use of:
events
monitors
by defining a simple profiler that prints the starting and ending time for processing a message sent to an object.
Messages as eventsï
In a pure object-oriented system, all computations start by sending
messages to objects. We can thus define an event as the sending of a
message to an object. An event can then be specified by the tuple
(Object, Message, Sender)
. This definition can be refined by
interpreting the sending of a message and the return of the control to
the object that has sent the message as two distinct events. We call
these events respectively before
and after
. Therefore, we end up
by representing an event by the tuple
(Event, Object, Message, Sender)
. For instance, if we send the
message:
| ?- foo::bar(X).
X = 1
yes
the two corresponding events will be:
(before, foo, bar(X), user)
(after, foo, bar(1), user)
Note that the second event is only generated if the message succeeds. If
the message as a goal has multiple solutions, then an after
event
will be generated for each solution.
Events are automatically generated by the message-sending mechanisms for each public message sent using the (::)/2 operator.
Profilers as monitorsï
A monitor is an object that reacts whenever a spied event occurs. The
monitor actions are defined by two event handlers:
before/3 for before
events
and after/3 for after
events. These predicates are automatically called by the message-sending
mechanisms when an event registered for the monitor occurs. These event
handlers are declared as public predicates in the monitoring
built-in protocol.
In our example, we need a way to get the current time before and after
we process a message. We will assume that we have a time
object
implementing a cpu_time/1
predicate that returns the current CPU
time for the Prolog session:
:- object(time).
:- public(cpu_time/1).
:- mode(cpu_time(-number), one).
...
:- end_object.
Our profiler will be named stop_watch
. It must define event handlers
for the before
and after
events that will print the event
description (object, message, and sender) and the current time:
:- object(stop_watch,
% event handler predicates protocol
implements(monitoring)).
:- uses(time, [cpu_time/1]).
before(Object, Message, Sender) :-
write(Object), write(' <-- '), writeq(Message),
write(' from '), write(Sender), nl, write('STARTING at '),
cpu_time(Seconds), write(Seconds), write(' seconds'), nl.
after(Object, Message, Sender) :-
write(Object), write(' <-- '), writeq(Message),
write(' from '), write(Sender), nl, write('ENDING at '),
cpu_time(Seconds), write(Seconds), write(' seconds'), nl.
:- end_object.
After compiling and loading the stop_watch
object (and the objects
that we want to profile), we can use the define_events/5
built-in predicate to set up our profiler. For example, to profile all
messages that are sent to the object foo
, we need to call the goal:
| ?- define_events(_, foo, _, _, stop_watch).
yes
This call will register stop_watch
as a monitor to all messages sent
to object foo
, for both before
and after
events. Note that
we say âas a monitorâ, not âthe monitorâ: we can have any number of
monitors over the same events.
From now on, every time we sent a message to foo
, the stop_watch
monitor will print the starting and ending times for the message
execution. For instance:
| ?- foo::bar(X).
foo <-- bar(X) from user
STARTING at 12.87415 seconds
foo <-- bar(1) from user
ENDING at 12.87419 seconds
X = 1
yes
To stop profiling the messages sent to foo
we use the
abolish_events/5 built-in predicate:
| ?- abolish_events(_, foo, _, _, stop_watch).
yes
This call will abolish all events defined over the object foo
assigned to the stop_watch
monitor.
Summaryï
An event is defined as the sending of a (public) message to an object.
There are two kinds of events:
before
events, generated before a message is processed, andafter
events, generated after the message processing has completed successfully.Any object can be declared as a monitor to any event. A monitor shall reference the
monitoring
built-in protocol in the object opening directive.A monitor defines event handlers, the predicates before/3 and after/3, that are automatically called by the runtime engine when a spied event occurs.
Three built-in predicates, define_events/5, current_event/5, and abolish_events/5, enables us to define, query, and abolish both events and monitors.