Carpe Diem

備忘録

Golangの良いところ

概要

Golangの良いところってなんだろう?と思ってまとめます。
多分新しいことを知ったら追記していきます。

よく言われるところ

コンパイルが速い

JavaC++に比べてかなり高速です。

メモリ安全

golangC言語に近いですが、C言語で問題になっていたメモリ周りは言語側でちゃんとカバーして使う側が意識する必要がなくなってます。

型安全性

LL言語に比べれば。

標準ライブラリの充実

外部ライブラリを使わずとも標準ライブラリでほぼ何でもできます。

学習コストが低く、可読性が高い

言語の仕様がシンプルなので、他の言語に比べてすぐに一通り理解できます。

gofmt

先程の可読性に関連しますが、フォーマッターがデフォルトで付いているので読みやすくなります。
タブかスペースかといった宗教論争をせずに済みます。

標準ツールの充実

  • pprofでプロファイリング
  • race detectorでgoroutineによるデータ競合を検知
  • テストコードで簡単にベンチマーク取れる

このように標準ツールが充実しているので開発効率が高いです。

シングルバイナリ

デプロイやツールの配布が簡単です。

深ぼってみる

言語仕様がシンプル

技術選定をする上で「Simple」か「Easy」かの軸はとても重要です。

www.publickey1.jp

golangはSimpleを重要視した言語です。なので他の言語のように色んな機能を持っていません。
しかしながらSimpleに保つことで可読性が上がり、途中からジョインしたエンジニアもすぐにキャッチアップできますし、能力が低いエンジニアであっても言語によるバグを回避できます。

golangのゴールはスケーラビリティにあります。Simpleを重視したgolangは前述したようにチーム開発に向き、開発のスケールがしやすい言語と言えます。

逆にEasyである言語は覚えることは多いものの、様々な手間を省くことが可能です。
個人開発に向いていると思いますし、もし同じレベルの人材が揃うのであればチーム開発の効率も非常に高いでしょう。揃えば。

軽量スレッドgoroutine

書き始めたら非常に長くなったので別記事にまとめました。

christina04.hatenablog.com

GCがシンプルで現実的

こちらの記事で非常に分かりやすく書かれています。

engineering.linecorp.com

その中でポイントとなる点をまとめます。

なぜ世代別GCを使わないのか

世代別GCは短命な(新世代)オブジェクトを頻繁にGCし、長寿命の方を残します。
ただし長寿命のオブジェクトが新世代オブジェクトを参照している可能性があります。
それをライトバリアと呼ばれる仕組みで防いでいますが、これに依るオーバーヘッドがかかります。

Goは短命なオブジェクトはヒープでなくスタックに保持するので、先程のライトバリアのオーバーヘッドを考慮するとメリットが少ないため世代別GCを使いません。

なぜコンパクションが無いのか

TCMallocをベースとしたメモリアロケーターを採用することで、断片化およびアロケーションの速度の問題を解決してる

と説明されているので調べてみると、TCMallocは

種類 説明
スレッドキャッシュ 各スレッドごとに用意されたキャッシュ。
32K 以下のオブジェクトはスレッドキャッシュ上に確保。
スレッドごとにメモリブロックを管理ことでロックなどのスレッド競合によるパフォーマンス劣化を防ぐ
中央ページヒープ 全スレッドによって共有されるヒープ。
32Kより大きいオブジェクトに利用。
1ページあたり4キロバイト単位で、255ページまでのサイズごとの空きメモリブロックのリストになっている。
256ページを超えるオブジェクトはサイズごとに分類されずに1つのリストになっている。

の2種類で構成されています。

スレッドキャッシュはサイズごとにクラス分けされ、以下の図でいうとサイズが 14 バイトのオブジェクトを確保する際にはクラス 1 の中で空きリストを探しそれを利用します。

f:id:quoll00:20181026065502j:plain

ref: Google Japan Blog: Google が公開しているソフトウェアの解説(その4)- Performance tools -

中央ページヒープもページサイズごとにフリーリストを持っています。
たとえば、999キロバイトのメモリが必要であれば、1000に丸めて250ページ長のリストを探しに行きます。
返却されている空きメモリがあればそれを使い、空きがなければ最低1メガバイト単位でOSからメモリをもらって必要なサイズに切り出して返します。

f:id:quoll00:20181026065640g:plain

ref: TCMalloc : Thread-Caching Malloc

このようにサイズ毎にクラス分けされていることで、その時必要なメモリに応じてそれを利用します。
これによって断片化を避けることが可能になり、コンパクションをしなくても済んでいます。

golangは上記のように世代別GCやコンパクションを利用しないシンプルな形を取っていることで、GC自体にかかるリソースも削減しています。
そしてGCは一般的に

のどちらを優先するかのトレードオフになりますが、golangではレイテンシを優先しており、スループットに関してはハードウェアが後々解決してくれるだろう、という方針をとっています。

ソース