JavaScript のメモリや GC を扱った記事のメモ

JavaScript のメモリ や GC を意識したプログラミングに関する記事をふたつばかり読んだので、個人用のメモ。

www.html5rocks.com

  • V8 が minor GC を実行するときは 10ms オーダーの時間がかかる。
  • 60fps を実現する場合の 1フレームは 16ms であるから、GC が頻発すると全体の fps が低下し、表示のかくつきや操作の違和感に繋がる。
  • GC が発生するのは大量のオブジェクトの生成・破棄を繰り返したときだから、事前にオブジェクトを生成しておいてそれを使い回せばいい。(オブジェクトプール)
  • 万能ではない。初期化に時間がかかるし、大してオブジェクトを作らない場合でもメモリを無駄に使用することになる。また、その無駄なメモリを解放するタイミングが難しい。
  • ( モバイルの場合は、大量のメモリの使用がバッテリーの著しい消費に繋がるから、滑らかな UI を実現することとのトレードオフな気がする )
  • (どこで聞いたか忘れたけども、React は生成したインスタンスをプールするutil関数を定義している react/PooledClass.js at 19b8eadb242054e2668cf90e83490ef552bcdbfe · facebook/react · GitHub )
  • ( 話がズレるが、↑のコードで引数の個数毎に関数を定義しているのは apply よりも call のほうがパフォーマンスが良いため )

www.html5rocks.com

  • V8 の GC の説明。世代別 GC、minor GCアルゴリズムはコピー GC で、major GCアルゴリズムはマークコンパクト GC
  • Gmail のパフォーマンス改善のために performance.memory API で情報の収集をおこなった。
  • Chrome DevTools でメモリリークの原因を特定できる。
  • 先の記事と同様で、GC の頻発は避けましょう。
  • ( ページを何日も開き続けるサービスに関わったことがないのだけど、1~2時間開き続けるサービスは心当たりがあるので、メモリの使用量を調べてみるとよさそう )

彼女と彼女の猫 を読んだ

漫画のほうです。

吾輩は猫である のOL版といったところ。日常で漠然と感じる寂しさや希望みたいなものの描き方が秀逸だった。
猫が飼いたくなる。

彼女と彼女の猫 (アフタヌーンコミックス)

彼女と彼女の猫 (アフタヌーンコミックス)

ThinkPad のキーボードを買った

買った。

キーボードが欲しいなぁ、と前から思っていて、休みの日にお店に行って色々なキーボードを触ってみた。 ThinkPadの赤ポチの付いたキーボードが個人的に打ってて気分が良かったので購入。

レノボ・ジャパン ThinkPad USB トラックポイントキーボード(日本語) 55Y9024

レノボ・ジャパン ThinkPad USB トラックポイントキーボード(日本語) 55Y9024

赤ポチに慣れるのにちょっと時間がかかったけれども、ホームポジションから手を動かさなくても良いので段々楽になりつつある(ような気がする)。
スクロールは、Karabinar(旧KeyRemap4MacBook)で赤ポチ+中ボタンで出来るように設定できる。下の記事を参考に設定をおこなった。

qiita.com

君の名は。を観た

今日、前職の同期に誘われて、二子玉川の109シネマズで観てきた。

(ネタバレしているつもりはないんですが、なんとなく内容を察することができるので、そういうのが嫌な人は読まないほうがよいです)

はじまって、なんだただのよくあるボーイミーツガールか、と思って斜に構えて観ていたら、途中から突然、氷水をぶっかけられるような体験を間髪を入れずに何度も強いられた。最高だった。

映画が始まる前に「近頃、年をとったからか、若年性痴呆症か、昔、観た映画を誰と見に行ったか、レストランに誰と行ったか、思い出せなくなる。日記でも書こうかな」という世間話をしていたのだけど、何の偶然か、この映画で扱っているテーマと重なる部分があって、背筋が凍る思いがした次第です。

世界のドキュメンタリー カストロVSゲバラ を見た

NHKオンデマンドにあったので、見た。

自分のカストロ(兄)とゲバラの関係のイメージは学生の頃に見たスティーブン・ソダーバーグ監督の映画のそれだったのだが、そのイメージがぶち壊されるドキュメンタリーだった。

www.nhk-ondemand.jp

キューバ革命からゲバラの死までを、当時の映像や音声、作家やかつてのゲリラ兵のインタビューをまじえながら描く。

カストロの立ち回り方が凄まじく、革命中・直後は「私は共産主義者ではありません、決して!」と言っておきながら、アメリカとの関係がまずくなってソ連と組むしかなくなったら「私はマルクス・レーニン主義者です」とか言ってて、(経済の破綻を避けるにはそうするしかなかったのは分かるが)何だこいつ、という気持ちになった。
普通の指導者ならこんなことを繰り返せば何らかの形で引きずり降ろされると思うのだが、それでも長いこと権力の椅子に座り、今日まで生きながらえているということは、彼のカリスマ・老獪さ・人心掌握の能力は本物なのだろうと思う。

ゲバラは、高潔さ・理想の高さ・順従さをカストロに良いように利用され、用済みになったら追放・半ば見殺しという気の毒な感じだった。ゲバラは、メディアでなにかと神聖化して描かれることが多いが、このドキュメンタリーを見ると中庸なポジションが取れるようになってよいかもしれない(とはいえ、このドキュメンタリーも特定の側面だけを際立てて見せているだけなのかもしれないが)。

