呼び出しが高価な場合にPythonで単一責任原則(SRP)を使用する


12

いくつかのベースポイント:

  • Pythonのメソッド呼び出しは、その解釈された性質により「高価」です。理論的には、コードが十分に単純な場合、Pythonコードの分解は、読みやすさと再利用に加えてマイナスの影響を及ぼします(これは、ユーザーにとってはそれほどではなく、開発者にとって大きな利益です)。
  • 単一責任原則(SRP)は、コードを読み取り可能に保ち、テストと保守が容易です。
  • プロジェクトには、読み取り可能なコード、テスト、および時間パフォーマンスが必要な特別な種類の背景があります。

たとえば、いくつかのメソッド(x4)を呼び出すこのようなコードは、次のメソッド(1つ)よりも低速です。

from operator import add

class Vector:
    def __init__(self,list_of_3):
        self.coordinates = list_of_3

    def move(self,movement):
        self.coordinates = list( map(add, self.coordinates, movement))
        return self.coordinates

    def revert(self):
        self.coordinates = self.coordinates[::-1]
        return self.coordinates

    def get_coordinates(self):
        return self.coordinates

## Operation with one vector
vec3 = Vector([1,2,3])
vec3.move([1,1,1])
vec3.revert()
vec3.get_coordinates()

これと比較して:

from operator import add

def move_and_revert_and_return(vector,movement):
    return list( map(add, vector, movement) )[::-1]

move_and_revert_and_return([1,2,3],[1,1,1])

そのようなものを並列化する場合、パフォーマンスを失うのはかなり客観的です。これは単なる例です。私のプロジェクトには、数学などのミニルーチンがいくつかあります-作業はずっと簡単ですが、プロファイラーはそれを嫌っています。


固有の実装が直接影響するため、Pythonのパフォーマンスを損なうことなく、どのように、どこでSRPを採用しますか?

リリースのために物事をインラインにする何らかのプリプロセッサのような回避策はありますか?

それとも、Pythonはコードブレークダウンの処理だけでは不十分ですか?



19
価値のある2つのコード例では、責任の数に違いはありません。 SRPは、運動を数える方法ではありません。
ロバートハーヴェイ

2
@RobertHarveyあなたは正しい、貧しい例でごめんなさい。時間があればもっと良い例を編集します。いずれの場合も、読みやすさと保守性が損なわれ、クラスとそのメソッドを削減すると、最終的にコードベース内でSRPが破損します。
lucasgcb

4
そのノートの関数呼び出しは、任意の言語で高価であり、 AOTコンパイラがインライン化の贅沢を持っているものの、
イーブイ

6
PyPyなどのPythonのJITted実装を使用します。この問題はほとんど修正されるはずです。
バクリウ

回答:


17

Pythonは単にコードブレークダウンを完全に処理するのが苦手ですか?

残念ながら、Pythonは低速であり、関数をインライン化し、コードをくすることでパフォーマンスが大幅に向上するという逸話がたくさんあります。

回避策としてCythonがあります。これはPythonのコンパイル済みバージョンであり、はるかに高速です。

-編集コメントとその他の回答の一部に対処したかっただけです。それらの推力はおそらくPython固有のものではありませんが。しかし、より一般的な最適化。

  1. 問題が発生するまで最適化しないで、ボトルネックを探す

    一般的に良いアドバイス。しかし、「通常の」コードは通常パフォーマンスが良いという前提があります。これは常にそうではありません。個々の言語とフレームワークにはそれぞれ独自の特異性があります。この場合、関数呼び出し。

  2. わずか数ミリ秒で、他のものは遅くなります

    強力なデスクトップコンピューターでコードを実行している場合、シングルユーザーコードが数秒で実行される限り、おそらく気にする必要はありません。

    ただし、ビジネスコードは複数のユーザーで実行される傾向があり、負荷をサポートするために複数のマシンが必要です。コードの実行速度が2倍であれば、ユーザーの数は2倍、マシンの数は半分になります。

    マシンとデータセンターを所有している場合、通常、CPUパワーに大きなオーバーヘッドがかかります。コードの実行が少し遅い場合は、少なくとも2台目のマシンを購入する必要があるまで、それを吸収できます。

    必要な計算能力だけを使用し、それ以上使用しない最近のクラウドコンピューティングでは、非パフォーマンスコードに直接コストがかかります。

    パフォーマンスを改善することで、クラウドベースのビジネスの主な費用を大幅に削減することができ、パフォーマンスは真正面にあるべきです。


