将来のサーバー用の文字コンピューティングのプロファイリングと最適化

こんにちは、Habr! 今日は、将来のサーバーで決済を整理するためのツールを選択した経験を少し共有したいと思います。 この出版物では、サーバー自体についてではなく、その上でのシンボリック計算の最適化について話していることにすぐ気付きます。



挑戦する



ユーザーが次の一般的な形式のしばしば面倒な式を生成できる特定の機能がありますが、将来的には他のユーザーの要求を計算する必要があります。



典型的な式。 プロファイリングに使用
8.348841409877572e-11 * x1_ * x2_ * x3_ * x4_ * x5_ * x6_ * x7_-3.480284409621004e-9 * x1_ * x2_ * x3_ * x4_ * x5_ * x6_-1.44049340858321e-9 * x1_ * x2_4 * x2_4 * x7_ + 6.004816835089577e-8 * x1_ * x2_ * x3_ * x4_ * x5_-2.674192940005371e-9 * x1_ * x2_ * x3_ * x4_ * x6_ * x7_ + 1.1147596343241695e-7 * x1_ * x2_ * x2_ * x2_ 4.614001865646533e-8 * x1_ * x2_ * x3_ * x4_ * x7_-1.92338517189701e-6 * x1_ * x2_ * x3_ * x4_-3.980463071998064e-9 * x1_ * x2_ * x3_ * x5_ * x6_ * x9_ 1.65 + 775_5 x1_ * x2_ * x3_ * x5_ * x6_ + 6.867815593308846e-8 * x1_ * x2_ * x3_ * x5_ * x7_-2.862906227803913e-6 * x1_ * x2_ * x3_ * x5_ + 1.274970379896911_7 * x2 * x2 * x2_x2 x2 x2 x2 x2 x2 * x7_-5.314820397426395e-6 * x1_ * x2_ * x3_ * x6_-2.199809760060692e-6 * x1_ * x2_ * x3_ * x7_ + 9.1700905110223e-5 * x1_ * x2_ * x3_-1.84688853273324_ * x3_-1.846888532733293e * x5_ * x6_ * x7_ + 7.69890890657543e-8 * x1_ * x2_ * x4_ * x5_ * x6_ + 3.1865865064706345e-8 * x1_ * x2_ * x4_ * x5_ * x7_-1.3283551698311385e-6 * x1_4 * x1_4 * x4_5 5.915714175810938e-8 * x1_ * x2_ * x4_ * x6_ * x7_-2.4660148079756056e-6 * x1_ * x2_ * x4_ * x6_-1.0206861262244266e-6 * x1_ * x2_ * x4 _ * x7_ + 4.254815271209286e-5 * x1_ * x2_ * x4_ + 8.80537876744858e-8 * x1_ * x2_ * x5_ * x6_ * x7_-3.6705956013363683e-6 * x1_ * x2_ * x5_ * x6_ * 326 * 343_4 561-269 x2_ * x5_ * x7_ + 6.333176170880347e-5 * x1_ * x2_ * x5_-2.820424906208041e-6 * x1_ * x2_ * x6_ * x7_ + 0.0001175717652455964 * x1_ * x2_ * x6_ + 4.866307746137 * x1_ * x2_-4.643965319933718e-9 * x1_ * x3_ * x4_ * x5_ * x6_ * x7_ + 1.9358756900289542e-7 * x1_ * x3_ * x4_ * x5_ * x6_ + 8.012609870218512e-8 * x1_ * x1_ * x1_ * x1_ x7_-3.3401232720775553e-6 * x1_ * x3_ * x4_ * x5_ + 1.4874948386055242e-7 * x1_ * x3_ * x4_ * x6_ * x7_-6.200746333919621e-6 * x1_ * x3_ * x4_ * x6_2 * x6_2 * x6_012 x3_ * x4_ * x7_ + 0.00010698650352362546 * x1_ * x3_ * x4_ + 2.2140953873789337e-7 * x1_ * x3_ * x5_ * x6_ * x7_-9.229641340558273e-6 * x1_ * x3_ * x5_8 x3_8 x5_383 * * 883 * 883 * 883 * 883 * 883 * 883 * 883 * x5_ * x7_ + 0.00015924648463737888 * x1_ * x3_ * x5_ - 7.091903641665703e-6 * x1_ * x3_ * x6_ * x7_ + 0.00029563191995286495 * x1_ * x3_ * x6_ + 0.00012236236302703984 * x1_ * x3_ * x7_ - 0.005100777187540484 * x1_ * x3_ + 1.0273144909755949e-7 * x1_ * x4_ * x5_ * x6_ * x7_-4.282446163036621e-6 * x1_ * x4_ * x5_ * x6_-1.7725089771387925e-6 * x1_ * x4_ * x5_ * x7_ x4_4 x4_4 x1_4 7.38158 * x5_-2。 x6_ * x7_ + 0.0002041734515648569 * x1_ * x5_ * x6_ + 8.45076066374878e-5 * x1_ * x5_ * x7_ - 0.0035227700858871253 * x1_ * x5_ + 0.00015688350080537115 * x1_ * x6_ * x7_ - 0.006539819616205367 * x1_ * x6_ - 0.0027068382268636906 * x1_ * x7_ + 0.11283680975413288 * x1_-1.4404933842970813e-9 * x2_ * x3_ * x4_ * x5_ * x6_ * x7_ + 6.004816833354854e-8 * x2_ * x3_ * x4_ * x5_ * x6_ + 2.4854000114926666e-8 * x2_ * x3_ * x3_ 1.0360597302149638e-6 * x2_ * x3_ * x4_ * x5_ + 4.614001870156814e-8 * x2_ * x3_ * x4_ * x6_ * x7_-1.923385171910888e-6 * x2_ * x3_ * x4_ * x6_-7.960911-7.960911 x4_ * x7_ + 3.3185723683902546e-5 * x2_ * x3_ * x4_ + 6.867815595043569e-8 * x2_ * x3_ * x5_ * x6_ * x7_-2.8629062278143214e-6 * x2_ * x3 _ * x5_ * x6_-1.1849599028824348e-6 * x2_ * x3_ * x5_ * x7_ + 4.9396042143235244e-5 * x2_ * x3_ * x5_-2.1998097600572225e-6 * x2_ * x3_ * x6_ * x90_8 * 921 + 211 + 211 x3_ * x6_ + 3.795510120421959e-5 * x2_ * x3_ * x7_-0.0015821900589679597 * x2_ * x3_ + 3.1865865045624386e-8 * x2_ * x4_ * x5_ * x6_ * x7_-1.328355169817660 * 480_960 * 7900 e-7 * x2_ * x4_ * x5_ * x7_ + 2.2919188659665732e-5 * x2_ * x4_ * x5_-1.0206861262122835e-6 * x2_ * x4_ * x6_ * x7_ + 4.254815271210674e-5 * x2_ * x4_ * 1.46×461 * 5 * x2_ * x4_ * x7_-0.0007341177730757296 * x2_ * x4_-1.5192633852512821e-6 * x2_ * x5_ * x6_ * x7_ + 6.333176170880174e-5 * x2_ * x5_ * x6_ + 2.6213082872062861462872107228721072 * x2_ * x5_ + 4.8663077461354176e-5 * x2_ * x6_ * x7_-0.002028560982722149 * x2_ * x6_-0.0008396235272224249 * x2_ * x7_ + 0.03500040721534296 * x2_ + 8.012609870397 * * 2 2 6 * x3_ * x4_ * x5_ * x6_-1.38248054011407e-6 * x3_ * x4_ * x5_ * x7_ + 5.762985469397186e-5 * x3_ * x4_ * x5_-2.566495438213745 e-6 * x3_ * x4_ * x6_ * x7_ + 0.00010698650352363066 * x3_ * x4_ * x6_ + 4.428182648625982e-5 * x3_ * x4_ * x7_-0.00184592488062541 * x3_ * x4_-3.8201582714806 * _6 0.00015924648463738755 * x3_ * x5_ * x6_ + 6.591228770929172e-5 * x3_ * x5_ * x7_ - 0.0027476087026188038 * x3_ * x5_ + 0.00012236236302704678 * x3_ * x6_ * x7_ - 0.005100777187540465 * x3_ * x6_ - 0.0021112170500446024 * x3_ * x7_ + 0.08800784408220161 * x3_ - 1.7725089771387925 e-6 * x4_ * x5_ * x6_ * x7_ + 7.388851548491629e-6 - 0.002366701249731833 * x4_ * x6_ - 0.0009795801398659112 * x4_ * x7_ + 0.040834615376717426 * x4_ + 8.450760663750168e-5 * x5_ * x6_ * x7_ - 0.003522770085887094 * x5_ * x6_ - 0.0014580782487184623 * x5_ * x7_ + 0.060781208246755536 * x5_ - 0.0027068382268636976 * x6_ * x7_ + 0.11283680975413288 * x6_ + 0.04670327439658878 * x7_ + 0.5527559695044361



数式は文字列の形式で提供され、サーバーに保存して、ユーザーの要求に応じて呼び出す必要があります。 ユーザー要求では、パラメーターx1_、x2 _、...は値の単純なリストの形式で送信されると想定されています。 実行時間を最小限に抑えるために、そのような計算をバイアスで整理する方法を決定する必要があります。



特徴1



式自体の形成時間は非常に長いため(指定された式では数分まで)、したがって、この問題の着信行式の処理および保存時間は重要ではありません(将来、これらは異なる順序の値であることが示されます)。



特徴2



リクエストの大部分はグループ性、つまり 1つのリクエストで、同じ式を使用した計算のために値x1_、x2 _、...の複数のセットを送信できます。



ツール



プログラミング言語はPython 3xです。 DBMS- Redis (NoSQL)として。



Redisについて一言。 私の意見では、このタスクはその使用の素晴らしい例です。ユーザーは数式を作成します。 数式が処理され、リポジトリに送信されます。 次に、ストアから取得され、誰かが使用したい場合に処理されます。 要求に応じて送信された値が式に代入され、結果が表示されます。 それだけです 何かを計算したいユーザーに知っておく必要がある唯一のものは、数式内の一意の変数の数です。 Redisにはハッシュメカニズムが組み込まれているので、使用しないのはなぜですか?



Python + Redisの例
import redis r = redis.StrictRedis(host='localhost', port=6379, db=0) #   redis r.hset('expr:1', 'expr', expr) #     'expr:1' r.hset('expr:1', 'params', num) #     'expr:1' r.hget('expr:1', 'expr') #    'expr:1' r.hget('expr:1', 'params') #     'expr:1'
      
      







式自体を操作するには、文字列式を記号式に変換し、必要な計算を実行できるすばらしいSympyライブラリを使用します(一般に、ライブラリは記号式を操作するための巨大な数学機能を開きます)。



プロファイリングと最適化



コードセクションの実行時間を測定するために、次のクラスを使用します(インターネットのどこかに借用されています)。



 class Profiler(object): #  def __init__(self,info=''): self.info = info def __enter__(self): self._startTime = time() def __exit__(self, type, value, traceback): print(self.info, "Elapsed time: {:.3f} sec".format(time() - self._startTime))
      
      





行こう...実験の純度を高めるために、num_iter = 1000-テストの数を導入します。



ファイルから行式を読み取るときにプロファイラーをテストします。



 with Profiler('read (' + str(num_iter) + '): cycle'): for i in range(num_iter): f = open('expr.txt') expr_txt = f.read() f.close() >>read (1000): cycle Elapsed time: 0.014 sec
      
      





行数式が読み込まれました。 次に、その中に変数がいくつあるか、それらが何であるかを判断しましょう(値を代入する変数を知る必要があります)



 with Profiler('find unique sorted symbols (' + str(num_iter) + '): cycle'): for i in range(num_iter): symbols_set = set() result = re.findall(r"x\d_", expr_txt) for match in result: symbols_set.add(match) symbols_set = sorted(symbols_set) symbols_list = symbols(symbols_set) >>find unique sorted symbols (1000): cycle Elapsed time: 0.156 sec
      
      





受信時間はかなり満足しています。 次に、文字列式を文字式に変換します。



 with Profiler('sympify'): expr = sympify(expr_txt) >>sympify Elapsed time: 0.426 sec
      
      





このフォームでは、すでに計算に使用できます。 試してみましょう:



 with Profiler('subs cycle (' + str(num_iter) + '): cycle'): for i in range(num_iter): expr_copy = copy.copy(expr) for x in symbols_list: expr_copy = expr_copy.subs(x,1) >>subs cycle (1000): cycle Elapsed time: 0.245 sec
      
      





ここには機能があります:sympyはどのように(?)シンボリック式の変数に一度にすべての値を代入するかを知りません。 サイクルを使用する必要があります。 expr_copyで実行した結果、実数が得られます。



sympyでは、numpyモジュールを使用して文字式をラムダ関数に変換することができます。これにより、理論的には計算が高速化されます。 翻訳を実行します。



 with Profiler('lambdify'): func = lambdify(tuple(symbols_list), expr, 'numpy') # returns a numpy-ready function >>lambdify Elapsed time: 0.114 sec
      
      





あまりにも長くは発生しなかった、それは良いことです。 ここで、計算が実行される速さを確認しましょう。



 with Profiler('subs cycle (' + str(num_iter) + '): lambdify'): for i in range(num_iter): func(*[1 for i in range(len(symbols_set))]) >>subs cycle (1000): lambdify Elapsed time: 0.026 sec
      
      





これがレベルです! ほぼ1桁高速です。 グループクエリの必要性を考えると、特においしいです(機能2)。 念のため、値の一致を確認します。



 print('exp1 == exp2:', round(expr_copy,12) == round(func(*[1 for i in range(len(symbols_set))]),12)) >>exp1 == exp2: True
      
      





結論1



数式文字列を保存することは実用的ではありません。計算のための変換に時間がかかります。 シンボリック式またはラムダ関数のいずれかを格納することは理にかなっています。



ストレージを扱いましょう。 文字式はsympyクラスであり、ラムダ関数もクラスです(特に掘り下げていません)。 組み込みのpickle、cloudpickle、dillを使用してシリアル化を試みます。



 with Profiler('pickle_dumps cycle (' + str(num_iter) + '): sympifyed expr'): for i in range(num_iter): pickle_dump = pickle.dumps(expr) with Profiler('pickle_loads cycle (' + str(num_iter) + '): sympifyed expr'): for i in range(num_iter): pickle.loads(pickle_dump) print() with Profiler('cloudpickle_dumps cycle (' + str(num_iter) + '): sympifyed expr'): for i in range(num_iter): cloudpickle_dump = cloudpickle.dumps(expr) with Profiler('cloudpickle_loads cycle (' + str(num_iter) + '): sympifyed expr'): for i in range(num_iter): cloudpickle.loads(cloudpickle_dump) print() with Profiler('dill_dumps cycle (' + str(num_iter) + '): sympifyed expr'): for i in range(num_iter): dill_dump = dill.dumps(expr) with Profiler('dill_loads cycle (' + str(num_iter) + '): sympifyed expr'): for i in range(num_iter): dill.loads(dill_dump) >>pickle_dumps cycle (1000): sympifyed expr Elapsed time: 0.430 sec >>pickle_loads cycle (1000): sympifyed expr Elapsed time: 2.320 sec >> >>cloudpickle_dumps cycle (1000): sympifyed expr Elapsed time: 7.584 sec >>cloudpickle_loads cycle (1000): sympifyed expr Elapsed time: 2.314 sec >> >>dill_dumps cycle (1000): sympifyed expr Elapsed time: 8.259 sec >>dill_loads cycle (1000): sympifyed expr Elapsed time: 2.806 sec
      
      





ピクルは、ピアと比較した場合、文字表現を超高速でシリアル化することに注意してください。 デシリアライゼーションの時間は異なりますが、それほど重要ではありません。 ここで、彼はRedisのストレージ/ロードと組み合わせて、シリアライゼーション/デシリアライゼーションをテストしようとします。 pickleがラムダ関数のシリアライズ/デシリアライズに失敗したことに注意してください。



 with Profiler('redis_set cycle (' + str(num_iter) + '): sympifyed expr'): for i in range(num_iter): r.set('expr', pickle_dump) with Profiler('redis_get cycle (' + str(num_iter) + '): sympifyed expr'): for i in range(num_iter): r.get('expr') print() with Profiler('pickle_dumps + redis_set cycle (' + str(num_iter) + '): sympifyed expr'): for i in range(num_iter): r.set('expr', pickle.dumps(expr)) with Profiler('redis_get + pickle_loads cycle (' + str(num_iter) + '): sympifyed expr'): for i in range(num_iter): pickle.loads(r.get('expr')) print() with Profiler('cloudpickle_dumps + redis_set cycle (' + str(num_iter) + '): sympifyed expr'): for i in range(num_iter): r.set('expr', cloudpickle.dumps(expr)) with Profiler('redis_get + cloudpickle_loads cycle (' + str(num_iter) + '): sympifyed expr'): for i in range(num_iter): cloudpickle.loads(r.get('expr')) print() with Profiler('dill_dumps + redis_set cycle (' + str(num_iter) + '): lambdifyed expr'): for i in range(num_iter): r.set('expr', dill.dumps(expr)) with Profiler('redis_get + dill_loads cycle (' + str(num_iter) + '): lambdifyed expr'): for i in range(num_iter): dill.loads(r.get('expr')) >>redis_set cycle (1000): sympifyed expr Elapsed time: 0.066 sec >>redis_get cycle (1000): sympifyed expr Elapsed time: 0.051 sec >> >>pickle_dumps + redis_set cycle (1000): sympifyed expr Elapsed time: 0.524 sec >>redis_get + pickle_loads cycle (1000): sympifyed expr Elapsed time: 2.437 sec >> >>cloudpickle_dumps + redis_set cycle (1000): sympifyed expr Elapsed time: 7.659 sec >>redis_get + cloudpickle_loads cycle (1000): sympifyed expr Elapsed time: 2.492 sec >> >>dill_dumps + redis_set cycle (1000): lambdifyed expr Elapsed time: 8.333 sec >>redis_get + dill_loads cycle (1000): lambdifyed expr Elapsed time: 2.932 sec
      
      





