优化函数调用中的python性能

时间:2013-09-11 08:42:08

标签: python performance

我有以下示例代码,类似于我正在处理的主要代码。我看到的主要瓶颈是函数调用call_fun。有没有办法加快速度? ..示例:不使用字典对象self._d,而是使用其他函数查找?在主代码中,列表“名称”非常大。您可以启用已注释的打印语句,以便快速了解代码(.​​..但如果要打印输出,请务必将范围(500000)中的i更改为范围(1)中的i)

import time

names = [ ('f_a', ([1,1],)), ('f_b', ([3,4],) ) ]

class A(object):
    def __init__(self):        
        self._d = {}
        for n in names:            
            self._d[n[0]] = getattr(self, n[0])

    def call_fun(self, k):       
        #print " In call_fun: k: ", k
        return self._d[k[0]](*k[1])

    def f_a(self, vals):
        #print " I am here in f_a.. vals=", vals
        v =  2*vals
        return v

    def f_b(self, vals):
        v =  3*vals
        return v


# Run the code

start = time.clock()
a = A()
print "names[0]:", names[0]
for i in range(5000000):
    a.call_fun((names[0]))
print "done, elapsed wall clock time (win32) in seconds: " , time.clock() - start

以下是分析输出:python -m cProfile --sort cumulative foo.py

    10000009 function calls in 5.614 seconds

   Ordered by: cumulative time

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
        1    2.066    2.066    5.614    5.614 foo.py:1(<module>)
  5000000    2.345    0.000    3.412    0.000 foo.py:11(call_fun)
  5000000    1.067    0.000    1.067    0.000 foo.py:15(f_a)
        1    0.135    0.135    0.135    0.135 {range}
        1    0.000    0.000    0.000    0.000 foo.py:6(__init__)
        2    0.000    0.000    0.000    0.000 {time.clock}
        1    0.000    0.000    0.000    0.000 foo.py:5(A)
        2    0.000    0.000    0.000    0.000 {getattr}
        1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}

4 个答案:

答案 0 :(得分:2)

我认为没有太大的改进空间。毕竟,你在大约5秒钟内进行了500万次函数调用,即每个函数调用2 GHz CPU上的1μs(不是1ns)或大约2000个CPU周期。

如果你能忍受其局限性,最好的选择可能是PyPy

$ python -V
Python 2.7.1 
$ python so18736473.py
names[0]: ('f_a', ([1, 1],))
done, elapsed wall clock time (win32) in seconds:  5.418259
$ pypy -V
Python 2.7.2 (341e1e3821fff77db3bb5cdb7a4851626298c44e, Jun 09 2012, 14:24:11)
[PyPy 1.9.0]
$ pypy so18736473.py
names[0]: ('f_a', ([1, 1],))
done, elapsed wall clock time (win32) in seconds:  0.648846

答案 1 :(得分:1)

Python可能很快就不会做任何500万次...看到这个代码的提取示例完全摆脱了字典并对函数进行了硬编码(但嵌套调用的数量相同):

import time

class A(object):
    def __init__(self):
        pass

    def call_fun(self, k):       
        return self.f_a([1,1])

    def f_a(self, vals):
        v =  2*vals
        return v

start = time.clock()
a = A()
for i in range(5000000):
    a.call_fun([1,1])
print "done, elapsed wall clock time (win32) in seconds: " , time.clock() - start

它的配置文件基本相同,可能非常稍快一些。开销主要在你的函数调用中。

通过将它们移出类和模块级别,你可以获得约10%的速度提升:

import time

def call_fun(k):       
    return f_a([1,1])

def f_a(vals):
    v =  2*vals
    return v

start = time.clock()
for i in range(5000000):
    call_fun([1,1])
print "done, elapsed wall clock time (win32) in seconds: " , time.clock() - start

在这种情况下,这个典型的答案是&#34;你真正试图完成什么?&#34;

答案 2 :(得分:0)

通过消除将方法名称映射到方法的字典查找,您可以获得稍微更好的性能。这通过创建names2列表来完成。同样,您可以更进一步存储names2[0],因为它在for循环中没有变化。

这一切都没有消除你通过将它传递给另一个函数而间接调用该函数的事实,该函数基本上只是通过canned参数列表为你调用它。原因并不明显,原因在于您的示例代码。

import time

names = [ ('f_a', ([1,1],)), ('f_b', ([3,4],) ) ]

class A(object):
    def __init__(self):
        pass

    def call_fun(self, k):
        #print " In call_fun: k: ", k
        return k[0](*k[1])

    def f_a(self, vals):
        #print " I am here in f_a.. vals=", vals
        v =  2*vals
        return v

    def f_b(self, vals):
        v =  3*vals
        return v

# Run the code

start = time.clock()
a = A()
print "names[0]:", names[0]
names2 = [(getattr(a, name[0]), name[1]) for name in names]
func = names2[0]
for i in range(5000000):
    a.call_fun(func)
print "done, elapsed wall clock time (win32) in seconds: " , time.clock() - start

答案 3 :(得分:0)

当您在特定行上没有可见性时,只会在函数处发生这种情况。

它表示模块使用5.614秒,对call_fun的调用使用3.412秒。 (682纳秒/电话。) 与range中的0.135秒一起,在未计入的模块中留下2.067秒,或37%。

call_fun中的3.412秒包含对f_a(通过k)的调用,使用1.067秒,无法计算2.345秒,占总数的42%。

所以,总的来说,79%的时间都是无法解释的,而你要么猜测它是什么,要么得出结论说什么都做不了。 There's a better way to find out where you should look