1
Robert's Answerは、この種の最適化(この質問に当てはまる)を行う背後にある潜在的な誤解のいくつかの基盤をカバーするのに役立ちますが、Pythonコンテキストと少し直接的にインラインで状況に答えると感じています。
lucasgcb

2
申し訳ありませんが、やや短いです。もっと書く時間はありません。しかし、ロバートはこれについて間違っていると思います。Pythonでの最善のアドバイスは、コーディングするときにプロファイルを作成することです。いけないあなたは、問題を見つけた場合、それはパフォーマンスだけで最適化されますと仮定
ユアン・

2
@Ewan:私のアドバイスに従うために、最初にプログラム全体を書く必要はありません。1つまたは2つの方法で十分なプロファイリングを取得できます。
ロバートハーヴェイ

1
あなたはまた、JITコンパイルのpythonであるpypy、試すことができます
イーブイ

2
@Ewan関数呼び出しのパフォーマンスオーバーヘッドが本当に心配な場合は、おそらくPythonには向いていないでしょう。しかし、実際には多くの例は考えられません。ビジネスコードの大部分はIOが制限されており、CPUに負荷のかかる処理は通常、ネイティブライブラリ(numpy、tensorflowなど)を呼び出すことで処理されます。
Voo

50

多くの潜在的なパフォーマンスの問題は実際には問題ではありません。 あなたが提起する問題はそれらの一つかもしれません。専門用語では、それらが実際の問題の時期尚早な最適化であることの証拠なしに、それらの問題について心配することを呼びます

Webサービスのフロントエンドを作成する場合、ネットワーク経由でデータを送信するコストはメソッド呼び出しにかかる時間をはるかに超えるため、パフォーマンスは関数呼び出しの影響をあまり受けません。

ビデオ画面を1秒間に60回更新するタイトなループを作成している場合、問題になる可能性があります。しかし、その時点で、Pythonを使用してそれを実行しようとしている場合、あなたはより大きな問題を抱えていると主張します。

いつものように、あなたが見つける方法は測定することです。コードでパフォーマンスプロファイラーまたはいくつかのタイマーを実行します。実際に問題になっているかどうかを確認してください。


単一責任の原則は、法律または義務ではありません。それはガイドラインまたは原則です。ソフトウェア設計は常にトレードオフです。絶対的なものはありません。読みやすさや保守性を犠牲にして速度を犠牲にすることは珍しいことではないため、パフォーマンスの祭壇でSRPを犠牲にしなければならない場合があります。ただし、パフォーマンスの問題があることがわかっている場合を除き、そのトレードオフを行わないでください。


3
クラウドコンピューティングを発明するまで、これは真実だったと思います。現在、2つの機能の1つは他の機能の4倍の費用がかかります。
ユアン

2
@Ewanの4回は、気にするのに十分なほど重要であると測定するまでは重要ではありません。Fooが1ミリ秒かかり、Barが4ミリ秒かかる場合、それは良くありません。ネットワークを介したデータの送信に200ミリ秒かかることがわかるまで。その時点で、Barが遅くてもそれほど問題ではありません。(X倍遅くても、顕著なまたはインパクトのある違いをもたらさない1つの可能な例です。必ずしも超現実的ではありません。)
Becuzz

8
@Ewan法案の削減により月額15ドル節約できたが、それを修正してテストするのに1時間あたり125ドルの請負業者が4時間かかる場合、ビジネスの時間の価値がない(または少なくとも正しくしない)ことを簡単に正当化できます市場投入までの時間が非常に重要な場合など)。常にトレードオフがあります。そして、ある状況で意味のあることは、別の状況では意味がないかもしれません。
Becuzz

3
AWSの請求書は確かに非常に低いです
ユアン

6
@Ewan AWSは、とにかくバッチ単位で上限に達します(標準は100ミリ秒)。つまり、この種の最適化は、常に次のチャンクにプッシュすることを避けている場合にのみ、何も節約しません。
Delioth

2

まず、いくつかの明確化:Pythonは言語です。Python言語で記述されたコードを実行できるいくつかの異なるインタープリターがあります。リファレンス実装(CPython)は通常、誰かが「Python」についてそれが実装であるかのように話すときに参照されるものですが、パフォーマンス特性について話すときは実装間で大きく異なる可能性があるため正確であることが重要です。

固有の実装が直接影響するため、Pythonのパフォーマンスを損なうことなく、どのように、どこでSRPを採用しますか?

ケース1.) 純粋なPythonモジュールのみに依存する純粋なPythonコード(<= Python言語バージョン3.5、3.6には「ベータレベルサポート」)がある場合、どこでもSRPを受け入れ、PyPyを使用して実行できます。PyPy(https://morepypy.blogspot.com/2019/03/pypy-v71-released-now-uses-utf-8.html)は、ジャストインタイムコンパイラー(JIT)を備え、機能を削除できるPythonインタープリターです実行されたコードをトレースして「ウォームアップ」するのに十分な時間がある限り、オーバーヘッドを呼び出します(数秒のIIRC)。**

CPythonインタープリターの使用に制限されている場合は、Cで記述された拡張機能に低速関数を抽出できます。これは、プリコンパイルされ、インタープリターのオーバーヘッドを受けません。どこでもSRPを使用できますが、コードはPythonとCに分割されます。SRPを選択的に放棄し、Pythonコードのみに固執するよりも保守性が良いか悪いかは、チームに依存しますが、 CPythonで解釈される最も最適化された純粋なPythonコードでさえ、間違いなく高速になります。Pythonの最速の数学ライブラリの多くは、この方法を使用しています(numpyおよびscipy IIRC)。これはケース2の良いセグエです...

ケース2.) C拡張機能を使用する(またはC拡張機能を使用するライブラリに依存する Pythonコードがある場合、PyPyはそれらの記述方法に応じて役立つ場合と役に立たない場合があります。詳細については、http: //doc.pypy.org/en/latest/extending.htmlを参照してください。ただし、要約すると、CFFIのオーバーヘッドは最小限であり、CTypesは低速です(PyPyで使用すると、CPythonよりも遅くなる場合があります)。

Cython(https://cython.org/)は、私があまり経験のない別のオプションです。完全を期すためにこれに言及しているので、私の答えは「独立している」ことができますが、専門知識は主張しません。私の限られた使用から、PyPyで「無料」で得ることができる同じ速度の改善を得るために一生懸命に働かなければならないと感じました。コードを別の場所で再利用したり、その一部をライブラリに抽出したりすると、すべてのコードをPythonインタープリターで実行でき、Cythonで実行する必要はありません)。

Cythonに「ロック」されるのが怖いのですが、PyPy用に書かれたコードはCPythonでも実行できます。

**本番環境でのPyPyに関する追加の注意事項

大規模なコードベースでPyPyに「ロックイン」するという実際的な効果がある選択を行う場合は、十分に注意してください。一部の(非常に人気があり便利な)サードパーティライブラリは、前述の理由でうまく動作しないため、それらのライブラリのいずれかが必要であると気付いた場合、後で非常に難しい決定を下す可能性があります。私の経験は主に、PyPyを使用して、本番環境にごくわずかな複雑さを追加する企業環境でパフォーマンスに敏感な一部の(しかしすべてではない)マイクロサービスを高速化することです(すでに複数の言語がデプロイされており、いくつかは2.7 3.5とにかく実行)。

PyPyとCPythonの両方を使用すると、言語仕様自体によって行われた保証のみに依存し、いつでも変更される可能性のある実装の詳細に依存しないコードを書くことを定期的に余儀なくされました。そのような詳細について考えることは余分な負担になると思うかもしれませんが、私はそれが私の専門的な開発において価値があると感じ、Pythonエコシステム全体にとって「健康」だと思います。


うん!私は原則を捨ててワイルドコードを書くのではなく、このケースではC拡張に焦点を当てることを検討してきましたあなたの意見では賢明なアプローチになりますか?
lucasgcb

1
ケース1(2番目のパラ)では、関数自体がコンパイルされていても、関数を呼び出すオーバーヘッドが同じになりませんか?
ユアン

CPythonは、一般的に真剣に考えられている唯一のインタープリターです。PyPyはおもしろいですが、確かに広く採用されることはありません。さらに、その動作 CPython とは異なり、scipyなどのいくつかの重要なパッケージでは動作しません。健全な開発者のほとんどは、PyPyを本番に推奨しません。そのため、言語と実装の区別は実際には重要ではありません。
jpmc26

頭に釘を打ったと思いますが。優れたインタープリターやコンパイラーを作成できない理由はありません。それは言語としてのpythonに固有のものではありません。あなたはちょうど実用的な現実に立ち往生しています
ユアン

@ jpmc26私は本番環境でPyPyを使用しましたが、他の経験豊富な開発者にPyPyを使用することを検討することをお勧めします。これは、軽量レストAPIにfalconframework.orgを使用するマイクロサービスに最適です(一例)。開発者が言語の保証ではない実装の詳細に依存しているため、動作が異なるため、PyPyを使用しない理由はありません。コードを書き直す理由です。とにかく、CPythonがその実装に変更を加えると、同じコードが壊れる可能性があります(言語仕様に準拠している限り、自由に実行できます)。
スティーブンジャクソン
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.