JavaScript(TypeScript)で書かれたプログラムを、Go言語で書き直しました。
その動機や、書き換えた結果などを書こうと思います。
また、今回Go言語が初めてだったので、Go言語とはどういったものかや、Go言語をやってみた感想なども合わせて書こうと思います。
動機
メンテナンスしづらい
移植したプログラムは、JavaScript(TypeScript)で書かれた、データ処理関連のプログラムで、サーバーで動かしていました。
JavaScriptはポピューラーな言語ですが、フロントエンドをやらない人にはあまり馴染みがないようで、扱える人が限られていて何かあった時の対応が不安でした。
どのプログラム言語にも似たような文法があり、ちょこっと修正するくらいなら、一通り文法書に目を通せば何とかなりそうな気もするのですが、PromiseなどのJavaScriptの非同期プログラミング文法は、JavaScript固有の文法で、別途覚える必要があります。
しかも、JavaScriptはベースが非同期プログラミングなのでそれらは必須なのですが、せっかく使い方を覚えても他で活用できる場面がありません。
そういった理由もあって、他にJavaScriptを使う予定がないのに、そのためだけにJavaScriptを覚えてもらうのも何だかなぁと、もう少し扱いやすそうな言語で書きなおすことにしました。
そして、どうせ書き直すなら、新しく覚えるのなら、モチベーションの上がる何かしらのメリットがある言語がいいなと考えていました。
処理時間を短くしたい
あるあるな話ですが、プログラムは最初のころは処理時間は短かったのですが、段々扱うデータ量が増えてきて、かな~り待たされるようになってきました。
スケールアウトすれば処理時間が短くなる類のものだったのですが、処理をタスクに分けて、それらを統合する仕組みを作るのが億劫で…。
とりあえず横着して、PCのスペックを上げつつ、プログラム内容は今のまま、言語を変えることによって処理時間を短くしたいなと考えました。
言語選定
まずは「メンテがしやすく、使ってくれそうなもの」つまり、「簡単にセットアップと習得ができて、トレンド的に覚えて損はなさそうなもの」で考えて、PythonかGo言語にしようと考えました。
速度面は、Pythonは何とも言えないのですが、Go言語はコンパイル言語なので、今より確実に速くなることは期待できました。
また、恥ずかしながら自分はタイプミスが多く、動的型付け言語でランタイムエラーを起こしまくった経験から、 静的型付け言語のメリットは十分に感じていたので、大きめのプログラムを書くなら、静的型付け言語にしたいという思いもありました。
そういった理由から、Go言語で書き直すことにしました。
移植
ゼロからGo言語を学び始めて、結局かな~り四苦八苦しながら移植しました。
Go言語にも非同期処理(並行処理)の文法があるので、非同期プログラムを移植するのは問題なかったのですが、後述の通り、Go言語の文法まわりで結構四苦八苦しました。
結果
速くなりました!
条件によって変動があり、一概には言えないのですが、大体処理時間が30~60%くらい短くなりました。
特に面白いのが、PCのスペックが高いほど、目に見えてその差が大きくなることでした。
これは、Go言語で並行処理していると、CPUのコア数が増えた時に、それぞれに処理が割り当てられ、その恩恵がフルに受けられるのに対し、 JavaScriptはシングルスレッドで動くため、CPUのコア数が増えても、その恩恵があまり受けられないことに起因しているようです。
逆に、スペックの低いPCで、並行処理なしのCPU演算の少ない簡単なプログラムで比較すると、処理時間の短縮は10~20%ほどになり、個人的には思った程速くないなと感じました。
ベンチマークプログラムと違って、実務プログラムは演算処理よりもI/Oや通信時間が大きなウエイトを占めるので、いくらGo言語が速いといっても、力を発揮できる箇所が少ないとそうなるのは致し方ないですね。
Go言語のメリットを最大限に活かすには?
とはいえ、やはりGo言語の魅力はその速さだと思います。
Go言語が速いのは、主に下記によるものです。
- ネイティブプログラム
- 並行処理
ですので、ゴリゴリ演算するプログラムほど、また、ゴリゴリ並行処理するプログラムほどGo言語に向いています。
そして、そういったプログラムを実行するPCの、マシンパワーを上げれば上げるほど、Go言語はその期待に応えて速く動いてくれます。
逆に、マイクロサービスのような、「簡単だけど大量にある処理を、低いスペックのインスタンスで、数を使ってこなす」といった用途においては、 処理速度が早いという理由でGo言語を使うメリットは、期待した程はないかなぁと、個人的には思いました。
もちろん、スクリプトプログラムより確実に速いので、言語の制約がないのなら、はじめからGo言語にしておけば、速度的には間違いはないです。
以上が、JavaScriptからGo言語に乗り換えた動機と結果です。
以下、今回Go言語が初めてだったので、Go言語について色々感じたことを徒然なるままに書こうと思います。
Go言語ってどういう言語?
一言で言えば
「現代版C言語」
でした。
GoogleがGo言語を開発した動機が下記に記載されているのですが、まさにそのまんまです。
- 速いプログラムを書きたい
- 楽に書きたい
- 並行処理したい
速いプログラムを書くには、ネイティブプログラムを書けばいいのですが、C言語の設定・扱いは面倒だし、機能がプリミティブ過ぎてコーディングが大変です。
そこで、面倒なことは隠蔽し、今時の言語風にC言語の文法を改良して、プログラマがロジックに専念してネイティブプログラムを書けるようにしたのが、Go言語といった感じでした。
学習コスト
当初の予想に反して、正直高いと感じました。
C言語系の言語なので、メモリ・ポインタ・データ構造を理解して、常にそれらを意識しながらプログラミングする必要があるので、本質的に習得が難しい言語の部類に入ると思います。
とは言え、ポインタ演算が無いので、C言語のようなポインタを駆使したプログラミングはなく、実態とポインタ(参照)の違いを知っている程度で十分で、あくまでポインタが無い言語と比較して難しいという度合いです。
また、シンプルに書けるよう、Go言語独特の書き方がいくつかあるのですが、ここで結構ハマりました。
そして、文法習得とは別に、並行処理のデザインパターンの習得も必要で、これはこれである程度覚えることがあります。
書きやすさ
難しいと相反するようですが、コードは書きやすく、そして、意図的に書きやすくなるように作られています。
面倒な環境構築やボイラープレートがなく、プログラムでやりたいことだけを書けるように作られています。
また、よく書く処理には、それを簡潔に書ける書き方が用意されていたりします。
メモリや並行処理など、本質的に難しいことを扱う必要があるが故に、プログラマはそれ以外のことに労力を割かなくて済むように作られていて、プログラム内容に集中することができます。
並行処理
個人的にはGo言語の最大の特徴だと思っているのですが、言語レベルで並行処理の機能が備わっています。
昨今CPUのマルチコアは当たり前で、それを活かすには、処理をスレッドに分けて実行するなど、ハードウェアを扱うプログラムを書く必要があります。
しかしGo言語は並行処理が抽象化されていて、並行実行したい処理を指定するだけで、Go言語が裏でよしなにスレッドに自動でスケジューリングしてくれるので、プログラマはハードウェアを意識することなく、 知らずとマルチコアを使った並行処理プログラムを書くことができます。
用途
Go言語はネイティブプログラムが書けますが、本当にネイティブな処理をするための言語ではないようです。
どういうことかと言うと、Go言語を使って、システムプログラムや、GUIプログラムを書くことはあまり想定されていないようです。(書けなくはないですが)
Go言語には予めよく使うパッケージが揃っていて、大抵のことはそれを使えばできるようになっているのですが、パッケージは主にデータ処理や通信周りが中心で、 サーバーサイドでデータ処理を行うことをメインターゲットとした言語かなぁと感じます。(正にGoogleが作った言語って感じです)
データ処理であれば、わざわざクラスを使うこともあまりないので、Go言語がC++ではなく、あえてC言語をベースにしているもの納得です。
また、ネイティブプログラムが作れるので、コマンドラインツールの作成にも向いていますね。
生産性
Go言語は
- 新しいプログラムをすぐに作れる
- プログラムにはやりたいロジックだけを書けばいい
- よく書く処理は簡潔に書ける
- よく使う処理はパッケージでそろっている
なので、一般的なデータ処理プログラムを書くのであれば、生産性は高いと思います。
つまり、スクリプト言語の手軽さで、コンパイルプログラムが書けるのです。
Go言語の気になったところ
コードが長い
めちゃめちゃ主観ですが、コードが長く、そのため読みづらいです。
Go言語では、関数を呼び出した後、必ず戻り値でエラーチェックをするのが慣習となっています。
関数の呼び出し毎に、エラーチェックのif文の3行が必ずつくので、結果として全体のコードが長くなりがちです。
並行処理のチューニングが面倒
並行処理は自動でやってくれるのですが、いくつ並行処理させるかや、処理待ちの時のバッファサイズは自分で調整する必要があります。
PCスペックやデータ量、データ頻度が様々に変わる中で、いい調整値を見つけるのは中々面倒ですし、そもそも最適解などないのかも知れません…。
まぁ、他の言語だと、並行処理させるだけで一苦労なので、その先の苦労とは、Go言語ならではの贅沢な悩みとも言えますね。
感想など
今回、用途がサーバーサイドの、大量のファイルを並行処理するプログラムだったので、Go言語の長所にマッチして、期待通り速くなって大成功でした!!
これからもデータ処理プログラムは、Go言語で書いていこうと思います!
しかし、ちょっとしたプログラムも、Go言語で書くかと言われると考えてしまいます。
それは、Go言語はスクリプト言語の手軽さで書けるには書けるのですが、やはりしっかりとした言語で、雰囲気では書けないからです。
このあたりは実際どうなのか、ツールプログラムをいくつか書いてみて確かめてみようと思います。
今回Go言語を学ぶにあたって、Go言語の書籍には書かれていない、Go言語特有の書き方や、難しくならないようあえて詳しく説明されていないことがあって、そこでめちゃめちゃハマりました。
皆同じところでハマりそうなので、参考までに、そのあたりを記事として残しておきました。