68

The IPython %timeit magic command does its job well for measuring time required to run some Python code. Now, I want to use something analogous in the Python script. I know about the timeit module, however, it has several disadvantages, for example, how to select the number of runs adaptively? i.e., the default code

import timeit
t=timeit.Timer("code(f)", "from __main__ import code,f")
t.timeit() 

runs the code million times. The %timeit IPyhton magic command does it automatically. I suggest that I could use something like the MATLAB code http://www.mathworks.com/matlabcentral/fileexchange/18798

that does all the job automatically (and also tells if the overhead of the function is large).

How can I call %timeit magic from a Python script (or maybe there is a better timing solution) ?

Trenton McKinney
  • 56,955
  • 33
  • 144
  • 158
Ivan Oseledets
  • 2,270
  • 4
  • 23
  • 28
  • 10
    If you make it an IPython script (*.ipy), then all of the IPython syntax will be available, and you can just do `%timeit foo` as you normally would. – minrk Apr 28 '12 at 17:50
  • 1
    Thank you, it is a good suggestion. But I use this in an org-mode file, and I thinks it knows only about python yet :) – Ivan Oseledets Apr 29 '12 at 12:38
  • More on @minrk's suggestion: https://stackoverflow.com/questions/11782744/can-i-execute-ipython-scripts – flow2k Jun 27 '19 at 07:31

5 Answers5

94

It depends on which version of IPython you have. If you have 1.x:

from IPython import get_ipython
ipython = get_ipython()

If you have an older version:

import IPython.core.ipapi  
ipython = IPython.core.ipapi.get()

or

import IPython.ipapi  
ipython = IPython.ipapi.get()

Once that's done, run a magic command like this:

ipython.magic("timeit abs(-42)")

Note: The script must be run via ipython.

See Line magics

magic(...) is deprecated since IPython 0.13, use run_line_magic(magic_name, parameter_s)

ipython.run_line_magic("timeit", "abs(-42)")
run_line_magic(magic_name: str, line, _stack_depth=1) method of ipykernel.zmqshell.ZMQInteractiveShell instance
    Execute the given line magic.
    
    Parameters
    ----------
    magic_name : str
        Name of the desired magic function, without '%' prefix.
    line : str
        The rest of the input line as a single string.
    _stack_depth : int
        If run_line_magic() is called from magic() then _stack_depth=2.
        This is added to ensure backward compatibility for use of 'get_ipython().magic()'

Also see ipython.run_cell_magic and Cell magics

run_cell_magic(magic_name, line, cell) method of ipykernel.zmqshell.ZMQInteractiveShell instance
    Execute the given cell magic.
    
    Parameters
    ----------
    magic_name : str
        Name of the desired magic function, without '%' prefix.
    line : str
        The rest of the first input line as a single string.
    cell : str
        The body of the cell as a (possibly multiline) string.
Trenton McKinney
  • 56,955
  • 33
  • 144
  • 158
user2261139
  • 964
  • 7
  • 2
  • 11
    Note, the `magic()` method is now deprecated. Use `run_line_magic()` instead. Also check out `run_cell_magic()` – nealmcb Jun 08 '19 at 18:09
7

Both IPython and the timeit module, when called with python -m timeit, execute the same loop with a growing value of number until the timing result surpasses a certain threshold that guarantees the time measurement is mostly free of operating system interferences.

You can compare the IPython implementation of the %timeit magic with the Python timeit standard module to see that they are doing mostly the same.

So, answering your question, you should probably replicate the same loop until you find the correct value for the number parameter.

C2H5OH
  • 5,452
  • 2
  • 27
  • 39
3

The following works if one runs the Python script interactively in IPython. E.g., test.py:

def f():
    # Algorithm 1
    pass

def g():
    # Algorithm 2
    pass

# which one is faster?
mgc = get_ipython().magic
mgc(u'%timeit f()')
mgc(u'%timeit g()')

Then run it interactively in IPython

%run -i test.py

to spit out the timings. The -i switch is necessary so that the variables are in scope. I have not figured out how to do this without running an IPython instance, i.e., by simply importing timeit from IPython and using it as a function. However, this solution works for my purposes, which is to automate some timing runs.

Stefan
  • 4,380
  • 2
  • 30
  • 33
3

One way to run ipython magic function probably is using embed ipython instance.
For example: (most of the codes are borrowed from ipython website)

from IPython.terminal.embed import InteractiveShellEmbed

ipshell = InteractiveShellEmbed()
ipshell.dummy_mode = True
print('\nTrying to call IPython which is now "dummy":')
ipshell.magic("%timeit abs(-42)");
ipshell()
print('Nothing happened...')

This can work by using python interpreter
PS: using dummy_mode will prevent from invoking interactive shell.

Rene Wang
  • 31
  • 2
  • 1
    `from IPython import get_ipython; ipython = get_ipython()` is returning "AttributeError: 'NoneType' " on v4.2.0 but this seems to work just fine. – J'e Aug 28 '16 at 15:59
3

According to the documentation of the timeit.py module, when timeit is run in command-line mode,

If -n is not given, a suitable number of loops is calculated by trying successive powers of 10 until the total time is at least 0.2 seconds.

This is what IPython does. That is why the number of loops is always a power of 10. You could do something similar in your own code by embedding the call to t.timeit() inside a loop that makes sure you don't wait too long:

import timeit
t = timeit.Timer("code(f)", "from __main__ import code, f")

max_time = 0.2
N = 0
curr_time = t.timeit(1)

while curr_time < max_time:
    N += 1
    curr_time = t.timeit(10**N)

mean_time = curr_time / float(10**N)

This would ensure that the profiling time is at least 0.2 seconds, but not significantly more --- unless calling the function once takes a long time.

Jesse
  • 31
  • 1