You are here

Time, time, time.

Within the walls here at NRL, there have been a few conversations regarding how time is handled. ACT-R has two modes for handling time: fast as possible and real-time. Fast as possible just zips through the event queue advancing the clock based on the earliest event available. The real-time clock does the same thing, except that if the next event is further from the current time, the current thread will sleep. Fairly simple and effective, unless you are interfaced with an external device, which likely involves networking and latency problems. Ugh.

How does jACT-R deal with it? Well, there is a great deal of customization available here, and it all depends on what is connected. The simplest case (running w/o any embodiment) is almost exactly the same as the Lisp. The model checks its internal queue of events for the earliest one, then it calls IClock.waitForTime(eventTime). The IClock instance comes from ACTRRuntime.getRuntime().getClock(model). While each any every model has its own clock, they are ultimately all backed by a single master clock. If modelA calls IClock.waitForTime(1.0) and modelB calls IClock.waitForTime(2.0), the clock will advance to 1.0, waking modelA so it can run, but modelB will still be blocked until modelA waits for a time greater than or equal to 2. In this way, all models are kept synchronized in time.

If the clock is configured for real time, (commonreality.time.impl.RealTimeClock), they will behave similarly. If the model waits for time 1, but the real-time clock is currently 0.5, the model thread will sleep/yield for a percentage of the difference (if you sleep the actual delta, you will likely over-shoot).

Where things get interesting is when there is some embodiment (CommonReality is running). In this configuration, CommonReality controls the master clock that all models and sensors participate in. Calls to IClock.waitForTime() are routed to CommonReality. Once it has heard from all the participants, it advances the clock to the minimum of all the requests. The goal is to have all participants in the larger simulation operating within the same time frame. This means that the entire system will be limited by the slowest participants (often the sensor processing). However, the simulation clock will be mostly deterministic.

If one of the participants doesn't handle clock control (e.g. player/stage maintains its own clock that cannot be paused), any one participant (model or sensor) can be granted ownership of the clock. When this clock owner calls IClock.waitForTime() the time is actually set to that value and CommonReality sends the time update to the other participants.

Most of us will just run our simulations full bore, with little regard for real-time. But even in this (default) situation, there are still a few timing issues related to the multithreading that CommonReality entails. If precise timing is critical to a model, perceptual modules should have the StrictTimingEnabled parameter set. An example will illustrate why.

Model A issues a motor command. This motor command is translated into an IEfferentCommand which is routed through CommonReality to the appropriate actuator participant that will executed it. That participant examines the command and either accepts it as valid or rejects it. A response is issued back to the model. In the ACT-R parlance, this is the preparation phase of the motor movement (literally). The model, because it just previously issued the same command, will anticipate that the preparation will take 0 time, and so the execution should start immediately. It may, assuming the actuator can process the request before the next cycle, but it most likely won't. If the model hasn't heard back from the actuator by the time the preparation phase was supposed to elapse, it will extend the duration of it. So, the simulated preparation time may actually be longer than what ACT-R anticipated. Similarly for motor executions and even visual encoding.

Enabling StrictTimingEnabled will post-pone the clock request of the model until it has had a chance to synchronize with the actuator (i.e., send the request, then send a request to wait until all the actuator's processing has completed). This affects performance by flattening many of the major points of concurrency in the simulation and can have serious deleterious effects if there are many participants. In reality, it's not necessary, the simulated clock drifts are around 50ms.

However, it can be important in another aspect. Let's take a simple model that attends to something on the screen and responds. If the model depends upon buffer stuffing, at the start of the model, the participant that parses the visual scene for the visual module may not have had a chance to send any data to the model. In this case, the model will not get anything stuffed and will fail.

Anyway, that's just a quick explanation. More details as I start assembling an official manual.