Tcl Source Code

View Ticket
Login
2026-05-27
10:39 • Closed ticket [7da6c2d04c]: fileevent poor performance due to shimmering plu... artifact: 9afc3d4e11 user: jan.nijtmans
10:35
Fix [7da6c2d04c]: fileevent poor performance due to shimmering check-in: ab9c069c58 user: jan.nijtmans tags: core-9-0-branch
10:21 • Ticket [7da6c2d04c] fileevent poor performance due to shimmering status stil... artifact: b6f30ca7fd user: oehhar
10:17 • Ticket [7da6c2d04c]: 4 changes artifact: 544a013fa0 user: oehhar
08:53 • Ticket [7da6c2d04c]: 3 changes artifact: 4dd45e54f7 user: apnadkarni
08:43 • Ticket [7da6c2d04c]: 3 changes artifact: d9a31f4839 user: ralfixx
00:45 • Ticket [7da6c2d04c]: 3 changes artifact: d8dcf0ac49 user: emiliano
2026-05-26
21:53
Fix [7da6c2d04c]: fileevent poor performance due to shimmering. Same pattern in a lot of other place... check-in: c26a56dd82 user: jan.nijtmans tags: trunk, main
20:09 • Ticket [7da6c2d04c] fileevent poor performance due to shimmering status stil... artifact: 5210e020a9 user: dgp
20:05 • Ticket [7da6c2d04c]: 3 changes artifact: 32f216cf10 user: dgp
17:57 • Add attachment diff to ticket [7da6c2d04c] artifact: 1dfd84f438 user: ralfixx
17:54 • New ticket [7da6c2d04c] fileevent poor performance due to shimmering. artifact: a064c6601d user: ralfixx

Ticket UUID: 7da6c2d04c219e8ed4896b9d4e9eb88e586447f2
Title: fileevent poor performance due to shimmering
Type: RFE Created on: 2026-05-26 17:54:55
Submitter: ralfixx Assigned to: jan.nijtmans
Subsystem: 24. Channel Commands Severity: Important
Priority: 5 Medium Last modified: 2026-05-27 10:39:48
Status: Closed Closed by: jan.nijtmans
Resolution: Fixed Closed on: 2026-05-27 10:39:48
Version: 9.0.3
Description:

When a fileevent is set up to include the data read from the channel, the performance is dramatically reduced due to shimmering from list to string in Tcl_FileEventObjCmd().

Script: set up a pipeline, and on each read in the fileevent, reset the fileevent script to include the new data. I have intentionally limited the read to 65536 bytes at one time in order to show the problem.

proc read_channel {fd data} {
    append data [read $fd 65536]
    # puts stderr "[string length $data]"
    if {[eof $fd]} {
	set now [clock milliseconds]
	puts stderr "read [string length $data] bytes in [expr {$now-$::start}]ms"
	catch {close $fd}
	set ::forever 1
	return
    }
    fileevent $fd readable [list read_channel $fd $data]
}
set fd [open "|dd if=/dev/zero bs=65536 count=200" rb]
fconfigure $fd -blocking 0
set start [clock milliseconds]
fileevent $fd readable [list read_channel $fd ""]
vwait forever

Tcl_FileEventObjCmd() is checking whether to remove the fileevent by issuing a TclGetString() on the fileevent script and checking whether the returned string is '\0'

generic/tclIO.c #9285
    /*
     * If we are supposed to delete a stored script, do so.
     */
    if (*(TclGetString(objv[3])) == '\0') {
	DeleteScriptRecord(interp, chanPtr, mask);
	return TCL_OK;
    }

This causes the fileevent script to shimmer into a string each time the filevent is reset.

Proposed patch: I think it is safe to assume that fileevent scripts should be valid lists, so we could check the list length instead. If I do so, the perfomance is greatly enhanced (20x).

Original tcl9.0.3 read 13107200 bytes in 10245ms

With proposed patch applied: read 13107200 bytes in 529ms

User Comments:
dgp added on 2026-05-26 20:05:57:
Be very cautious.  I do not think all of Tcl's callback mechanisms are
consistent, but some if not most of them allow for a callback *script*
and not only a callback command or command prefix.  When any script is
allowed, it is not at all safe to assume a list value.

Make changes based on how you see the code to be written, not on
assumptions about "the way it must be".

(FWIW, given the fantasy to imagine starting Tcl over, limiting all callbacks to command prefixes would have been a better idea.)

dgp added on 2026-05-26 20:09:50:
Also worth noting that string rep generation is not what most
people mean by "shimmering".  Usually the unwanted effects
that get disparaged as shimmering have to do with the loss of
an internal rep.  Generating a string does not (have to) provoke
internal rep. loss.  The stork can have two legs on the ground.

emiliano added on 2026-05-27 00:45:03:
Note that, on trunk, the check uses the new Tcl_IsEmpty() function introduced with TIP#711, which doesn't generate a string representation if the provided object has a list internal representation

ralfixx added on 2026-05-27 08:43:37:

Emiliano, that is great news, even better solution than to fix it in just one place.

Donal, thanks for the reminder that "there might be dragons" :-)
My proposed patch tried "list" first, and if that failed for any reason, the "old" GetString was used.
Reasoning was to revert the penalty: if a ("proper":-) list is used as callback, have the quick path.
If a non-list is used, use the string rep. Since the script is "eval"d, I assumed it needed to be a proper list anyway
(eg.

  fileevent $fd readable "some-func $fd {"
fails with

missing close-brace
    while executing
"foo file3 {"
)


apnadkarni added on 2026-05-27 08:53:27:
@ralfixx, and thanks for taking the trouble to track this down, I haven't had time to follow up on c.l.t.

oehhar added on 2026-05-27 10:17:41:

Maybe TIP 711 "Tcl_IsEmpty()" may be used to avoid shimmering...


oehhar added on 2026-05-27 10:21:33:

Sorry, Jan has replaced all the Tcl_GetString() == \0 by Tcl_IsEmpty in commit [c26a56dd82].

Thanks, Harald


jan.nijtmans added on 2026-05-27 10:39:48:

What can be done in 9.0 (which doesn't have Tcl_IsEmpty) is [ab9c069c58ac773e|this]. It's harmless, because it only operates when there is no string representation.

The disadvantage of using Tcl_ListObjLength() is that it might shimmer a non-list type to a list, that's what we don't want.


Attachments: