p5.js の createGraphics() がメモリを放してくれない!

2021年12月12日日曜日

p5.js テクニック 中級者向け

t f B! P L

Qiita p5.js Advent Calendar 2021 13日目の記事です。

放してくれない猫ちゃん

『ちょっと奥さん!ご存知でした? p5.js の createGraphics() って、メモリを掴んで離してくれないんですって!』
『んまー!それじゃブラウザ落ちちゃうじゃない!』
『そうなのよ!やーねー』

というお話です。

👉 Read this article in English.

createGraphics() でブラウザが固まる!?

createGraphics() の使い方に気をつけないとメモリが枯渇すると知ったのは、中山 哲法(@tetunori_lego)さんのツイートからでした。


調べてみると、p5.js の GitHub Issues でも取り上げられていた挙動のようです。

createGraphics() stays in memory, 1GB+ RAM after a while #1785

これを解決するには、createGraphics() で生成された p5.Graphics を不要になった時点で remove() することが必要です。


const pg = createGraphics(w, h);
pg.colorMode(RGB, 255);
pg.clear();
 .
 .
 .
image(pg, 0, 0);
pg.remove();

reference | remove() - P5.js

この問題を、createGraphics() を多く使っている JavaScript モジュール 'Garg' を例に解決させてみました。

後述の改良後のコードや diff が、この問題を具体的にどう解決するかの具体例になるかと思います。

※Garg とは、Generativemasks を生成する JavaScript モジュールです。

 

Garg 改良

中山さんは Garg でも同様の問題が起きることに気が付かれ、修正のパッチを提供してくださいました。あらためてお礼申し上げます。中山さん、ありがとうございました。

実際に修正前の Garg を使って大きなサイズのマスクを連続で生成してみると、落ちる落ちる!気持ち良いほどサックリとブラウザが落ちます。


function setup() {
    createCanvas(5000, 5000);
}

function draw() {
    clear();
    gg = new Garg(false, true, false);
    image(gg.createMask(0, 5000), 0, 0);
}

これに中山さんパッチを当てると、ウソのように全く落ちなくなり、動作も軽くなりました。

 

Garg だけでは解決しない

そもそも Garg は createGraphics() で生成した p5.Graphics を呼び元に返すものなので、Garg の修正だけでは問題が解決しません。

もし、Garg の呼び出し元で下記のような使い方をしているとメモリは開放されません。


image(gg.createMask(id, size), 0, 0);


createMask() で返される p5.Graphics を remove() するよう下記のように書くことでメモリが開放されます。


let mask = gg.createMask(id, size);
image(mask, 0, 0);
mask.remove();


つまり、修正版の Garg を使った上で、呼び出し元のコードにも修正が必要ということです。Garg の問題というより p5.Graphics の問題なので、これは仕方ないですね。

 

改良版の Garg 0.1a

修正を施して、Garg のバージョンは 0.1 から 0.1a になりました。「a」 は nAkAyAmA さんの「a」です!

改良版の Garg 0.1a はこちらに置きました。Garg の派生版を作られた方向けに diff も置いています。

Generativemasks を生成する JavaScript モジュールを作る

ブラウザによる?

これで万事うまく行くと思っていた矢先、手持ちの Firefox 94.0 64bit では修正版のコードでもメモリが開放されないことがわかりました。結果、Firefox では修正版でも落ちます。この問題は解決できていません。

Google Chrome 96.0.4664.93 64bit ではメモリは開放されており、挙動がブラウザによって異なるようです。

死のデモ

問題を含んだ旧版 Garg 0.1 と、中山さんご提供のパッチを施した Garg 0.1a の動作を比較するデモンストレーション「Garg the Big Eater」を作成しました。ブラウザを落としてみたい方は、『どうなっても知らない!あたしを無茶苦茶にして!』という覚悟のもとでお試しください。
※ Firefox では修正版でも落ちてしまいます。
※私が試した限りでは OS ごと落ちるということはありませんでしたが、もしそうなっても責任持ちません。

メモリの使用量を計れるもの(Windows だとタスクマネージャとか)を見ながら試すと面白いですよ。

 

 

結論:createGraphics() は remove() できちんと後始末

メモリの圧迫はアプリの大敵! ブラウザによっては完全な解決とはならないものの、大きなサイズの createGraphics() で生成した p5.Graphics を保持するようなコードを書くときは、適切に remove() でメモリを開放したほうがよいでしょう。

 

QooQ