LoginSignup
66
52

More than 5 years have passed since last update.

Python 省メモリ化のためにやったこと

Last updated at Posted at 2015-03-22

背景

4CPU メモリ4Gの小規模サーバで数日掛かる処理を複数立ち上げて
CPU100% x 4を維持して計算していたら、数時間でメモリ不足が発生して
CPU使用率が激減した。どうやらメモリ不足に陥っていたようだ。

起きていた事

処理時間の増加ニトモナイ、プロセスのメモリ使用量が増加
→メモリ枯渇
→メモリswap発生
→メモリアクセス速度が大幅劣化
→メモリ待ちCPU使用率が1%以下
→いつまでたっても終わらない(性能大幅劣化)

対策0. お金で解決

たいていの場合、サーバ台数増やしたり、サーバの性能アップさせたら解決します。
個人のPJだったので選択しませんでしたが、メモリ32Gくらいのサーバに乗り換えたら
それだけで解決したんだろうなぁ。

対策1. 処理の垂直分割

pythonのメモリ管理は基本VM任せの全自動。メモリリークの根本解決はプロセス殺すしかない
処理にベキ等性もたせて、1つのコマンドで8種類のカテゴリの処理を行っていた箇所を分割した

改善前

class Category(Enum):
    A = 1
    B = 2
    C = 3

for category in Category:
  benchmark(category)

改善後

category = manage.get_category_by_priority()
benchmark(category)

対策2. supervisorを導入した

改善1のコードは8回にわけて実行する必要があったので、プロセスが停止したら
再度立ち上げる処理を各必要があった。そんなときsupervisor使うと便利

shell
easy_install supervisor
echo_supervisord_conf > /etc/supervisord.conf
supervisord
supervisord status
alias sc='supervisorctl'
sc restart
sc reread
sc stop all
sc status
sc restart all

対策3. 手動GCに手を染めた

pythonのGCは詳しくないので副作用があるかも。
いまのところメモリリーク解消して安定していますが
黒魔術の可能性あるなので、あまりオススメできません。

クラスキャッシュを多用していると、python2系だとメモリリークが頻発して
消費メモリ量がどんどん増えていく問題は、python3にすると解消するのだろうか

class Category(Enum):
    A = 1
    B = 2
    C = 3

for category in Category:
  benchmark(category)

def benchmark(category):
  bulk = []
  tmp_data = Tmp.get_all()
  for _tmp in tmp_data:
    bulk.append(calc(_tmp))
  DBTable.bulk_create(bulk)  # バルク!

  # メモリ解放
  import gc
  del tmp_data
  del bulk
  gc.collect()

参考: gc — ガベージコレクタインターフェース
http://docs.python.jp/2/library/gc.html

66
52
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
66
52