Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Eventloop very efficient?

183 views
Skip to first unread message

Cecil Westerhof

unread,
Jan 1, 2018, 5:28:06 PM1/1/18
to
I had a program running under systemd which saved every minute some
system info and every hour some other info. I did this in a while
loop, where I used an after to get to the start of the next minute.
And in this while loop I checked if it was the beginning of the hour
and saved the extra information.

I really did not like this, because you need the logic in your loop
then. With two pieces of data, this is not really a problem, but if it
increases it can become messy. So I rewrote it using an eventloop.

The funny thing is: it looks like it uses about half the CPU time as
the previous one. Is this a known result, or is it just accidental?


I wrote two simple scripts to test this. And these seem to give an
even bigger difference, but I want to run them a bit longer before I
give it any significance.

--
Cecil Westerhof
Senior Software Engineer
LinkedIn: http://www.linkedin.com/in/cecilwesterhof

Arjen Markus

unread,
Jan 2, 2018, 4:34:57 AM1/2/18
to
Can you show us these scripts? That would help understanding the issue and giving you informed advice.

Regards,

Arjen

Cecil Westerhof

unread,
Jan 2, 2018, 5:14:06 AM1/2/18
to
I have written a script that notifies me. Originally the main was:
while {True} {
waitMinutes ${sleepMins}
if {([clock format [clock seconds] -format %H] eq "00") && ${signalHour}} {
giveSignal ${hourDuration}
} else {
giveSignal ${normalDuration}
}
}

For the eventloop version I changed this to:
after [getWaitMinutesTicks ${sleepMins}] giveSignalLoop
vwait forever

And the proc is:
proc giveSignalLoop {} {
if {([clock format [clock seconds] -format %H] eq "00") && ${::signalHour}} {
giveSignal ${::hourDuration}
} else {
giveSignal ${::normalDuration}
}
after [getWaitMinutesTicks ${::sleepMins}] giveSignalLoop
}

The proc waitMinutes does an after until there are a multiply of the
minutes given. (In my case it is 30, so the signal is given at 00 and
30.

The proc getWaitMinuteTicks returns the ticks that waitMinutes would
wait.

Both are now running for 44 hours. The first one has used 9 seconds
CPU time and the second is still standing on zero. So that is a very
significant difference.


The procs used:
# Usage: waitMinutes INTERVAL
# Waits until minutes is a multiply of INTERVAL
# 60 % INTERVAL should be 0
proc waitMinutes {interval} {
if {[expr {(60 % ${interval}) != 0}]} {
error "[getProcName]: 60 is not a multiply of ${interval}"
}
after [getWaitMinutesTicks ${interval}]
}

# Usage: waitSeconds INTERVAL
# Waits until seconds is a multiply of INTERVAL
# 3600 % INTERVAL should be 0
proc waitSeconds {interval} {
if {[expr {(3600 % ${interval}) != 0}]} {
error "[getProcName]: 3600 is not a multiply of ${interval}"
}
after [getWaitSecondsTicks ${interval}]
}

And in the second case:
# Usage: getWaitMinutes INTERVAL
# Get ticks to wait until minutes is a multiply of INTERVAL
# 60 % INTERVAL should be 0
proc getWaitMinutesTicks {interval} {
if {[expr {(60 % ${interval}) != 0}]} {
error "[getProcName]: 60 is not a multiply of ${interval}"
}
getWaitSecondsTicks [expr {${interval} * 60}]
}

# Usage: getWaitSeconds INTERVAL
# Get ticks to wait until seconds is a multiply of INTERVAL
# 3600 % INTERVAL should be 0
proc getWaitSecondsTicks {interval} {
if {[expr {(3600 % ${interval}) != 0}]} {
error "[getProcName]: 3600 is not a multiply of ${interval}"
}
set secondsInHour [expr {[clock seconds] % 3600}]
set secondsToWait [expr {${interval} - (${secondsInHour} % ${interval})}]
expr {1000 * ${secondsToWait}}

Cecil Westerhof

unread,
Jan 3, 2018, 9:28:07 AM1/3/18
to
They have been running for three days now. The one with the while loop
has used 15 seconds of CPU time, while the eventloop version still
stands on zero. So it looks like that a program that is mostly waiting
surely wins by using an eventloop.

Cecil Westerhof

unread,
Jan 31, 2018, 5:44:06 AM1/31/18
to
More info. The program that uses the while loop uses about 5 seconds a
day. With the event loop it took 18 days before it took a second. So
you can say it uses about 1% of the while loop.

It is of-course a bit contrived, but combined with that it makes
things simpler and more logical it is a good idea to use an event loop
when possible.

Ralf Fassel

unread,
Jan 31, 2018, 8:03:28 AM1/31/18
to
* Cecil Westerhof <Ce...@decebal.nl>
| [after MS ; cmd ] vs [after MS cmd]
| >> Both are now running for 44 hours. The first one has used 9 seconds
| >> CPU time and the second is still standing on zero. So that is a very
| >> significant difference.
| >
| > They have been running for three days now. The one with the while loop
| > has used 15 seconds of CPU time, while the eventloop version still
| > stands on zero. So it looks like that a program that is mostly waiting
| > surely wins by using an eventloop.
>
| More info. The program that uses the while loop uses about 5 seconds a
| day. With the event loop it took 18 days before it took a second. So
| you can say it uses about 1% of the while loop.

Looking at the source code (generic/tclTimer.c, static int
AfterDelay()), it seems that [after ms] splits up the interval into
500ms chunks and does some some checks in between. This obviously
uses more CPU than the event loop waiting for events...

R'

Cecil Westerhof

unread,
Jan 31, 2018, 9:14:06 AM1/31/18
to
Are they necessary, or could it be optimised?

Ralf Fassel

unread,
Feb 1, 2018, 5:12:18 AM2/1/18
to
* Cecil Westerhof <Ce...@decebal.nl>
| Ralf Fassel <ral...@gmx.de> writes:
| > Looking at the source code (generic/tclTimer.c, static int
| > AfterDelay()), it seems that [after ms] splits up the interval into
| > 500ms chunks and does some some checks in between. This obviously
| > uses more CPU than the event loop waiting for events...
>
| Are they necessary, or could it be optimised?

The comments seem to indicate it is necessary...

* AfterDelay --
*
* Implements the blocking delay behaviour of [after $time]. Tricky
* because it has to take into account any time limit that has been set.

Haven't checked the details...

R'

Andreas Kupries

unread,
Mar 28, 2018, 1:38:07 AM3/28/18
to
Cecil Westerhof <Ce...@decebal.nl> writes:

> I have written a script that notifies me. Originally the main was:
> while {True} {
> waitMinutes ${sleepMins}
> if {([clock format [clock seconds] -format %H] eq "00") && ${signalHour}} {

Feels a bit complicated.

if {$signalHour && (([clock seconds] % 3600) == 0)} { ...

Placing $signalHour as the first term short circuits calculation of
the second term when $signalHour is false. The second term computes
hourness directly from the seconds.


> if {[expr {(60 % ${interval}) != 0}]} {

The 1st argument to [if] is an expression.
No need to nest an [expr] into it.

if {(60 % ${interval}) != 0} {

--
See you,
Andreas Kupries <akup...@shaw.ca>
<http://core.tcl.tk/akupries/>
Developer @ SUSE (MicroFocus Canada LLC)
<andreas...@suse.com>

EuroTcl 2018, Jul 7-8, Munich/DE, http://eurotcl.eu/
Tcl'2018, Oct 15-19, Houston, TX, USA. https://www.tcl.tk/community/tcl2018/
-------------------------------------------------------------------------------
0 new messages