web-dev-qa-db-ja.com

コードのどの部分がPython

前の質問で、マルチプロセッシング、複数のコアを使用してプログラムをより高速に実行することについて尋ねていましたが、誰かがこれを教えてくれました:

多くの場合、4倍の改善と比較してより良いコードで100倍以上の最適化を得ることができ、マルチプロセッシングでさらに複雑になります。

その後、私は次のことを推奨しました。

プロファイラーを使用して何が遅いのかを理解し、それを最適化することに集中します。

だから私はこの質問に行きました: スクリプトをどのようにプロファイリングできますか?

ここで私はcProfileを見つけ、それがどのように機能するかを確認するためにいくつかのテストコードに実装しました。

これは私のコードです:

_import cProfile

def foo():
    for i in range(10000):
        a = i**i
        if i % 1000 == 0:
            print(i)

cProfile.run('foo()')
_

しかし、それを実行した後、これは私が得たものでした:

_0
1000
2000
3000
4000
5000
6000
7000
8000
9000
         1018 function calls in 20.773 seconds

   Ordered by: standard name

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
        1    0.000    0.000   20.773   20.773 <string>:1(<module>)
      147    0.000    0.000    0.000    0.000 rpc.py:150(debug)
       21    0.000    0.000    0.050    0.002 rpc.py:213(remotecall)
       21    0.000    0.000    0.002    0.000 rpc.py:223(asynccall)
       21    0.000    0.000    0.048    0.002 rpc.py:243(asyncreturn)
       21    0.000    0.000    0.000    0.000 rpc.py:249(decoderesponse)
       21    0.000    0.000    0.048    0.002 rpc.py:287(getresponse)
       21    0.000    0.000    0.000    0.000 rpc.py:295(_proxify)
       21    0.001    0.000    0.048    0.002 rpc.py:303(_getresponse)
       21    0.000    0.000    0.000    0.000 rpc.py:325(newseq)
       21    0.000    0.000    0.002    0.000 rpc.py:329(putmessage)
       21    0.000    0.000    0.000    0.000 rpc.py:55(dumps)
       20    0.000    0.000    0.001    0.000 rpc.py:556(__getattr__)
        1    0.000    0.000    0.001    0.001 rpc.py:574(__getmethods)
       20    0.000    0.000    0.000    0.000 rpc.py:598(__init__)
       20    0.000    0.000    0.050    0.002 rpc.py:603(__call__)
       20    0.000    0.000    0.051    0.003 run.py:340(write)
        1   20.722   20.722   20.773   20.773 test.py:3(foo)
       42    0.000    0.000    0.000    0.000 threading.py:1226(current_thread)
       21    0.000    0.000    0.000    0.000 threading.py:215(__init__)
       21    0.000    0.000    0.047    0.002 threading.py:263(wait)
       21    0.000    0.000    0.000    0.000 threading.py:74(RLock)
       21    0.000    0.000    0.000    0.000 {built-in method _struct.pack}
       21    0.000    0.000    0.000    0.000 {built-in method _thread.allocate_lock}
       42    0.000    0.000    0.000    0.000 {built-in method _thread.get_ident}
        1    0.000    0.000   20.773   20.773 {built-in method builtins.exec}
       42    0.000    0.000    0.000    0.000 {built-in method builtins.isinstance}
       63    0.000    0.000    0.000    0.000 {built-in method builtins.len}
       10    0.000    0.000    0.051    0.005 {built-in method builtins.print}
       21    0.000    0.000    0.000    0.000 {built-in method select.select}
       21    0.000    0.000    0.000    0.000 {method '_acquire_restore' of '_thread.RLock' objects}
       21    0.000    0.000    0.000    0.000 {method '_is_owned' of '_thread.RLock' objects}
       21    0.000    0.000    0.000    0.000 {method '_release_save' of '_thread.RLock' objects}
       21    0.000    0.000    0.000    0.000 {method 'acquire' of '_thread.RLock' objects}
       42    0.047    0.001    0.047    0.001 {method 'acquire' of '_thread.lock' objects}
       21    0.000    0.000    0.000    0.000 {method 'append' of 'collections.deque' objects}
        1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}
       21    0.000    0.000    0.000    0.000 {method 'dump' of '_pickle.Pickler' objects}
       20    0.000    0.000    0.000    0.000 {method 'get' of 'dict' objects}
       21    0.000    0.000    0.000    0.000 {method 'getvalue' of '_io.BytesIO' objects}
       21    0.000    0.000    0.000    0.000 {method 'release' of '_thread.RLock' objects}
       21    0.001    0.000    0.001    0.000 {method 'send' of '_socket.socket' objects}
_

私のコードのどの部分が最も長くかかっているかを示すことが期待されていました。たとえば、_a = i**i_が計算に最も時間がかかっていることを示すためですが、 foo()関数が最も長くかかりましたが、その関数内で最も長くかかったものは、データからはわかりません。

また、これを実際のコードに実装すると、同じことが行われます。すべてが関数内にあり、関数内の時間が長くかかっているのではなく、どの関数が最も長くかかっているかを知るだけです。

これが私の主な質問です:

  1. 関数内でコードに時間がかかっている原因を知るにはどうすればよいですか(cProfileを使用する必要がありますか?)

  2. 最も多くのCPUを使用しているものがわかったら、コードを最適化するための最良の設定方法は何ですか

注:私のRAMとディスクなどは絶対に問題ありませんが、最大になっているのはCPUだけです(12% CPUは単一コアでのみ実行されるため)

18

関数内でコードに時間がかかっている原因を知るにはどうすればよいですか(cProfileを使用する必要がありますか?)

はい、cProfileを使用できますが、質問の仕方で line_profiler (サードパーティのモジュール、それをインストールする必要があります)は優れたツールではありません。

関数のプロファイルを作成するときに、このパッケージのIPython/Jupyterバインディングを使用しています。

%load_ext line_profiler

関数を実際にプロファイルするには:

%lprun -f foo foo()
#             ^^^^^---- this call will be profiled
#         ^^^-----------function to profile

これはこの出力を生成します:

Timer unit: 5.58547e-07 s

Total time: 17.1189 s
File: <ipython-input-1-21b5a5f52f66>
Function: foo at line 1

Line #      Hits         Time  Per Hit   % Time  Line Contents
==============================================================
     1                                           def foo():
     2     10001        31906      3.2      0.1      for i in range(10000):
     3     10000     30534065   3053.4     99.6          a = i**i
     4     10000        75998      7.6      0.2          if i % 1000 == 0:
     5        10         6953    695.3      0.0              print(i)

それには興味深いかもしれないいくつかの事柄が含まれています。例えば ​​99.6%の時間はi**i行。

  1. 最も多くのCPUを使用しているものがわかったら、コードを最適化するための最良の設定方法は何ですか

場合によります。さまざまな関数/データ構造/アルゴリズムを使用する必要がある場合もあれば、何もできない場合もあります。しかし、少なくともボトルネックがどこにあるかはわかっており、ボトルネックまたは他の場所での変更がどれほどの影響を与えるかを見積もることができます。

18
MSeifert

プロファイリングログで気づいたように、cProfileの最大解像度はfunctionです。

そう:

  • 関数が小さい場合は、どの部分に時間がかかっているかを把握できる場合があります(inのような組み込みの呼び出しでは難しい場合があります)。
  • 関数が大きい場合は、それを「プロファイル可能」になる小さな関数に切り詰めるときが来たかもしれませんが、関数呼び出しのオーバーヘッドにより物事が遅くなる可能性があります