2

I am solving a stochastic differential equation and I have a function that contains an algorithm to solve it. So I have to call that function at each time step (it is similar to Runge Kutta's method but with a random variable), then I have to solve the equation many times (since the solution is random) to be able to make averages with all the solutions . That is why I want to know how to call this function in each iteration in the most efficient way possible.

  • 4
    There is only one way to call a function. Also it is slightly faster to not use functions at all. Can you elaborate on what the function does? – Robert Axe Feb 14 '21 at 14:06
  • Can you clarify your question? There is exactly one obvious way to call a function in a loop, and that is the most efficient way. Are you perhaps trying to do some related but different, such as calling a function on each element in a container? – MisterMiyagi Feb 14 '21 at 14:13
  • Why are you calling a function in a loop? Can you please elaborate on your use case? – pykam Feb 14 '21 at 14:31
  • I am solving a stochastic differential equation and I have a function that contains an algorithm to solve it. So I have to call that function at each time step (it is similar to Runge Kutta's method but with a random variable), then I have to solve the equation many times (since the solution is random) to be able to make averages with all the solutions . That is why I want to know how to call this function in each iteration in the most efficient way possible. – Jose Antonio Almanza Marrero Feb 14 '21 at 14:33
  • 1
    From your description, it does not seem as if the act of calling is relevant for performance. It could be that you are looking for parallelism, or compilation. It is impossible to say for sure, though. You might want to take a look at the [ask] and [mcve] help pages to ask a more focused question. – MisterMiyagi Feb 14 '21 at 15:00
  • "I am solving a stochastic..." - Please edit your question to include this important explanatory information. – PaulMcG Feb 14 '21 at 17:35

4 Answers4

3

Some ways to optimize function calls:

  • if the function arguments and results are always the same, move the function call out of the loop
  • if some function arguments are repeated and the results for a given set of arguments are the same, use memoize or lru_cache

However, since you say that your application is a variation on Runge-Kutta, then neither of these is likely to work; you are going to have varying values of t and the modeled state vector, so you must call the function within the loop, and the values are constantly changing.

If your algorithm is slow, then it won't matter how efficient you make the function calls. Look at optimizing the function to make it run faster (or convert to Cython) - the actual call itself is not the bottleneck.

EDIT: I see that you are running this multiple times, to determine a range of values given the stochastic nature of this simulation. In that case, you should use multiprocessing to run multiple simulations on separate CPU cores - this will speed things up some.

PaulMcG
  • 62,419
  • 16
  • 94
  • 130
2

If you are using a for loop and range(), and won't be using the number that comes with each iteration, you can use an underscore to save you some efficiency:

for _ in range(100):
    print("Function call")

If you are only going to use the function in the loop, you can directly pass in the contents of the function you are using, and eliminate the defining of the function to save you some efficiency.

Red
  • 26,798
  • 7
  • 36
  • 58
2

The best way to implement a function on an iterable is to use the map function.

Since map is written in C and is highly optimized, its internal implied loop can be more efficient than a regular Python for loop.

pykam
  • 1,223
  • 6
  • 16
2

Depending on your use-case it may be advantageous to use itertools.starmap(). You can think of starmap() as being the faster, multi-variable implementation of map(). You can find more information on it here. Its behavior is roughly equivalent to:

def starmap(function, iterable):
    # starmap(pow, [(2,5), (3,2), (10,3)]) --> 32 9 1000
    for args in iterable:
        yield function(*args)

so you could use it like so:

from itertools import starmap

def myAddNumbers(a,b):
    return a+b

myListOfArgTuples = [(1,2), (3,4), (5,6)]
cosmicCartograph = starmap(myAddNumbers, myListOfArgs)

Note that starmap() returns a generator object, so in order to "execute" the generator you need to instantiate it:

myResultsList = list(cosmicCartograph)
# [3, 7, 11]

or

myResultsSummed = sum(cosmicCartograph)
# 21
Jacob Faib
  • 1,062
  • 7
  • 22