4

I have a very long function in Python, which I want to split up for readability in several functions. So the parts gets called only from one point, the in program, but very often.

This reduces performance since function calls are very expensive.

Is there a method to both maintain the unsplitted performance, while improving readability/maintenance?

8
  • 3
    "...function calls are very expensive." Have you measured this, that is, compared the long function and the one split in multiple parts? Commented Jan 4, 2016 at 16:39
  • Yes i profiled it and there is definitely a not so small slow down. The long function gets called very very often. Commented Jan 4, 2016 at 16:50
  • 2
    If you are so worried about performance, then consider using a compiled language like Cython or C for the most critical parts. Commented Jan 4, 2016 at 16:52
  • 3
    If the time spent calling a function is a bottleneck, Python is not the right language for your use case. Commented Jan 4, 2016 at 16:56
  • 2
    I guess your question is too vague. Give us some more information such as performance gains and code Commented Jan 4, 2016 at 16:57

4 Answers 4

5

CPython interpreter does not support function inlining.

For performance-critical sections you may consider writing C-extension or using C Foreign Function Interface library.

C Foreign Function Interface for Python. The goal is to provide a convenient and reliable way to call compiled C code from Python using interface declarations written in C.

Another alternative is PyPy interpreter, which is equipped with JIT compiler and tends to achieve huge performance gains.

It depends greatly on the type of task being performed. The geometric average of all benchmarks is 0.14 or 7.0 times faster than CPython

Sign up to request clarification or add additional context in comments.

Comments

5

Besides numba and similar JIT compilers (which retain their own inline caches), that may help you in JIT-code-reuse, as you denoted in numerous re-call of the unspecified function, there are several ways how to inline into Python code.

Inline symbolic x86 ASM assembly language into Python

from pyasm              import Program
from pyasm.data         import String
from pyasm.macro        import syscall
from pyasm.instructions import mov, ret, push, add
from pyasm.registers    import eax, ebx, ecx, edx, ebp
import sys

def example():
    msg = 'Hello World!'
    prog = Program(     # _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
                        #
                        ### ASSUME NOTHING - a lovely ASM note to always remember & self.remind
                        mov(ebx, 1),
                        mov(ecx, String(msg)),
                        mov(edx, len(msg)),
                        syscall('write'),
                        ret(),
                        # _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
                        )
    fun = prog.compile()
    fun()

if __name__ == '__main__':
    example()

Inline (almost) C language into Python

from pycca.cc import CCode, Function, Assign, Return

code = CCode( [ Function( 'int', 'add_one', [( 'int', 'x' )],
                        [
                          Assign( x='x + 1' ),
                          Return('x')
                          ])
                ])
print code.dump_asm()
print "3 + 1 = %d" % code.add_one( 3 )

For details & other options look at this.

Inline C language into Python (since 2001)

import PyInline, __main__

m = PyInline.build( language     = "C",
                    targetmodule = __main__,
                    code         = """
                                   /*_______________________________WORLDS-OF-C*/


                                     double my_C_adder( double a, double b )
                                     {
                                                        return a + b;
                                     }
   
   
                                   /*_______________________________WORLDS-OF-C*/
                                  """
                    )
              
print my_C_adder( 4.5, 5.5 )                     # Should print out "10.0"

Another approach at PyInline.

Comments

3

No, Python doesn't support inline functions.

5 Comments

Mark, you might want to know that your text does not prove to be correct & you might want to update or remove the original statement. Anyway, PF-2016
@user3666197 Python doesn't support it in the OP's definition, writing multiple Python functions but inlining them to have the same performance as a single function. It isn't in the language definition. Sure you can write an extension in another language that improves performance. C++ has inlining; Python does not.
With all due respect, Mark, the numba use-case does hold both the OP's definition & OP's motivation (performance+maintainability+(cit.)"... very, very often" call/code re-use) while at the same time does provide purely pythonesque solution [I would dare to accuse Travis for "not keeping (also OP's) python ideals"]. Yes, there are limitation of what can be numba-accelerated, but that is why there are other approaches (just let's take the genuine PyInline worlds-of-c) mentioned. Yes, Guido had initially another mindset, but check Mitchell Charity's note in PyInline ref'd below.
@user366197, Your definition of inlining differs from mine. Does Python have a language function similar to C++'s inline keyword? No. Can you write extensions that accelerate performance? Yes.
In a serious context, Stroustrup himself in [The design and evolution of C++] refers to inline as being a 'hint' to the compiler. It allows to define the same function in more than one translation unit. This is a prerequisite for inlining the function at a call-spot (without link-time optimization), which is where the keyword got it's name. IMHO, OP is not related to keyword(s), nor to c/C++ language per se, nor to compile-/link-time code-optimisation, but to root idea of inline-d pieces of code ( i.e. why to design a code in this particular way, independently of keywords )
0

It is possible to inline a function using JAX. The following is a minimal cross product function (much simpler than the one in Numpy/JAX).

Since JAX 0.8.1:

import jax
import jax.numpy as jnp

@jax.jit(inline=True)
def cross(a, b):
    """
    Return the cross product of two vectors.

    Parameters
    ----------
    a : (3,) array_like
        First vector.
    a : (3,) array_like
        Second vector.

    Returns
    -------
    v : (3,) ndarray
        Cross product of a and b.

    """
    return jnp.array(
        [
            a[1] * b[2] - a[2] * b[1],
            a[2] * b[0] - a[0] * b[2],
            a[0] * b[1] - a[1] * b[0],
        ]
    )

Before JAX 0.8.1:

from functools import partial
@partial(jax.jit, inline=True)
def _cross(a, b):

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.