Python: Way to speed up a repeatedly executed eval statement?
在我的代码中,我使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | import math import random result_count = 100000 expression ="math.sin(v['x']) * v['y']" variable = dict() variable['x'] = [random.random() for _ in xrange(result_count)] variable['y'] = [random.random() for _ in xrange(result_count)] # optimize anything below this line result = [0] * result_count print 'Evaluating %d instances of the given expression:' % result_count print expression v = dict() for index in xrange(result_count): for name in variable.keys(): v[name] = variable[name][index] result[index] = eval(expression) # <-- option ONE #result[index] = math.sin(v['x']) * v['y'] # <-- option TWO |
对于快速比较选项,一个在我的机器上需要2.019秒,而选项二只需要0.218秒。当然,Python有一种在不硬编码表达式的情况下实现这一点的方法。
您还可以使用python:
1 2 | expression ="math.sin(v['x']) * v['y']" exp_as_func = eval('lambda: ' + expression) |
号
然后像这样使用:
1 | exp_as_func() |
速度测试:
1 2 3 4 5 | In [17]: %timeit eval(expression) 10000 loops, best of 3: 25.8 us per loop In [18]: %timeit exp_as_func() 1000000 loops, best of 3: 541 ns per loop |
。
附带说明,如果
1 | exp_as_func = eval('lambda v: ' + expression) |
并称之为:
1 | exp_as_func(my_v) |
。
通过使用
1 2 3 4 5 6 7 8 9 10 11 12 13 | In [1]: import math, compiler In [2]: v = {'x': 2, 'y': 4} In [3]: expression ="math.sin(v['x']) * v['y']" In [4]: %timeit eval(expression) 10000 loops, best of 3: 19.5 us per loop In [5]: compiled = compiler.compile(expression, '<string>', 'eval') In [6]: %timeit eval(compiled) 1000000 loops, best of 3: 823 ns per loop |
只需确保只编译一次(在循环之外)。正如注释中所提到的,当对用户提交的字符串使用
我认为你在优化错误的结局。如果要对许多数字执行相同的操作,应考虑使用numpy:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | import numpy import time import math import random result_count = 100000 expression ="sin(x) * y" namespace = dict( x=numpy.array( [random.random() for _ in xrange(result_count)]), y=numpy.array( [random.random() for _ in xrange(result_count)]), sin=numpy.sin, ) print ('Evaluating %d instances ' 'of the given expression:') % result_count print expression start = time.time() result = eval(expression, namespace) numpy_time = time.time() - start print"With numpy:", numpy_time assert len(result) == result_count assert all(math.sin(a) * b == c for a, b, c in zip(namespace["x"], namespace["y"], result)) |
。
为了让您了解可能获得的收益,我添加了一个使用通用python和lambda技巧的变体:
1 2 3 4 5 6 7 8 9 | from math import sin from itertools import izip start = time.time() f = eval("lambda:" + expression) result = [f() for x, y in izip(namespace["x"], namespace["y"])] generic_time = time.time() - start print"Generic python:", generic_time print"Ratio:", (generic_time / numpy_time) |
以下是我的老化机的结果:
1 2 3 4 5 6 | $ python speedup_eval.py Evaluating 100000 instances of the given expression: sin(x) * y With numpy: 0.006098985672 Generic python: 0.270224094391 Ratio: 44.3063992807 |
。
速度没有我预期的那么快,但仍然很重要。