Tcl Source Code

View Ticket
Login
Ticket UUID: 2905784
Title: Sync [after] works too hard
Type: Bug Version: None
Submitter: ferrieux Created on: 2009-11-30 00:16:17
Subsystem: 03. Timer Events Assigned To: kennykb
Priority: 7 High Severity:
Status: Closed Last Modified: 2010-10-29 23:42:26
Resolution: Fixed Closed By: ferrieux
    Closed on: 2010-10-29 16:42:26
Description:
Synchronous [after] makes many calls to the underlying OS's getttimeofday() or equivalent.
This is due to an ill-defined responsibility boundary between AfterDelay() and Tcl_Sleep().
Indeed, Tcl_Sleep() already has the logic to repeatedly check the current time and wait, not trusting the OS's time, and also taking car of virtual time.
However, AfterDelay repeats this logic one level up, calling Tcl_Sleep in a loop until endTime is reached.

The existence of the loop in itself has good reasons, namely respecting a reasonable granularity for limits or Async stuff.
But in the degenerate, but important, case of "small" delays below those limits, a very simple optimization (patch attached) lets AfterDelay call Tcl_Sleep exactly once, essentially "trusting" it on the accuracy of the resulting wait. Additionally, one trivial move of the Tcl_GetTime in the loop removes one unwanted extra gettimeofday().

The bottom line: this patch makes a small [after] generate only 3 gettimeofday()s: one on entry, to compute the endTime, and two inside Tcl_Sleep. Squeezing one more out would need a change in the Tcl_Sleep() API; I'm not attempting that.
User Comments: ferrieux added on 2010-10-29 23:42:26:
Committed to HEAD.

ferrieux added on 2010-10-29 23:36:39:
Attaching the final patch. Care is taken to never undershoot, using a ceiling() approach when rounding usecs do msecs in time differences. Now a loop of {after 1} consumes < 0.1% CPU instead of 50%. Also, the total syscall budget is much reduced: one select() and three gettimeofday() only for a small [after].

Cost in accuracy:

% time {after 1} 1000
1060.089 microseconds per iteration
% time {after 2} 1000
2061.111 microseconds per iteration
% time {after 3} 1000
3062.367 microseconds per iteration
% time {after 100} 10
100235.0 microseconds per iteration
% time {after 10000} 1
10002067 microseconds per iteration

ferrieux added on 2010-10-29 23:31:58:

File Added - 391655: afteropt2.patch

ferrieux added on 2009-11-30 07:19:53:
The attached patch includes the 2904418  fix, which removes the bulk of the unneeded gettimeofday()s.

ferrieux added on 2009-11-30 07:18:21:

File Added - 353060: afteropt.patch

Attachments: