CPython vs. IronPython:MD5ハッシュ計算

どういうわけかプロジェクトでは、クライアントアプリケーションの自動更新を行う必要がありました。 .Netから簡単にアクセスできる国内の暗号化プロバイダーと連携していたため、IronPythonで作成されました。 同時に、C#は選択されませんでした。これは、Pythonがサーバー側で既に積極的に使用されており、あまり学習し直したくないためです。



それは簡単に思えます。 アプリケーションに含まれるファイルのmd5ハッシュを計算し、「相対パス」:「md5」という形式の行を持つすべてのファイルにすべてをコンパイルし、nginx静的を配布ディレクトリにアップロードするスクリプトがコンパイルされました。 起動時のクライアントアプリケーションはファイルを取得し、同様のスクリプトを実行して、標準で結果を検証します。



しかし、その後、細部が明らかになりました。 IronPythonでは、スクリプトの実行が数倍遅くなりました。 そして、これはかなり高速なハードウェアです。 ユーザーにとっては、はるかに弱い可能性があります。 最適化が始まり、その間にこの例でCPythonとIronPythonのパフォーマンスを比較するというアイデアが生まれました。 この記事ではそれぞれ、CPython、IronPython、IronPythonに適応スクリプトを使用した3つの個別の結果を検討しています。

カットの下の結果。



構成




スクリプトの「食べ物」として、アプリケーションファイルのあるディレクトリが使用されました。 IronPythonのランタイム自体、追加のライブラリ、およびその他の必要なファイルが含まれています。 キロバイトから3メガバイトまでの合計約350ファイル。



スクリプトコード:

1| import os 2| import hashlib 3| 4| def getMD5sum(fileName): 5| m = hashlib.md5() 6| fd = open(fileName, 'rb') 7| b = fd.read() 8| m.update(b) 9| fd.close() 10| return m.hexdigest() 11| 12| output = '' 13| rootpath = 'app' 14| 15| for dirname, dirnames, filenames in os.walk(rootpath): 16| for filename in filenames: 17| fname = os.path.join(dirname, filename).replace('\\', '/') 18| md5sum = getMD5sum(fname) 19| output+='{0}:{1}\n'.format(fname.replace(rootpath, ''), md5sum) 20| 21| f = open('./checksums.csv', 'w') 22| f.write(output) 23| f.close()
      
      







IronPythonに適合した同じスクリプト:

  1| import os 2| import System.IO 3| from System.Security.Cryptography import MD5CryptoServiceProvider 4| 5| def getMD5sum(fileName): 6| b = System.IO.File.ReadAllBytes(fileName) 7| md5 = MD5CryptoServiceProvider() 8| hash = md5.ComputeHash(b) 9| result = '' 10| for b in hash: 11| result += b.ToString("x2") 12| return result 13| 14| output = '' 15| rootpath = 'app' 16| 17| for dirname, dirnames, filenames in os.walk(rootpath): 18| for filename in filenames: 19| fname = os.path.join(dirname, filename).replace('\\', '/') 20| md5sum = getMD5sum(fname) 21| output += fname.replace(rootpath, '', 1) + ':' + md5sum + '\n' 21| 22| System.IO.File.WriteAllText('checksums.csv', output)
      
      





原則として、全体の適応は、ファイルの読み取り/書き込みとハッシュの計算が.Netで上書きされるという事実に帰着します。 これにより、十分なパフォーマンスが向上します。 これは、ipy自体がc#で記述されており、「バッテリー」のほとんどが.Netの単なるラッパーであるためです。 この意味で、メインの19行目と適合した21行目の違いはおもしろそうです。



 19| output += '{0}:{1}\n'.format(fname.replace(rootpath, ''), md5sum)
      
      





 21| output += fname.replace(rootpath, '', 1) + ':' + md5sum + '\n'
      
      





ipyでは、2番目のオプションの方が高速でした。 pythonに関しては、統計誤差を超える差を確認できませんでした。



結果


そして、コールドスタートの結果(平均):



肉眼で見ると、PythonとIronPythonで同じスクリプトが実行され、Python側で5倍以上の利点があることがわかります。 同時に、ipyに適合したスクリプトは、まだ実行されていますが、まだ遅いですが、結果はすでにかなり受け入れられます。



もう1つ微妙な違いがあります。クライアントでは、このスクリプトをアプリケーション自体に組み込む必要があります。 したがって、関心があるのはコールドスタートの時間ではなく、インタープリターの開始を考慮に入れない直接実行の時間です。 コードをループに配置することにより、この動作を再現します。



典型的な結果:
Cpython イピー ipy(適応)
0:00:00.057000 0:00:00.327000 0:00:00.161000
0:00:00.056000 0:00:00.243000 0:00:00.093000
0:00:00.055000 0:00:00.234000 0:00:00.099000
0:00:00.058000 0:00:00.228000 0:00:00.096000
0:00:00.055000 0:00:00.226000 0:00:00.093000
0:00:00.055000 0:00:00.236000 0:00:00.093000
0:00:00.055000 0:00:00.225000 0:00:00.093000
0:00:00.055000 0:00:00.261000 0:00:00.092000
0:00:00.057000 0:00:00.240000 0:00:00.092000
0:00:00.057000 0:00:00.227000 0:00:00.093000


結論


このテストの結果によれば、多かれ少なかれもっともらしい結論を引き出すことはすでに可能です。 IronPythonインタープリター自体を開始するのに必要な時間は約0.7秒であることがわかります。 この間、ネイティブpythonで実行されているスクリプトは完了する時間があります。 CPythonはほぼ瞬時に起動し、ご覧のとおり、最初の反復は後続の反復と同じくらい高速でした。 ipy用に最適化されたコード(ホットなコードで起動されたコード)でさえ、ネイティブコードよりもほぼ1.5倍遅いことがわかります。



CPythonとIronPythonに同じコードを使用することは、パフォーマンスがまったく重要である場合にはまったく適切ではないようです。 ただし、同じコードを使用する場合のIronPythonの制限はこれだけではありません。 パフォーマンスに関連しないニュアンスとバグがいくつかありますが、これはすでにこの記事の範囲外です。 ただし、IronPythonの使用を拒否することも問題外であることを控えておきたいと思います。 彼は彼に割り当てられた任務に非常にうまく対処しています。



建設的な批判を聞いてうれしいです。



UPD

mstyuraは、 ipy用のスクリプトのより最適化されたバージョンを提案し、より興味深い結果を得ました。



 from System.IO import StreamWriter, Directory, SearchOption, File, Path from System import String, BitConverter, Environment, Array from System.Security.Cryptography import MD5CryptoServiceProvider def getMD5sum(fileName): stm = File.OpenRead(fileName) md5 = MD5CryptoServiceProvider() hash = md5.ComputeHash(stm) stm.Close() return BitConverter.ToString(hash).Replace("-", "").ToLower() rootpath = 'app' workingDir = Environment.CurrentDirectory Environment.CurrentDirectory = rootpath appFiles = Directory.EnumerateFiles('.', '*', SearchOption.AllDirectories) output = StreamWriter(File.OpenWrite(Path.Combine(workingDir, 'checksums.csv'))) for _, file in enumerate(appFiles): output.Write(file.replace(".", "", 1).replace("\\", "/")) output.Write(":") output.WriteLine(getMD5sum(file)) output.Close() Environment.CurrentDirectory = workingDir
      
      





このオプションの結果:

0:00:00.116000

0:00:00.063000

0:00:00.064000

0:00:00.063000

0:00:00.059000

0:00:00.059000

0:00:00.058000

0:00:00.058000

0:00:00.058000

0:00:00.059000



もう少しすれば、彼はpythonを追い抜くことがわかります。 もちろん、開始はまだ遅いですが、Pythonライブラリがインポートも使用もされていないという事実により、明らかに高速になっています。 ただし、インポートosを追加し、os.walk(rootpath)をアイドル状態にすると、最初の反復の時間が約0.145秒に増加します! ただし、明らかにこの機能自体は非常に重いです。 os.getcwd()のような単純なものを呼び出すと、速度はあまり変わりません



All Articles