大きな配列の変換処理にunsetが無い†
- ページ: BugTrack2
- 投稿者: ko-zu
- 優先順位: 低
- 状態: 完了
- カテゴリー: 本体バグ
- 投稿日: 2006-02-02 (木) 01:28:01
- バージョン:
- cvs:lib/backup.php (1.11): BugTrack2/159: Just free used memory chunks when copying memory to memory (Patched by ko-zu)
- あるページのバックアップデータを操作する際、最大でバックアップデータの二倍の量のメモリを確保していた部分について、一倍 + 一履歴分となるように修正
メッセージ†
BugTrack/732やメモリ制限周りの問題を見たのですが、データの配列を全コピーしている所で逐一unsetが必要かと思われます。
- make_backup()
foreach($backups as $age=>$data) {
$strout .= PKWK_SPLITTER . ' ' . $data['time'] . "\n"; // Splitter format
$strout .= join('', $data['data']);
+ unset($backups[$age]);
}
$strout = preg_replace("/([^\n])\n*$/", "$1\n", $strout);
簡単なメモリ測定関数を作って遊んでいたところ、(解凍後の)実データの5倍近くメモリ喰ってたもので作成。パッチでメモリ使用量ピークは半減、負荷が微増…
数kBくらいのページでも頻繁に更新すればバックアップは1メガいきますし、
大容量投稿で案外簡単に攻撃できそう、と思ったら実は難しい?
- gzfile内部のメモリピークをターゲットに、バックアップ読み込み時でメモリリミットを選択的に発動!というように攻撃出来るか試してますが、あまりうまくいってません。diffがメモリとCPUをくうため投稿に失敗する… (^^;
- ローカル(CPUが十分速い)の実験では投稿内容をうまく選べば、まれに成功してしまうこともあるみたいなので、十分試行錯誤すれば出来そうな予感。
投稿時やバックアップ書込時の記事サイズ判定を導入した方が良いかもしれません。
*1
検証…3.4MBのソース(1MB*2の投稿三回分として)をgz圧縮してget_backupに読ませてみました。
メモリ測定はforeachループ脱出直後に設置、スキン中で出力。
Memory Usage 22114 kbytes, in get_backup. // パッチ前
Memory Usage 12124 kbytes, in get_backup_unset. // 測定直前にunset
Memory Usage 12528 kbytes, in get_backup_patch. // あててみる
配列が10Mのメモリを占有…恐怖。
- ちなみに4MBくらいの改行の多いファイルにすると、40MBのmemolylimitを突破します。
どうやら内部のgzfileが喰っているようです。fopenして必要な長さずつ文字列に取り込んでしまった方がいいような気がします。
*3
- 興味深いですね :) メモリ喰ってそうな処理が色々あることは分かっているのですが、今まで深く検証されたことがないような気がします。 -- teanan
- こんにちは :) 投稿ありがとうございます。teananさんの言う通りで、実地で色々調べた例は少ないのでとても有り難いです。また編集時の処理は特に見直しの足りない部分のひとつで、このsf.jpの環境に来てやっとみんな目が向いてきた様な状況です*4。どちらも、不要になったメモリを順次開放しているだけであり、全く問題無いと思います。 -- henoheno
- 既にコメントにある通り、さらに改善する余地はあると思います。本当に直すならfopen/gzopenを使ったものに書き直して、必要な最小限のデータだけをメモリに格納したり、まさに必要なデータ構造だけを速やかに作成するよう直すのが良いと思います。ひとまずこれで状況が少し改善しそうですね :) -- henoheno
- ということで cvs:lib/backup.php (1.11) -- henoheno
- official, dev にも適用しました。(devはCVS版と同じ状態になりました) -- henoheno
- こちらも完了にしておきます。 -- henoheno
- その「簡単なメモリ測定関数」ってどうやって書くのでしょう?調べてみたい --
余談: get_backupの最適化に挑戦†
- 対応どもです。get_backupを最適化*5に挑戦してますが、streamや追加引数使ってほとんど総書き換え中です。pukiwikiは全部streamではなくgzに置き換えてますが何か問題在るんでしょうか? 一応動きそうなコード -- ko-zu
- 正規表現の\sは改行を含むので' '半角スペースに固定してます。他にもタブとか必要なのでしょうか? あと、改行処分はpregを使わずrtrim($body,"\n") . "\n" で高速化になるような気が。 -- ko-zu
- こちらの件、ついていけておらず申し訳ありません。話題別に、切りのいい所でBugTrackを立ち上げていただく方が良いでしょう -- henoheno