Home > Back-end >  Counting number of operations in Python
Counting number of operations in Python

Time:01-03

What I am doing wrong in the following code:

import dis


def count_operations(f):
    operations = 0
    for op in dis.get_instructions(f):
        if op.opname in ('ADD', 'SUB', 'MULT', 'DIV', 'MOD'):
            operations  = 1
    return operations

def solve_system(A, b):
    x = np.linalg.solve(A, b)
    return x

A = np.array([[2, 3],
              [3, 4]])
b = np.array([8, 11])

operations = count_operations(solve_system)

print(f'Number of operations: {operations}')

I wrote two functions, one for counting operations and one for solving a system.

CodePudding user response:

Numpy does a lot of the heavy lifting with (compiled) library routines written in C or Fortran. You won't see those in dis output:

In [1]: import numpy as np

In [2]: import dis

In [3]: def solve_system(A, b):
   ...:     x = np.linalg.solve(A, b)
   ...:     return x
   ...:     

In [4]: dis.dis(solve_system)
  2           0 LOAD_GLOBAL              0 (np)
              2 LOAD_ATTR                1 (linalg)
              4 LOAD_METHOD              2 (solve)
              6 LOAD_FAST                0 (A)
              8 LOAD_FAST                1 (b)
             10 CALL_METHOD              2
             12 STORE_FAST               2 (x)

  3          14 LOAD_FAST                2 (x)
             16 RETURN_VALUE

In [5]: dis.dis(np.linalg.solve)
179           0 LOAD_DEREF               0 (dispatcher)
              2 LOAD_FAST                0 (args)
              4 BUILD_MAP                0
              6 LOAD_FAST                1 (kwargs)
              8 DICT_MERGE               1
             10 CALL_FUNCTION_EX         1
             12 STORE_FAST               2 (relevant_args)

180          14 LOAD_GLOBAL              0 (implement_array_function)

181          16 LOAD_DEREF               1 (implementation)
             18 LOAD_DEREF               2 (public_api)
             20 LOAD_FAST                2 (relevant_args)
             22 LOAD_FAST                0 (args)
             24 LOAD_FAST                1 (kwargs)

180          26 CALL_FUNCTION            5
             28 RETURN_VALUE

From the numpy.linalg.solve documentation:

The solutions are computed using LAPACK routine _gesv.

Those routines (sgesv and dgesv for single and double precision) are written in Fortran. See e.g. the documentation for dgesv.

CodePudding user response:

This is actually an interesting question. So you are making a wrapper function to show the amount of operations that are in a given tuple.

On inspection, the if statement is failing and you can verify this by including an else.

So i do this modification to verify:

import dis
import numpy as np

def count_operations(f):
    operations = 0
    not_operations = 0    # <---added this
    for op in dis.get_instructions(f):
        if op.opname in ('ADD', 'SUB', 'MULT', 'DIV', 'MOD'):
            operations  = 1
        else:
            not_operations  = 1
    return operations, not_operations

def solve_system(A, b):
    x = np.linalg.solve(A, b)
    return x

A = np.array([[2, 3],
              [3, 4]])
b = np.array([8, 11])

operations = count_operations(solve_system)

print(f'Number of operations: {operations}')

The above code now returns a operations as a tuple (of ops and not ops).

When i run the modified code i get this:

Number of operations: (0, 9)

You can also add a print statement to expose what you get in each loop. An example of the first operation in the list is this:

Instruction(opname='LOAD_GLOBAL', opcode=116, arg=0, argval='np', argrepr='np', offset=0, starts_line=18, is_jump_target=False)

Which is <class 'dis.Instruction'> So you could expose that...

Finally, i can show that the code does infact work, but adding LOAD_FAST into the tuple which then returns (3,6) as it occurs 3 times...

  • Related