ホームページ  >  記事  >  バックエンド開発  >  Pythonのパフォーマンス改善方法

Pythonのパフォーマンス改善方法

高洛峰
高洛峰オリジナル
2017-02-28 16:46:041139ブラウズ

Pythonのパフォーマンスを向上させるためのいくつかのソリューション。

1. 関数呼び出しの最適化 (スペーススパン、メモリアクセスの回避)

プログラム最適化の核心は、コード実行時間のスパンとメモリ内のスペースのスパンを含む操作スパンを最小限に抑えることです。

1. 大きなデータの合計には sum

a = range(100000)
%timeit -n 10 sum(a)
10 loops, best of 3: 3.15 ms per loop
%%timeit
  ...: s = 0
  ...: for i in a:
  ...:  s += i
  ...:
100 loops, best of 3: 6.93 ms per loop

を使用します。2. 小さなデータの合計には sum

%timeit -n 1000 s = a + b + c + d + e + f + g + h + i + j + k # 数据量较小时直接累加更快
1000 loops, best of 3: 571 ns per loop
%timeit -n 1000 s = sum([a,b,c,d,e,f,g,h,i,j,k]) # 小数据量调用 sum 函数,空间效率降低
1000 loops, best of 3: 669 ns per loop

を使用しないでください。結論: ビッグデータの合計合計は非常に効率的です。 、小さなデータの合計を直接蓄積することは非常に効率的です。

2. 要素を取得するための For ループの最適化 (メモリへのアクセスを避けるためにスタックまたはレジスタを使用します)

for lst in [(1, 2, 3), (4, 5, 6)]: # lst 索引需要额外开销
  pass

インデックスの使用を避けるように努めるべきです。

for a, b, c in [(1, 2, 3), (4, 5, 6)]: # better
  pass

は、各要素に値を直接代入することと同等です。

def force():
 lst = range(4)
 for a1 in [1, 2]:
   for a2 in lst:
     for a3 in lst:
       for b1 in lst:
         for b2 in lst:
           for b3 in lst:
             for c1 in lst:
               for c2 in lst:
                 for c3 in lst:
                   for d1 in lst:
                     yield (a1, a2, a3, b1, b2, b3, c1, c2, c3, d1)
                      
%%timeit -n 10
for t in force():
  sum([t[0], t[1], t[2], t[3], t[4], t[5], t[6], t[7], t[8], t[9]])
10 loops, best of 3: 465 ms per loop
%%timeit -n 10
for a1, a2, a3, b1, b2, b3, c1, c2, c3, d1 in force():
  sum([a1, a2, a3, b1, b2, b3, c1, c2, c3, d1])
10 loops, best of 3: 360 ms per loop

3. ジェネレーターの最適化 (演算の代わりにルックアップテーブル)

def force(start, end): # 用于密码暴力破解程序
  for i in range(start, end):
    now = i
    sublst = []
    for j in range(10):
      sublst.append(i % 10) # 除法运算开销较大,比乘法大
      i //= 10
    sublst.reverse()
    yield(tuple(sublst), now)

def force(): # better
 lst = range(5)
 for a1 in [1]:
   for a2 in lst:
     for a3 in lst:
       for b1 in lst:
         for b2 in lst:
           for b3 in lst:
             for c1 in lst:
               for c2 in lst:
                 for c3 in lst:
                   for d1 in lst:
                     yield (a1, a2, a3, b1, b2, b3, c1, c2, c3, d1)
  

r0 = [1, 2] # 可读性与灵活性
r1 = range(10)
r2 = r3 = r4 = r5 = r6 = r7 = r8 = r9 = r1
force = ((a0, a1, a2, a3, a4, a5, a6, a7, a8, a9)
      for a0 in r0 for a1 in r1 for a2 in r2 for a3 in r3 for a4 in r4
      for a5 in r5 for a6 in r6 for a7 in r7 for a8 in r8 for a9 in r9)

4. 電力演算の最適化 (pow(x, y, z))

