Basic Knowledge of Space Complexity
空間計算量の話Ryo TomidokoroPHP Study 2019/4/24@hanhan1978
View Slide
前回までのあらすじ
@hanhan1978は、PHPerKaigi2019で時間計算量のトークをしてきた。しかし、Q&Aで質問されたのは空間計算量についての質問だったため、何を言っているのかよく分からない。ちなみにAsk The Speakerでは、「今、Out of Memoryで困ってます。」という人生相談を受けた。なので、おそらく顧客が本当に知りたいのは時間計算量ではなく、空間計算量。
時間計算量のおさらい※手抜きではない。
コード例
コードの問題は何?仕様は満たしている、動作も問題ない。しかし、データの増大と共に問題を起こす可能性がある。実際に、サンプルコードの負荷試験をして、データ量と処理時間の関係を確認する。
データ件数と処理時間の関係
計算量視点を持ついつものコーディングに、新しい視点として、計算量を加えて見よう。
計算量とは?
2つの計算量時間計算量(Time Complexity)プログラムの演算の回数空間計算量(Space Complexity)プログラムが利用するメモリ使用量
時間計算量の測り方
単純な掛け算関数
O記法 (Big-O notation)計算量の目安を表す便利な記法。O記法での表現によって、そのアルゴリズムがどんな時間計算量特性を持つのかを理解できる。O(1), O(n), O(n^2), O(n*log n)括弧の中身が計算量のオーダーを表す
データ量と時間計算量特性の関係[引用] 開発新卒に捧ぐ、基本のアルゴリズムと計算量https://www.techscore.com/
アルゴリズムと計算量アルゴリズム 計算量バブルソート O(n^2)マージソート O(n * log n)バイナリーサーチ O(log n)
計算量視点で最初の例を読み返す
計算量オーダーを下げる
改善例※ $purchased_usersのkeyとvalueを入れ替えておく
改善例
処理時間を再計測
データ件数と処理時間の関係(改善後)
計算量という視点を持つことで、プログラムが潜在的にもつ問題点を見つけることが出来た。※ただし、データ量が少なければ問題ないことが多いので、無闇に計算量ばかり指摘するのはやめましょう。
ここから空間計算量の話
だいたい時間計算量の話と一緒手抜きではない...
例えばこんなプログラム
例えばこんなプログラム1000 + 100 * 5 = 1500(Byte)
PHPにもGCはあるけど、一回のウェブリクエスト中では発動しない。リクエスト終了後にメモリがクリアされる方が先。時間計算量同様に、一個ずつメモリの使用量を計算していけば理詰めで算出できる。(する?)
PHPのGChttps://www.php.net/manual/en/features.gc.php
gc_collect_cycles()を実行すると、強制的にGCさせられるらしい。そういえば、composerもそのへんで一度トラブルを抱えていたような…
いや、違う
顧客が本当に求めているのは、空間計算量の話でもなく、Allowed memory size of … でエラーが起きないようにするにはどうしたらいいか?だった。
Allowed memory size of ..で怒られなくする話Ryo TomidokoroPHP Study 2019/4/24@hanhan1978
サンプル
実行すると
怒られる
基本的に無限にメモリを利用するようなプログラムを書いた場合は、どんなにメモリを積んでも避けられない。無限にメモリを利用しない場合は、避ける方法が何個かある。
たくさんメモリを割り当てるini_set(‘memory_limit’, ‘8G’);とかすると、きっと頑張ってくれるが、もちろん根本的解決ではない。メモリ量を上回る富豪プログラマーが破産させてくれるはず。
最近のfreeコマンドは、available列を表示してくれるので、空きメモリ容量が分かりやすい。memory_limitを上げるときは空き容量と相談しながら。
例えばunbuffered query$pdo->setAttribute(PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, false);こうすると、DB側のメモリにリザルトセットを積めるので、クライアント側はメモリを節約できる。けど、これやってる間は他のクエリを受け付けてくれないので、用法用量を守らないと事故る。
データ分割可変の大量データを扱うのが問題なので、固定量のデータを扱うプログラムに変更すればよい。王道だけど、バッチプログラムとかでは再実行性とか冪等性に配慮する必要はある。
ファイルIOメモリにファイルを全部積むようなプログラムを書くと、発生してしまいがち。GuzzleとかStreamであつかえるAPIがあるので、そっちを使いましょう。DropboxAPIとかも大容量ファイルを省メモリで送ることのできるAPIをちゃんと準備してくれています。
その他のtipsmemory_get_usage で現在のメモリ使用量を取得することができる。これはZend/zend_alloc.cの_zend_mm_heap->sizeを返してる。バッチとかならvmstatとかでメモリの減り具合を見ても分かる。
バッチならまだしも、ウェブアプリでMemoryallocation ...が出てる場合は、どうせろくでもない事をしているに違いない。間違いない。とにかく、可変でサイズの大きいデータを扱う場合は一次作業場所(メモリ)が限られていることを事前に意識しないと問題が起きがち。
ところで
このmemory allocationのエラーの本当の意味を理解するには、PHPのプログラムがどのように実行されているのかを知る必要がある。すごく細かく説明されているサイトを発見した
https://codingjp.com/web/21119/
PHPが実行されていく過程
Nginx + PHP-FPM, php-cliの場合※諸事情によりApache mod-php駄目絶対マンになったので、今後私からその方面の情報が出てくることは多分ない。
Allowed memory size of ...実行時にmemory_limitをオーバーした場合
Out of memory ...実行時に新しいchunkを割り当てられない場合
つまり誰か、私と一緒にゆるゆるPHP闇勉強会やろうず
おしまい