cloudpickleとdillは、ラムダ関数をシリアル化/非シリアル化することができました(ただし、上記の例では、cloudpickleは文字式で機能しました)。



結論2



Redisは、単一のスレッドで1000個の値の良好な読み取り/書き込み結果を示します。 将来的に選択を行うには、数式行の受信から計算された値のユーザーへの発行までの一連のアクションをプロファイルする必要があります。



 print('\nFINAL performance test:') with Profiler('sympify + pickle_dumps_sympifyed_expr + redis_set cycle (' + str(num_iter) + '): '): for i in range(num_iter): expr = sympify(expr_txt) r.set('expr', pickle.dumps(expr)) with Profiler('redis_get + pickle_loads_sympifyed_expr + subs cycle (' + str(num_iter) + '): '): for i in range(num_iter): loaded_expr = pickle.loads(r.get('expr')) expr_copy = copy.copy(loaded_expr) for x in symbols_list: expr_copy = expr_copy.subs(x,1) with Profiler('sympify + lambdify + dill_dumps_lambdifyed_expr + redis_set cycle (' + str(num_iter) + '): '): for i in range(num_iter): expr = sympify(expr_txt) func = lambdify(tuple(symbols_list), expr, 'numpy') r.set('expr', dill.dumps(expr)) with Profiler('redis_get + dill_loads_lambdifyed_expr + subs cycle (' + str(num_iter) + '): '): for i in range(num_iter): loaded_expr = dill.loads(r.get('expr')) func(*[1 for i in range(len(symbols_set))]) with Profiler('sympify + cloudpickle_dumps_sympifyed_expr + redis_set cycle (' + str(num_iter) + '): '): for i in range(num_iter): expr = sympify(expr_txt) r.set('expr', cloudpickle.dumps(expr)) with Profiler('redis_get + cloudpickle_loads_sympifyed_expr + subs cycle (' + str(num_iter) + '): '): for i in range(num_iter): loaded_expr = cloudpickle.loads(r.get('expr')) expr_copy = copy.copy(loaded_expr) for x in symbols_list: expr_copy = expr_copy.subs(x,1) with Profiler('sympify + lambdify + cloudpickle_dumps_lambdifyed_expr + redis_set cycle (' + str(num_iter) + '): '): for i in range(num_iter): expr = sympify(expr_txt) func = lambdify(tuple(symbols_list), expr, 'numpy') r.set('expr', cloudpickle.dumps(expr)) with Profiler('redis_get + cloudpickle_loads_lambdifyed_expr + subs cycle (' + str(num_iter) + '): '): for i in range(num_iter): loaded_expr = cloudpickle.loads(r.get('expr')) func(*[1 for i in range(len(symbols_set))]) >>FINAL performance test: >>sympify + pickle_dumps_sympifyed_expr + redis_set cycle (1000): Elapsed time: 15.075 sec >>redis_get + pickle_loads_sympifyed_expr + subs cycle (1000): Elapsed time: 2.929 sec >>sympify + lambdify + dill_dumps_lambdifyed_expr + redis_set cycle (1000): Elapsed time: 87.707 sec >>redis_get + dill_loads_lambdifyed_expr + subs cycle (1000): Elapsed time: 2.356 sec >>sympify + cloudpickle_dumps_sympifyed_expr + redis_set cycle (1000): Elapsed time: 23.633 sec >>redis_get + cloudpickle_loads_sympifyed_expr + subs cycle (1000): Elapsed time: 3.059 sec >>sympify + lambdify + cloudpickle_dumps_lambdifyed_expr + redis_set cycle (1000): Elapsed time: 86.739 sec >>redis_get + cloudpickle_loads_lambdifyed_expr + subs cycle (1000): Elapsed time: 1.721 sec
      
      





