24

I've noticed some strange behaviour that may or may not be specific to my system. (lenovo t430 running windows 8)

With this script:

import time

now = time.time()
while True:
    then = now
    now = time.time()
    dif = now - then
    print(dif)
    time.sleep(0.01)

I get the following output (what I would consider nominal) with a browser open.

enter image description here

However without a browser open I observe a severe per loop latency.

enter image description here

Obviously this is counter-intuitive as I think anyone would expect better performance when you have fewer concurrant processes.

Any insights or simple replication of these results would be appreciated.

EDIT: Interestingly I observe similar latency with this code:

import time

now = time.time()

def newSleep(mark,duration):
    count = 0
    while time.time()-mark < duration:
        count+=1
    print(count)


while True:
    then = now
    now = time.time()
    dif = now - then
    print(dif)
    #time.sleep(0.01)
    newSleep(now,0.01)

While it does provide additional insight - that is some instances of latent loops are due to lack of processor availability (noted by a count of 0 being printed)- I still notice the 15ms behavior where the printed count will be as high as 70k... and 10ms behavior with counts around 40k.

Darkonaut
  • 20,186
  • 7
  • 54
  • 65
kpie
  • 9,588
  • 5
  • 28
  • 50
  • Is the browser actually doing something, like displaying animation? If it's idle, it shouldn't have much effect on performance. – Barmar Jan 16 '20 at 17:16
  • 2
    I don't think this benchmark is enough to make any sort of conclusions. – AMC Jan 16 '20 at 17:21
  • It's hard to say. There are certainly instances of the increased latency while the browser is open (for example right now with just SO open I'm watching it bounce back and forth as I type) however with gmail open I get fairly consistent "fast" loops. As I move my mouse around over a browser it performs nominally. When the user input stops I observe latancy... – kpie Jan 16 '20 at 17:21
  • 1
    I have tested the same code on my machine and tried opening and closing different processes. Can't seem to find any differences in the time of each iteration. – nima Jan 16 '20 at 17:22
  • But I don't think that I am putting enough pressure on my CPU cores. – nima Jan 16 '20 at 17:24
  • See that's the strange bit... my cpu is only running at like 2% it shouldn't be choking like this. – kpie Jan 16 '20 at 17:25
  • 1
    Perhaps this plays a role: https://en.wikipedia.org/wiki/Dynamic_frequency_scaling – VPfB Jan 23 '20 at 13:57
  • Note that *it is the `time.sleep(..)` duration being measured*, and *not* actual load-work being done (the loop is written in a way that obscures this, although it can be trivially updated to make it more clear). From a system viewpoint, sleeping takes no CPU resources. I've updated the title to reflect. – user2864740 Jan 26 '20 at 01:50
  • Related: https://stackoverflow.com/q/19415153/2864740 – user2864740 Jan 26 '20 at 02:29

5 Answers5

15

I extra fired up Windows 7 to replicate your findings and I can confirm it.

It's a Windows thing with the type of timer used and a default resolution of 15.6 ms (minimum 0.5 ms). Applications can alter the current resolution (WinAPI function: timeBeginPeriod) and Chrome does so.

This function affects a global Windows setting. Windows uses the lowest value (that is, highest resolution) requested by any process. Setting a higher resolution can improve the accuracy of time-out intervals in wait functions. However, it can also reduce overall system performance, because the thread scheduler switches tasks more often. High resolutions can also prevent the CPU power management system from entering power-saving modes. Setting a higher resolution does not improve the accuracy of the high-resolution performance counter.

An article from 2014 in Forbes is covering a bug in Chrome which would set the resolution permanently to 1 ms no matter what current load would require - a problem because it's a system-wide effect with impact on energy consumption. From that article:

In an OS like Windows, events are often set to run at intervals. To save power, the processor sleeps when nothing needs attention, and wakes at predefined intervals. This interval is what Chrome adjusts in Windows, so reducing it to 1.000ms means that the system is waking far more often than at 15.625ms. In fact, at 1.000ms the processor is waking 1000 times per second. The default, of 15.625ms means the processor wakes just 64 times per second to check on events that need attention.

Microsoft itself says that tick rates of 1.000ms might increase power consumption by "as much as 25 per cent".

You can get the default resolution from Python with time.get_clock_info().

namespace = time.get_clock_info('time')
namespace.adjustable
# True
namespace.implementation
# 'GetSystemTimeAsFileTime()'
namespace.monotonic
# False
namespace.resolution
# 0.015600099999999999

You can get the actual resolution from cmd with the ClockRes applet.