def isprime(n):
  if n & 1 == 0:
    return False
  k, q = find_kq(n)
  a = randint(1, n - 1)
  if pow(a, q, n) == 1: # 比使用 a ** q % n 运算优化数倍
    return True
  for j in range(k):
    if pow(a, pow(2, j) * q, n) == n - 1: # a **((2 ** j) * q) % n
      return True
  return False

結論: pow(x,y,z) は x**y%z よりも優れています。

5. 除算演算の最適化

In [1]: from random import getrandbits
 
In [2]: x = getrandbits(4096)
 
In [3]: y = getrandbits(2048)
 
In [4]: %timeit -n 10000 q, r = pmod(x, y)
10000 loops, best of 3: 10.7 us per loop
 
In [5]: %timeit -n 10000 q, r = x//y, x % y
10000 loops, best of 3: 21.2 us per loop

結論: pmod は // や % よりも優れています。

6. 最適化アルゴリズムの時間計算量

アルゴリズムの時間計算量は、プログラムの実行効率に最も大きな影響を与えます。Python では、次のような時間計算量を最適化するために適切なデータ構造を選択できます。特定の要素を見つけるための list と set の時間計算量はそれぞれ O(n) と O(1) です。シナリオが異なれば、最適化方法も異なります。一般的に、分割統治、分岐結合、貪欲動的プログラミングなどの考え方があります。

7. コピーとディープコピーの合理的な使用法

dict や list などのデータ構造のオブジェクトに対して、直接代入は参照を使用します。場合によっては、オブジェクト全体をコピーする必要があります。この場合、コピー パッケージで copy と deepcopy を使用できます。これらの 2 つの関数の違いは、deepcopy が再帰的にコピーすることです。効率は異なります:

In [23]: import copy
In [24]: %timeit -n 10 copy.copy(a)
10 loops, best of 3: 606 ns per loop
In [25]: %timeit -n 10 copy.deepcopy(a)
10 loops, best of 3: 1.17 us per loop

timeit の後の -n は実行数を示し、最後の 2 行は 2 つの timeit の出力に対応します。以下同様です。後者の方が一桁遅いことがわかります。

コピーに関する例:

>>> lists = [[]] * 3
>>> lists
[[], [], []]
>>> lists[0].append(3)
>>> lists
[[3], [3], [3]]

何が起こるかというと、 [[]] は空のリストを含む要素が 1 つだけあるリストなので、 [[]] * 3 の 3 つの要素すべてこの空のリストを指してください。リストの要素を変更すると、リストも変更されます。改質効率が高い。

8. dict または set を使用して要素を検索します

Python の辞書とセットは、ハッシュ テーブル (C++ 標準ライブラリ unowned_map と同様) を使用して実装され、要素を検索する時間計算量は O(1) です。

In [1]: r = range(10**7)
In [2]: s = set(r) # 占用 588MB 内存
In [3]: d = dict((i, 1) for i in r) # 占用 716MB 内存
In [4]: %timeit -n 10000 (10**7) - 1 in r
10000 loops, best of 3: 291 ns per loop
In [5]: %timeit -n 10000 (10**7) - 1 in s
10000 loops, best of 3: 121 ns per loop
In [6]: %timeit -n 10000 (10**7) - 1 in d
10000 loops, best of 3: 111 ns per loop

結論: set のメモリ使用量が最も小さく、dict の実行時間が最も短いです。

9. 合理的な使用 (ジェネレーター) と収量 (メモリの節約)

In [1]: %timeit -n 10 a = (i for i in range(10**7)) # 生成器通常遍历更高效
10 loops, best of 3: 933 ns per loop
In [2]: %timeit -n 10 a = [i for i in range(10**7)]
10 loops, best of 3: 916 ms per loop
In [1]: %timeit -n 10 for x in (i for i in range(10**7)): pass
10 loops, best of 3: 749 ms per loop
In [2]: %timeit -n 10 for x in [i for i in range(10**7)]: pass
10 loops, best of 3: 1.05 s per loop

結論: ジェネレーターを使用してトラバースしてみてください。

上記は Python のパフォーマンスを向上させるためのいくつかのソリューションです。今後も追加していきますので、必要に応じてご覧ください。

Pythonのパフォーマンス向上方法に関連するその他の記事については、PHP中国語Webサイトに注目してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。