Node v8.xのHTTP/2を試す

Node v8.8.0がリリースされたが、まだLTSにならない。どうやらv8.x LTSは10/31リリース目標でまだお預けらしい。

気を取り直してv8.8.0のchange logを眺めると、今回のリリースからHTTP/2モジュールがデフォルトで有効になったようだ。実験的実装という位置付けは変わらないようだが、とりあえず試してみよう。

まずサーバの鍵と証明書を作る。無論自己署名のオレオレ証明書。HTTP/2は仕様上、非TLSでの通信も可能であるようだが、各ブラウザベンダは非TLS版を実装しない方針のため事実上TLSが必須らしい。

$ openssl genrsa 2048 > server.key
$ openssl req -new -key server.key > server.csr
$ openssl x509 -req -signkey server.key < server.csr > server.crt

nodeのコードを書く。ほぼドキュメントのサンプルそのまま。サーバ作成をcreateSecureServerに変えただけ。

const http2 = require('http2');
const fs = require('fs')

const server = http2.createSecureServer({
    key: fs.readFileSync('./server.key'),
    cert: fs.readFileSync('./server.crt')
});

server.on('stream', (stream, headers) => {
  stream.respond({
    'content-type': 'text/html',
    ':status': 200
  });
  stream.end('<h1>Hello World</h1>');
});

server.listen(8888);

で、ブラウザで https://localhost:8888/ へ。オレオレ証明書なので安全ではないと言われるが、テスト用なので例外追加してアクセス。デベロッパツールで確認

f:id:paulownia:20171025140444p:plain

動いた。とても簡単。

サンプルのコードを見るとストリームが双方向になっているようで、サーバからのpushも簡単にできそうな感じ。

golangのappendの速度比較

appendって毎回新しいスライスが作られているような感じに見えるので、なんか遅そうなイメージがあるんすよね。まぁ、実際には内部で参照している配列のキャパシティに余裕があれば空きに追加されるだけなのでキャパシティさえあれば高速なのだが。

では、実際にキャパシティの有無でどのくらい差が出るのか?

キャパシティ未指定の場合

const x = 200000000

func main() {
    hoge := []int{}

    for i := 0; i < x; i++ {
        hoge = append(hoge, i)
    }

    fmt.Println(hoge[0] + hoge[x-1])
}
$ time go run 1.go
199999999
go run 1.go  5.87s user 5.83s system 139% cpu 8.393 total

キャパシティを指定した場合

const x = 200000000

func main() {
    hoge := make([]int, 0, x)

    for i := 0; i < x; i++ {
        hoge = append(hoge, i)
    }

    fmt.Println(hoge[0] + hoge[x-1])
}
$ time go run 2.go
199999999
go run 2.go  0.78s user 0.71s system 135% cpu 1.104 total

7〜8倍程度の高速化。

動的言語ではキャパシティが不足したら現在の倍のキャパシティを確保という感じのアルゴリズムになっているため、こんなに差が付かないのだが、Goはそういう余計なことはしないらしい。GCありの言語のくせにこういう所では低水準言語感を示してくるなこやつw

インデックスアクセスで値をセット

const x = 200000000

func main() {
    hoge := make([]int, x)

    for i := 0; i < x; i++ {
        hoge[i] = i
    }

    fmt.Println(hoge[0] + hoge[x-1])
}
$ time go run 3.go
199999999
go run 3.go  0.81s user 0.80s system 137% cpu 1.170 total

容量確保してappendとほぼ同じ速度。

まぁ、あえてインデックスアクセスで書く理由もないのでキャパシティ確保した上でappendする方がいいと思います。