Darkonaut
  • 20,186
  • 7
  • 54
  • 65
  • Running the same script in Ubuntu 18.04 (kernel 5.0.0) tells `1e-09`! – HongboZhu Jan 29 '20 at 12:34
  • So I assume it is a different concept in Linux. Otherwise, power consumption would be crazy ... – HongboZhu Jan 29 '20 at 12:40
  • I googled and found some answers ([tickless Linux kernel](https://blog.fpmurphy.com/2012/09/tickless-linux-kernels.html), and [tickless kernel @ Wikipedia](https://en.wikipedia.org/wiki/Tickless_kernel)]) – HongboZhu Jan 29 '20 at 13:26
8

I tried the same within both windows and ubuntu server(virtualbox)(which doen't have a browser) but the result's are same which average i am getting

in Ubuntu Server

    0.010122537612915039
    0.010426998138427734
    0.010067939758300781
    0.010767221450805664
    0.010728120803833008
    0.010106086730957031
    0.01068258285522461
    0.010105609893798828
    0.01118612289428711
    0.010136842727661133
    0.010585784912109375
    0.010425567626953125
    0.01014852523803711
    0.010422945022583008
    0.01010894775390625

and in Windows

    0.010767221450805664
    0.010751485824584961
    0.010716915130615234
    0.010229110717773438
    0.01016545295715332
    0.010195255279541016
    0.010723352432250977
    0.010744094848632812
    0.010716438293457031
    0.010564565658569336
    0.010889291763305664
    0.010728597640991211
    0.010579824447631836
    0.010889530181884766
    0.010567903518676758
    0.010717153549194336
    0.010735273361206055

so, in my opinion, there is no correlation between the opened browser and performance of python

i_am_deesh
  • 448
  • 3
  • 12
6

Any insights or simple replication of these results would be appreciated.

Here you go:

Using your code and the most recent release of Chrome, I can confirm this behaviour with nearly the same results.

I measured the average time taken-

Browser running: 0.01055538261329734

Browser not running: 0.01563055389053695

I have about 30 open tabs, but they are all idle. Currently, I can't think of any reason why this would happen.

Looking forward to further insights.

DerHamm
  • 173
  • 8
6

While I cannot reproduce this behavior on my machine, my suspicion is that it can be caused by dynamic processor clock (as well as memory and system bus clocks), e.g. when you don't have a browser open then processor is running at 1/2 of maximum clock, but once you start a browser it gets to a maximum clock. You can verify this by playing with "Maximum\minimum processor state\frequency" in Advanced Power Options or by running another application that spawns many processes (this is what Chrome does) instead of a browser and see if this changes the latency.

Vader
  • 3,675
  • 23
  • 40
  • That possibility occurred to me however I observed the latent and nominal behavior at a variety of clock rates. 1.2 - 3.x ghz – kpie Jan 22 '20 at 03:39
  • Have you tried to spawn multiple processes with a different apllication(s) and check the latency? – Vader Jan 22 '20 at 07:08
  • I think you are right. There is a lot variables involved how the kernel scheduler works is complex – pbacterio Jan 22 '20 at 14:43
2

I wrote a similar code and got similar outputs,

for i in range(10):
    then = time.time()
    time.sleep(0.01)
    now = time.time()
    # print('+---------------------------+')
    print(now - then)
    # print(f"rounded: {round(now - then, 2)}")
    # print('+---------------------------+')

I first suspected that what is causing this is, is the usage of floating point math check this one.

But then I got this output

Outputs

0.01080012321472168
0.01015472412109375
0.010109186172485352
0.022491931915283203 # THIS ONE IS REALLY WEIRD
0.01163029670715332
0.010137796401977539
0.010179996490478516
0.01008749008178711
0.010132551193237305
0.010088443756103516

Even if you assumed that there are a margin of error in calculating the difference, it shouldn't be that much

+---------------------------+
0.01080012321472168
rounded: 0.01
+---------------------------+
+---------------------------+
0.01015472412109375
rounded: 0.01
+---------------------------+
+---------------------------+
0.010109186172485352
rounded: 0.01
+---------------------------+
+---------------------------+
0.022491931915283203
rounded: 0.02                 
+---------------------------+
+---------------------------+
0.01163029670715332
rounded: 0.01
+---------------------------+
+---------------------------+
0.010137796401977539
rounded: 0.01
+---------------------------+
+---------------------------+
0.010179996490478516
rounded: 0.01
+---------------------------+
+---------------------------+
0.01008749008178711
rounded: 0.01
+---------------------------+
+---------------------------+
0.010132551193237305
rounded: 0.01
+---------------------------+
+---------------------------+
0.010088443756103516
rounded: 0.01
+---------------------------+

But after reading python's official documentation of the time.sleep method, these results make sense check here, I quote the documentation

the suspension time may be longer than requested by an arbitrary amount because of the scheduling of other activity in the system

So as an educated guess of the problem here,

It is being caused by multiple factors

  1. Operating system's thread scheduling
  2. Execution time before and after the sleep
  3. And, Floating-Point calculation.

I hope this helped.

Radwan Abu-Odeh
  • 1,897
  • 9
  • 16