2012-09-17 5 views
9

मेरे कोड में, मैं उपयोगकर्ता द्वारा दी गई स्ट्रिंग अभिव्यक्ति का मूल्यांकन करने के लिए eval का उपयोग कर रहा हूं। क्या इस कथन को संकलित या अन्यथा गति देने का कोई तरीका है?पायथन: बार-बार निष्पादित 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 

एक त्वरित तुलना विकल्प एक के लिए, मेरे मशीन पर 2.019 सेकंड लेता विकल्प दो ही 0.218 सेकंड लेता है। निश्चित रूप से पाइथन को अभिव्यक्ति को कड़ी मेहनत किए बिना ऐसा करने का एक तरीका है।

+4

इस पोस्ट में evaloverflow.com/questions/1832940 के साथ-साथ इससे दूर रहने के कुछ अच्छे कारणों को निकालने के कुछ विकल्प देखें। –

+2

क्या होगा यदि उपयोगकर्ता 'आयात os; os.system ("rm -rf /") टाइप करता है? आपको इनपुट स्ट्रिंग की व्याख्या करने के लिए एक पार्सर लिखना होगा, और केवल यह पहचान लें कि आप क्या उम्मीद करते हैं: 'sin',' cos', 'log', इत्यादि। अगर वे जो भी दर्ज करते हैं, तो कोई त्रुटि फेंकती है। यदि आप ऐसा नहीं करते हैं तो यह बुरा हो सकता है। – jozzas

+0

यदि उपयोगकर्ता "आरएम-आरएफ /" या ":() {: |: & };" चाहता है: "वह पाइथन के बजाय इसे खोल में कर सकता है। – devtk

उत्तर

16

तुम भी अजगर चाल कर सकते हैं:

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 वैश्विक नहीं है, आप कर सकते हैं इस तरह लैम्ब्डा eate:

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

और यह कहते हैं:

exp_as_func(my_v) 
+2

यह एफजे की प्रतिक्रिया पर एक उल्लेखनीय गति सुधार है, जो पहले से ही एक बड़ी गति में सुधार था। – devtk

+0

मुझे लगता है कि यह चाल eval से पहले 'compile' का उपयोग करने के बराबर है क्योंकि जब आप इसे चलाते हैं तो आपको सबसे धीमी गति से 17.90 गुना अधिक तेज़ लगता है। इसका मतलब यह हो सकता है कि एक मध्यवर्ती परिणाम कैश किया जा रहा है। – Mermoz

11

आप पहले से अभिव्यक्ति संकलन compiler.compile() का उपयोग करके भूमि के ऊपर से बचने कर सकते हैं:

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 

बस सुनिश्चित करें कि आप केवल एक बार (लूप के बाहर) संकलन करते हैं। जैसा कि टिप्पणियों में उल्लिखित है, उपयोगकर्ता द्वारा प्रस्तुत स्ट्रिंग्स पर eval का उपयोग करते समय सुनिश्चित करें कि आप जो स्वीकार करते हैं उसके बारे में आप बहुत सावधान हैं।

+0

यह एक बहुत ही महत्वपूर्ण लाभ है ... –

4

मुझे लगता है कि आप गलत अंत अनुकूलित कर रहे हैं। आप संख्या की एक बहुत कुछ के लिए एक ही कार्रवाई करने के लिए चाहते हैं तो आप 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 speedup_eval.py 
Evaluating 100000 instances of the given expression: 
sin(x) * y 
With numpy: 0.006098985672 
Generic python: 0.270224094391 
Ratio: 44.3063992807 

गति-अप के रूप में उच्च के रूप में मैं उम्मीद थी, लेकिन अभी भी महत्वपूर्ण नहीं है:

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) 

यहाँ मेरी उम्र बढ़ने मशीन पर परिणाम हैं।

+0

मेरे पास यहां 'numpy' तक पहुंच नहीं है। लेकिन मैं सहमत हूं, यह चीजों को गति दे सकता है। मैं आमतौर पर किसी तीसरे पक्ष की लाइब्रेरी पर भरोसा करने के खिलाफ हूं अगर मैं इसके बिना प्राप्त कर सकता हूं। – devtk

संबंधित मुद्दे