結論3



ラムダ関数の作成とcloudpickleを使用したそのシリアル化は、もちろん最も長いことが判明しましたが、重要な処理とストレージ時間(機能1)を思い出せば... Cloudpickleはよくできました! 1つのフロー内でベースから引き出し、デシリアライズし、1.7秒で1000回計算することができました。 元の行の式の複雑さを考えると、これは一般に良好です。



グループクエリのパフォーマンスを評価してみましょう。 結果を改善することを期待して、注文ごとにパラメータグループの数を変更します。



 print('\nTEST performance for complex requests:') for x in [1,10,100,1000]: with Profiler('redis_get + cloudpickle_loads_lambdifyed_expr + ' + str(x) + '*subs cycle (' + str(round(num_iter/x)) + '): '): for i in range(round(num_iter/x)): loaded_expr = cloudpickle.loads(r.get('expr')) for j in range(x): func(*[1 for i in range(len(symbols_set))]) >>TEST performance for complex requests: >>redis_get + cloudpickle_loads_lambdifyed_expr + 1*subs cycle (1000): Elapsed time: 1.768 sec >>redis_get + cloudpickle_loads_lambdifyed_expr + 10*subs cycle (100): Elapsed time: 0.204 sec >>redis_get + cloudpickle_loads_lambdifyed_expr + 100*subs cycle (10): Elapsed time: 0.046 sec >>redis_get + cloudpickle_loads_lambdifyed_expr + 1000*subs cycle (1): Elapsed time: 0.028 sec
      
      





結果はかなり実行可能に見えます。 計算は、OS Ubuntu 16.04.2 LTS、Intel®Core Processor(TM)i7-4720HQ CPU @ 2.60GHz(1コアが割り当てられている)、DDR3-1600(1Gbが割り当てられている)の仮想マシンで実行されました。



おわりに



見てくれてありがとう! 建設的な批判と興味深いコメントを歓迎します。



必要な計算のプロファイリングと最適化の問題については、 ここで提示されているアイデアとアプローチ(例では「弱すぎる」式ですが、テストの良いセット)とここ (ラムダ関数のシリアル化に関する情報)が使用されました。



