【半端ないって】PHPerが叫ぶGoの処理速度半端ないって

[グレンジ Advent Calendar 2018](https://qiita.com/advent-calendar/2018/grenge) 23日目の記事を担当しますkitaji_ngzkと申します。

昨年度はデータマイニングに従事していてその際使っていたTableauについての記事を書きました。
↓↓ちなみにコチラ↓↓
https://qiita.com/kitaji_ngzk/items/6ecdd69f720a9aa033df

今年度はサーバーサイドの業務に従事していて、そこではPHPを扱っております。

さて、今回私が紹介するのは
PHPerが叫ぶGoの処理速度半端ないって
ということで、
GoとPHPを超簡単に比較してみた実験報告をさせていただきまっす！！

というのは最近よくGoを取り扱う企業さんやプロジェクトの発表や報告を目にするなと思っており・・・というよくある流れでGoを知り、***速い！***とか***標準ライブラリが豊富！***といった良い情報を耳にするので、実際に比較実験してみました！

ただ、ぺーぺーな自分は高テクニックなかっこいいコードなど書けませんorz

ので、超簡単に「sampleという文字列に数字をつけたしていくのをforでぐるぐる10万回回して配列に入れていくという処理を3回繰り返す」をPHPとGoでそれぞれ作り、メモリ使用量と処理時間を比較したいと思います。

# 使用環境
macOS Mojave v10.14.1
PHP v7.1.19
Go v1.9.4

# PHP
PHPでの実装は以下になります

```sample.php
// メモリ使用量と開始時間取得
$baseMemoryUsage = memory_get_usage();
$baseTime = microtime(true);

// 将来的に3つのテーブルに10万レコードのテストデータを入れることを想定
$maxTableCount = 3;
$maxRecord = 100000;

$userList = [];
for ($k = 1; $k <= $maxTableCount ; $k++) { 
	for ($i = 1; $i <= $maxRecord; $i++) {
		$userList[] = 'sample' . sprintf('%06d', $i);
	}
}

printf(count($userList) . "\n");

// 処理後のメモリ使用のピーク量と処理にかかった時間取得
$memoryUsage = (memory_get_peak_usage() - $baseMemoryUsage) / (1024 * 1024);
$processTime = microtime(true) - $baseTime;

printf("Memory Usage : %.3f [MB]\n", $memoryUsage);
printf("Process Time : %f [ms]\n", $processTime * 1000);
```

# Go
Goでの実装は以下になります

```sample.go
package main

import (
	"fmt"
	"runtime"
	"time"
)

// ステータス定義
type status struct {
	memory uint64
	time   time.Time
}

var mem runtime.MemStats

// メモリ使用量と時間を取得
func (s *status) GetStatus() *status {
	s.time = time.Now()
	runtime.ReadMemStats(&mem)
	s.memory = mem.Alloc
	return s
}

// ステータス初期化
func NewStatus() status {
	return status{}
}

const (
	maxTableCount = 3
	maxRecord     = 100000
)

func main() {
	// メモリ使用量と時間取得
	status := NewStatus()
	status.GetStatus()
	memoryStart := status.memory
	timeStart := status.time

	var userList []string
	for k := 1; k <= maxTableCount; k++ {
		for i := 1; i <= maxRecord; i++ {
			userList = append(userList, fmt.Sprintln("sample" + fmt.Sprintf("%06d", i)))
		}
	}

	fmt.Println(len(userList))

	// メモリ使用量と時間取得
	status.GetStatus()
	memoryEnd := status.memory
	timeEnd := status.time

	fmt.Printf("Memory Usage : %f [MB]\n", float32(memoryEnd - memoryStart) / (1024 * 1024))
	fmt.Printf("Process Time : %f [ms]\n", (timeEnd.Sub(timeStart)) . Seconds())

}
```

# 結果

```
$ php sample.php
300000
Memory Usage : 27.448 [MB]
Process Time : 52.204847 [ms]
```
```
$ go run sample.go
300000
Memory Usage : 10.546051 [MB]
Process Time : 0.187551 [ms]
```

スピードは**圧倒的**でした。びびるレベルで速い。

調べてみると大きく起因してるのはPHPが**インタプリタ言語**、Goが**コンパイル言語**ってことっぽいです。
いわゆる処理実行のたびに解析するか、先に解析させてまとめて処理させるかってとこで大きく差が出るみたいですね。

割り当てられたメモリの量も約1/3！こちらも半端ないっすね汗
いいことずくめじゃないですか٩(๑•̀ω•́๑)۶
ただ実際内部で配列の持ち方が違ったりとかあると思うので、一概にイーブンで比較してるわけではないかもですが、
ここまで差が開くのはすごいなと思いました！


# 所感
やはり自分で体感してみて、Goは速いに尽きました。
自分はソーシャルゲーム開発に携わっている身ですが、ゲームプロダクトでGoを採用しているのはまだ聞いたことがないです。でも、活かせる部分はあるのではないかとは思いました。
特にリアルタイム同期やレスポンス速度重視な処理などでは活躍できるんじゃないかなと思うので、検証する余地はありそうです。

あとはGoはめちゃめちゃ書きやすかったです（もちろん最初のお作法のお勉強は苦しみました。。）
昨今TypeScryptと肩を並べてブームとなってきてる理由がわかるような気がしました

これからもちょこちょこGoをかじってみたいと思います！

# ちなみに
**やっている中でPHPでミスってる部分に気づかず、ちゃんと期待値通りの結果にならなかった時に思ったこと**

PHPで```$maxTableCount```と書いている変数名、最初は```$maxTable```だけでCountをつけていませんでした（Goの方でmaxTableCountと書いていた別の日の自分がいたので、後日合わせた）

ですが、forの方の```$maxTable```を変えておらず、正常処理になってなかったのですが、普通に時間とメモリ量が出てきて、goよりも圧倒的に速く終わり、「え！なぜ？？？」というしょうもない調査時間がかかってしまいました。

その時に思ったことが以下。
①***TDDって大事だな〜***
②***変数名をいったん適当にしてしまうことほど愚かなことないな〜***

でした。これは本業に活かしていきます！！(●´ω｀●)ゞ

ちなみにGoで同じようなミスをすると、、、

```
$ go run sample_1.go
# command-line-arguments
./sample_1.go:42:19: undefined: maxTable
```
なんとどこでどう間違ってるかを教えてくれちゃいます！！（これはとっても助かった！）
しかも、エラったところで止まるのではなく、コード全部検証して全部エラー出してくれます。
最初なんてこんな感じでしたw

```
$ go run sample.go
# command-line-arguments
./sample.go:21:2: too many arguments to return
	have (*status)
	want ()
./sample.go:31:11: undefined: GetStatus
./sample.go:33:23: status.Alloc undefined (type status has no field or method Alloc)
./sample.go:34:11: undefined: GetStatus
./sample.go:35:23: status.Alloc undefined (type status has no field or method Alloc)
```
テストを書かずして、ここまでエラー吐いてくれるのはすごく助かるっ！好き！ってなりました。

以上です！

# 参考
runtime.MemStatsでメモリ使用量を調べる&pprofモジュール
http://imagawa.hatenadiary.jp/entry/2016/12/22/190000

Goで時刻を取り扱う
https://qiita.com/taizo/items/acbee530bd33c803dab4

golangでかかった処理時間を計算するには？
https://kwmt27.net/index.php/2012/07/06/golangでかかった処理時間を計算するには/

PHPとGoって何が違うの？LIGが自社サービス開発にGo言語を採用したお話
https://liginc.co.jp/284306

Benchmarks: Node.js vs Go (vs PHP)
https://jaxbot.me/articles/benchmarks_nodejs_vs_go_vs_php_3_14_2013