チェ ダブルパック (「28歳の革命」&「39歳別れの手紙」) [DVD]

チェ ダブルパック (「28歳の革命」&「39歳別れの手紙」) [DVD]

WebPagetestのAPIを呼んで、結果を保存するスクリプトを書いた

WebPagetestが提供しているREST APIを定期的に呼んで、結果をMongoDBに保存したくなって、スクリプトを書いた。

webpagetest-api を使おうとしたときのメモ - kitak.blogでは、npm packageでインストールしたコマンドからAPIを呼び出したが、職場的にスクリプト言語Pythonが使われることが多いような気がするので、Pythonスクリプトで。
先の記事にも書いたが、WebPagetest - Get API Key で事前にAPIキーを発行しておく。

import os
import time
import requests
from pymongo import MongoClient

client = MongoClient('localhost:27017')
db = client.webpagetest
API_KEY = os.environ["API_KEY"]
target_url = os.environ["TARGET_URL"]
params = {
    "url": target_url,
    "k": API_KEY,
    "f": "json",
    "location": "",
    "mobile": 1,
    "mobileDevice": "Nexus5",
    "video": 1
}
r = requests.get("https://www.webpagetest.org/runtest.php", params=params)
response_json = r.json()

test_id = response_json["data"]["testId"]
result_url = response_json["data"]["jsonUrl"]
print(test_id)
print(result_url)

while True:
    time.sleep(10)
    params = {
        "f": "json",
        "test": test_id
    }
    r = requests.get("https://www.webpagetest.org/testStatus.php", params=params)
    response_json = r.json()
    status_code = int(response_json["statusCode"])
    if status_code == 200:
        r = requests.get(result_url)
        response_json = r.json()
        result = {}
        result['id'] = response_json['data']['id']
        result['summary'] = response_json['data']['summary']
        result['location'] = response_json['data']['location']
        result['from'] = response_json['data']['from']
        result['completed'] = response_json['data']['completed']
        result['average'] = response_json['data']['average']
        db.results.insert_one(result)
        break
    elif status_code >= 400:
        print("ERROR " + target_url)
        break

スクリプトでやっていることは、テストを走らせるAPIを呼び出して、テストが完了したか調べるAPIを10秒毎に呼び出し、テストが完了したら結果から関心のある項目だけ取り出して、MongoDBに格納する、それだけ。 MongoDBを使うと怖い人が来そうな気がするのだが、テスト結果の項目が色々あって、そこから取捨選択したり、構造を変更することを考えるとスキーマレスでカジュアルにバンバン突っ込めるほうが楽。

後はこれを毎日実行して、1~3ヶ月のスパンで数値の変化をみていこうと思っているのだけど、可視化をどうするかが悩ましい。TTFBとかSpeed Indexの推移をみるだけだったら、matplotlabとかでグラフ描けばいいかなぁ...

後、話がズレるのだけど、Pythonスクリプトを書いていて、dictから一部の項目だけ取り出して別のdictを作るのもっと楽に書けないかなぁ、と思った。

JavaScriptでfpsを計測する

Chrome DevToolsにFPS Meterというのがあって、FPSの推移をプロットしてくれるのだけど、他のブラウザや環境によってはプロットできない、あるいは、FPSの値だけ欲しくてプロットは自分でやりたいということもあるので、簡単に計測用のコードを書いてみた。こんなかんじ。

    class FpsCalculator {
      constructor() {
        this._isRunning = false;
        this._beginTime = Date.now();
        this._prevTime = this._beginTime;
        this._frames = 0;
      }
      start() {
        if (this._isRunning) {
          return null;
        }

        this._beginTime = Date.now();
        this._prevTime = this._beginTime;
        this._frames = 0;

        this._isRunning = true;
        const loop = () => {
          if (!this._isRunning) {
            return null;
          }
          this._update();
          requestAnimationFrame(loop);
        }
        loop();
      }
      stop() {
        this._isRunning = false;
        this._frames = 0;
      }
      _update() {
        this._frames++;
        let prevTime = this._prevTime;
        let time = Date.now();

        if (time > prevTime + 1000) {
          console.log((this._frames * 1000) / (time - prevTime));
          this._prevTime = time;
          this._frames = 0;
        }

        this._beginTime = time;
      }
    }
    const calculator = new FpsCalculator();
    calculator.start();

requestAnimationFrameで描画処理の前に呼び出したい関数を登録できる。今回は、描画したフレームの回数をカウントアップして、前回の結果の出力から1秒経過していれば新しい結果を出力するようにしている。描画処理は、理想的には毎秒約60回おこなわれるので(60fps)、requestAnimationFrameに登録し続けている関数も約60回呼ばれて、60に近い数値がConsoleに出力される。JavaScriptでヘビィな処理をおこなったり、リフロー/レイアウトが頻繁に走ったりすると、この回数が少なくなる(fpsが低下する)。

極端なfpsの低下は表示のカクツキ(ジャンク)を引き起こし、ユーザーに違和感を与えるので、原因を特定してそれを取り除く。