23

私のコードではeval、ユーザーが指定した文字列式を評価するために使用しています。このステートメントをコンパイルまたは高速化する方法はありますか?

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

簡単に比較すると、オプションONEは私のマシンで2.019秒かかりますが、オプションTWOはわずか0.218秒かかります。確かに、Pythonには、式をハードコーディングせずにこれを行う方法があります。

4

3 に答える 3

37

Python をだますこともできます。

expression = "math.sin(v['x']) * v['y']"
exp_as_func = eval('lambda: ' + expression)

そして、次のように使用します。

exp_as_func()

速度テスト:

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

補足として、vがグローバルでない場合は、次のようにラムダを作成できます。

exp_as_func = eval('lambda v: ' + expression)

そしてそれを呼び出します:

exp_as_func(my_v)
于 2012-09-17T22:05:32.140 に答える
20

compiler.compile()for Python 2 またはcompile()for Python 3を使用して事前に式をコンパイルすることで、オーバーヘッドを回避できます。

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

コンパイルは 1 回だけ (ループの外で) 行うようにしてください。コメントで述べたように、evalユーザーが送信した文字列を使用する場合は、受け入れる内容に十分注意してください。

于 2012-09-17T22:01:22.783 に答える
5

あなたは間違った終わりを最適化していると思います。多数の数値に対して同じ操作を実行する場合は、numpy の使用を検討する必要があります。

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 とラムダ トリックを使用してバリアントを追加しました。

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)

私の老朽化したマシンでの結果は次のとおりです。

$ 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

スピードアップは期待したほどではありませんが、それでもかなりのものです。

于 2012-09-18T09:16:13.320 に答える