Hansie
Hansie

Reputation: 39

What is memory and performance difference between importing all the functions of a given module and only those functions which we need?

For example import math import's all function in math module whereas if we write from math import sqrt,pow then only sqrt(),pow() are imported.

Is their any performance or memory difference in both of them.

Upvotes: 1

Views: 304

Answers (1)

Matteo Italia
Matteo Italia

Reputation: 126837

For example import math import's all function in math module whereas if we write from math import sqrt,pow then only sqrt(),pow() are imported.

It's a bit more complicated than that.

If we ignore for a moment that the math is built in into the interpreter (so there's nothing that has to be actually loaded), whether you do import math, from math import sqrt, pow or from math import *, the whole math module is loaded anyhow, and, after the import statement, it is present in sys.modules. So, it's not like there's more or less work to do for loading depending from how you import it.

The only difference between these constructs is the creation, in the import scope, of references to the names you mention in the import statement.

import math is probably the cheapest statement by itself, as it loads the module and just adds a local reference to the module object in the current scope; it essentially boils down to doing

math = __import__('math')

However, each usage is going to be more costly, as math.sin will need two dict lookups (one LOAD_GLOBAL and one LOAD_ATTR) if the import was at global scope, or one local variable lookup (LOAD_FAST) + one dict lookup (LOAD_ATTR) (if it was imported locally to a function) to actually reach the function object to invoke.

from math import sqrt, pow will cost a bit more, as it needs to load the module, look up sqrt and pow in it (two dict lookups) and create the corresponding entries in the scope where you imported them (two dict assignment if in global scope, two STORE_FAST local assignments if in function scope). It is equivalent to something like:

__temp = __import__('math')
sqrt = __temp.sqrt
pow = __temp.pow
del __temp

(__temp does not really exist, it's going to be just an object on the stack; sqrt and pow will be locals or globals depending from where the import statement has been issued)

On the other hand, on lookup this is going to be faster, as it needs only one dict lookup (LOAD_GLOBAL for global import) or one local lookup (LOAD_FAST for import local to a function) compared to the two above.

from math import * is the same as above, but for all the symbols provided in the module __all__ list (or all the symbols provided by the module period if no __all__ is specified).

From a memory standpoint, the latter two are going to cost more than the first, as they create more entries in the locals list or in the globals dict.

That being said, this kind of consideration is generally completely irrelevant, as we are talking about extremely small differences; what matters most when deciding how to import is how much pollution to your namespace is fine to allow in the name of less typing/less explicit imports.

Instead, the kind of optimization in these matters that usually counts is to bind frequently called functions to local variables to avoid continuous lookups in inner loops; for example

import math

def plot_it(framebuffer):
    sin = math.sin
    cos = math.cos
    for y in range(1024):
        for x in range(1024):
            framebuffer[y][x] = int(127*(sin(x)+cos(2*y)))

is generally going to be faster than doing math.sin and math.cos at each iteration, or even than importing sin and cos at top level in the file and using them straight inside the loop, as they are respectively two or one dict lookup away (to be done at each iteration), while here the interpreter just has to load two locals in the inner loop (which is very fast, it's just a straight array load).

Upvotes: 4

Related Questions