1

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

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

4 回答 4

2

我认为没有太大的改进空间。毕竟,您在大约 5 秒内执行 500 万次函数调用,即 1µs(不是 1ns)或在 2 GHz CPU 上每个函数调用大约 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
于 2013-09-11T09:06:40.653 回答
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

在这种情况下,这个典型的答案是“你真正想要完成什么?”

于 2013-09-11T08:59:45.073 回答
0

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

它说模块使用 5.614 秒,调用call_fun使用 3.412 秒。(682 纳秒/呼叫。)加上 0.135 秒range,模块中的 2.067 秒未计算,即 37%。

3.412 秒 incall_fun包括对f_a(通过k) 的调用,使用 1.067 秒,剩下 2.345 秒下落不明,或总数的 42%。

因此,总共有 79% 的时间是无法解释的,您要么猜测它是什么,要么得出无能为力的结论。 有一个更好的方法来找出你应该看的地方

于 2013-09-11T13:19:23.220 回答
0

通过消除将方法名称映射到方法的字典查找,您可以获得更好的性能。这是通过创建一个names2列表在下面完成的。同样,您可以更进一步并存储names2[0],因为它不会在for循环中更改。

所有这些都无法消除您通过将函数传递给另一个函数来间接调用该函数的事实,该函数基本上只是使用固定参数列表为您调用它。从您的示例代码中不清楚原因是什么。

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
于 2013-09-11T09:30:43.467 回答