ライブラリのインポートを含む、実行されたテストの全文
 import redis import pickle import dill import cloudpickle import re import copy from time import time from sympy.utilities.lambdify import lambdify from sympy import sympify, symbols class Profiler(object): #  def __init__(self,info=''): self.info = info def __enter__(self): self._startTime = time() def __exit__(self, type, value, traceback): print(self.info, "Elapsed time: {:.3f} sec".format(time() - self._startTime)) num_iter = 1000 dill.settings['recurse'] = True r = redis.StrictRedis(host='localhost', port=6379, db=0) with Profiler('read (' + str(num_iter) + '): cycle'): for i in range(num_iter): f = open('expr.txt') expr_txt = f.read() f.close() with Profiler('find unique sorted symbols (' + str(num_iter) + '): cycle'): for i in range(num_iter): symbols_set = set() result = re.findall(r"x\d_", expr_txt) for match in result: symbols_set.add(match) symbols_set = sorted(symbols_set) symbols_list = symbols(symbols_set) print() with Profiler('sympify'): expr = sympify(expr_txt) with Profiler('lambdify'): func = lambdify(tuple(symbols_list), expr, 'numpy') # returns a numpy-ready function print() with Profiler('subs cycle (' + str(num_iter) + '): cycle'): for i in range(num_iter): expr_copy = copy.copy(expr) for x in symbols_list: expr_copy = expr_copy.subs(x,1) with Profiler('subs cycle (' + str(num_iter) + '): lambdify'): for i in range(num_iter): func(*[1 for i in range(len(symbols_set))]) print() print('exp1 == exp2:', round(expr_copy,12) == round(func(*[1 for i in range(len(symbols_set))]),12)) print() with Profiler('pickle_dumps cycle (' + str(num_iter) + '): sympifyed expr'): for i in range(num_iter): pickle_dump = pickle.dumps(expr) with Profiler('pickle_loads cycle (' + str(num_iter) + '): sympifyed expr'): for i in range(num_iter): pickle.loads(pickle_dump) print() with Profiler('cloudpickle_dumps cycle (' + str(num_iter) + '): sympifyed expr'): for i in range(num_iter): cloudpickle_dump = cloudpickle.dumps(expr) with Profiler('cloudpickle_loads cycle (' + str(num_iter) + '): sympifyed expr'): for i in range(num_iter): cloudpickle.loads(cloudpickle_dump) print() with Profiler('dill_dumps cycle (' + str(num_iter) + '): sympifyed expr'): for i in range(num_iter): dill_dump = dill.dumps(expr) with Profiler('dill_loads cycle (' + str(num_iter) + '): sympifyed expr'): for i in range(num_iter): dill.loads(dill_dump) print() #,     ( 12 ),  ,    redis with Profiler('redis_set cycle (' + str(num_iter) + '): sympifyed expr'): for i in range(num_iter): r.set('expr', pickle_dump) with Profiler('redis_get cycle (' + str(num_iter) + '): sympifyed expr'): for i in range(num_iter): r.get('expr') print() with Profiler('pickle_dumps + redis_set cycle (' + str(num_iter) + '): sympifyed expr'): for i in range(num_iter): r.set('expr', pickle.dumps(expr)) with Profiler('redis_get + pickle_loads cycle (' + str(num_iter) + '): sympifyed expr'): for i in range(num_iter): pickle.loads(r.get('expr')) print() with Profiler('cloudpickle_dumps + redis_set cycle (' + str(num_iter) + '): sympifyed expr'): for i in range(num_iter): r.set('expr', cloudpickle.dumps(expr)) with Profiler('redis_get + cloudpickle_loads cycle (' + str(num_iter) + '): sympifyed expr'): for i in range(num_iter): cloudpickle.loads(r.get('expr')) print() with Profiler('dill_dumps + redis_set cycle (' + str(num_iter) + '): lambdifyed expr'): for i in range(num_iter): r.set('expr', dill.dumps(expr)) with Profiler('redis_get + dill_loads cycle (' + str(num_iter) + '): lambdifyed expr'): for i in range(num_iter): dill.loads(r.get('expr')) print('\nFINAL performance test:') with Profiler('sympify + pickle_dumps_sympifyed_expr + redis_set cycle (' + str(num_iter) + '): '): for i in range(num_iter): expr = sympify(expr_txt) r.set('expr', pickle.dumps(expr)) with Profiler('redis_get + pickle_loads_sympifyed_expr + subs cycle (' + str(num_iter) + '): '): for i in range(num_iter): loaded_expr = pickle.loads(r.get('expr')) expr_copy = copy.copy(loaded_expr) for x in symbols_list: expr_copy = expr_copy.subs(x,1) with Profiler('sympify + lambdify + dill_dumps_lambdifyed_expr + redis_set cycle (' + str(num_iter) + '): '): for i in range(num_iter): expr = sympify(expr_txt) func = lambdify(tuple(symbols_list), expr, 'numpy') r.set('expr', dill.dumps(expr)) with Profiler('redis_get + dill_loads_lambdifyed_expr + subs cycle (' + str(num_iter) + '): '): for i in range(num_iter): loaded_expr = dill.loads(r.get('expr')) func(*[1 for i in range(len(symbols_set))]) with Profiler('sympify + cloudpickle_dumps_sympifyed_expr + redis_set cycle (' + str(num_iter) + '): '): for i in range(num_iter): expr = sympify(expr_txt) r.set('expr', cloudpickle.dumps(expr)) with Profiler('redis_get + cloudpickle_loads_sympifyed_expr + subs cycle (' + str(num_iter) + '): '): for i in range(num_iter): loaded_expr = cloudpickle.loads(r.get('expr')) expr_copy = copy.copy(loaded_expr) for x in symbols_list: expr_copy = expr_copy.subs(x,1) with Profiler('sympify + lambdify + cloudpickle_dumps_lambdifyed_expr + redis_set cycle (' + str(num_iter) + '): '): for i in range(num_iter): expr = sympify(expr_txt) func = lambdify(tuple(symbols_list), expr, 'numpy') r.set('expr', cloudpickle.dumps(expr)) with Profiler('redis_get + cloudpickle_loads_lambdifyed_expr + subs cycle (' + str(num_iter) + '): '): for i in range(num_iter): loaded_expr = cloudpickle.loads(r.get('expr')) func(*[1 for i in range(len(symbols_set))]) print('\nTEST performance for complex requests:') for x in [1,10,100,1000]: with Profiler('redis_get + cloudpickle_loads_lambdifyed_expr + ' + str(x) + '*subs cycle (' + str(round(num_iter/x)) + '): '): for i in range(round(num_iter/x)): loaded_expr = cloudpickle.loads(r.get('expr')) for j in range(x): func(*[1 for i in range(len(symbols_set))]) #r.set('expr', func) >>read (1000): cycle Elapsed time: 0.014 sec >>find unique sorted symbols (1000): cycle Elapsed time: 0.156 sec >> >>sympify Elapsed time: 0.426 sec >>lambdify Elapsed time: 0.114 sec >> >>subs cycle (1000): cycle Elapsed time: 0.245 sec >>subs cycle (1000): lambdify Elapsed time: 0.026 sec >> >>exp1 == exp2: True >> >>pickle_dumps cycle (1000): sympifyed expr Elapsed time: 0.430 sec >>pickle_loads cycle (1000): sympifyed expr Elapsed time: 2.320 sec >> >>cloudpickle_dumps cycle (1000): sympifyed expr Elapsed time: 7.584 sec >>cloudpickle_loads cycle (1000): sympifyed expr Elapsed time: 2.314 sec >> >>dill_dumps cycle (1000): sympifyed expr Elapsed time: 8.259 sec >>dill_loads cycle (1000): sympifyed expr Elapsed time: 2.806 sec >> >>redis_set cycle (1000): sympifyed expr Elapsed time: 0.066 sec >>redis_get cycle (1000): sympifyed expr Elapsed time: 0.051 sec >> >>pickle_dumps + redis_set cycle (1000): sympifyed expr Elapsed time: 0.524 sec >>redis_get + pickle_loads cycle (1000): sympifyed expr Elapsed time: 2.437 sec >> >>cloudpickle_dumps + redis_set cycle (1000): sympifyed expr Elapsed time: 7.659 sec >>redis_get + cloudpickle_loads cycle (1000): sympifyed expr Elapsed time: 2.492 sec >> >>dill_dumps + redis_set cycle (1000): lambdifyed expr Elapsed time: 8.333 sec >>redis_get + dill_loads cycle (1000): lambdifyed expr Elapsed time: 2.932 sec >> >>FINAL performance test: >>sympify + pickle_dumps_sympifyed_expr + redis_set cycle (1000): Elapsed time: 15.075 sec >>redis_get + pickle_loads_sympifyed_expr + subs cycle (1000): Elapsed time: 2.929 sec >>sympify + lambdify + dill_dumps_lambdifyed_expr + redis_set cycle (1000): Elapsed time: 87.707 sec >>redis_get + dill_loads_lambdifyed_expr + subs cycle (1000): Elapsed time: 2.356 sec >>sympify + cloudpickle_dumps_sympifyed_expr + redis_set cycle (1000): Elapsed time: 23.633 sec >>redis_get + cloudpickle_loads_sympifyed_expr + subs cycle (1000): Elapsed time: 3.059 sec >>sympify + lambdify + cloudpickle_dumps_lambdifyed_expr + redis_set cycle (1000): Elapsed time: 86.739 sec >>redis_get + cloudpickle_loads_lambdifyed_expr + subs cycle (1000): Elapsed time: 1.721 sec >> >>TEST performance for complex requests: >>redis_get + cloudpickle_loads_lambdifyed_expr + 1*subs cycle (1000): Elapsed time: 1.768 sec >>redis_get + cloudpickle_loads_lambdifyed_expr + 10*subs cycle (100): Elapsed time: 0.204 sec >>redis_get + cloudpickle_loads_lambdifyed_expr + 100*subs cycle (10): Elapsed time: 0.046 sec >>redis_get + cloudpickle_loads_lambdifyed_expr + 1000*subs cycle (1): Elapsed time: 0.028 sec
      
      







コードを使用するには、以下を行う必要があります。






All Articles