Skip to content
Advertisement

Why is my function faster than python’s print function in IDLE?

I wrote this function a while back:

def faster_print(*args, sep=" ", end="n", file=stdout):
    file.write(sep.join(map(str, args))+end)

and I tested it:

from sys import stdout
from time import perf_counter

def faster_print(*args, sep=" ", end="n", file=stdout):
    file.write(sep.join(map(str, args))+end)

def time(function, *args, **kwargs):
    start = perf_counter()
    function(*args, **kwargs)
    return perf_counter()-start

def using_normal_print(number):
    for i in range(number):
        print("Hello world.", 5, 5.0, ..., str)

def using_faster_print(number):
    for i in range(number):
        faster_print("Hello world.", 5, 5.0, ..., str)

normal_time = time(using_normal_print, number=100)
faster_time = time(using_faster_print, number=100)

print("Normal print:", normal_time)
print("My print function", faster_time)

It turns out that it is only faster in IDLE and not cmd. I know that IDLE creates its own objects for sys.stdout, sys.stdin and sys.stderr but I don’t get why it only slows down python’s built in print function. This answer says that the built in print function is written in c. Shouldn’t that make it faster as my function needs to be compiled from python bytecode into machine code?

I am using Python 3.7.9 and IDLE version 3.7.9

Advertisement

Answer

TheLizard, thank you for reporting and amending your experiments. As an IDLE maintainer, am concerned about IDLE speed. I noticed that printing to screen is sometimes much slower than in the Python terminal/console REPL. In the latter, Python executes in the same process as the screen window and screen.write directly writes to the screen buffer. IDLE, on the other hand, executes user code in a separate process. In that process, the replacement sys.stdout send output through a socket to the IDLE GUI process, which then call tkinter text.insert, which calls tcl/tk functions that write to a screen bugger. But until now, I have not properly investigated.

I ran your code in 3.10.0a5 on my Win 10 machine. In the REPL, normal and fast print took .05 seconds. In IDLE, they took about 1.1 and .3 seconds. The factor of 6 (.3/.05) is explained by the overhead above. But the additional factor of about 3.7 (1.1/.3)?

To test kaya3’s 2nd hypothesis, I defined s = 'a'*75 and replaced your print arguments by s. In REPL, times are still .05 and .05. In IDLE, they are about .41 and .31. I conclude that there is a little internal print function overhead, but that most of the 3.7 is additional socket-to-screen overhead. When print is writing to a buffer, there is no reason to pre-join little strings, because multiple stdout.writes are essentially doing a join, whether to the screen buffer or a disk buffer.

To further test this, I altered the test to write 3 blocks of 40 lines. The REPL times remain the same. In IDLE, they average about .058 and .05, about as fast as in the REPL.

Conclusion: I should document that if one prints in code written to run regularly in IDLE and one cares about speed, one should pre-assemble everything one wants displayed together into one string first and just print that string. IDLE does this for tracebacks, which is why they display ‘all at once’.

Advertisement