17
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

グレンジAdvent Calendar 2018

Day 23

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

Last updated at Posted at 2018-12-22

グレンジ Advent Calendar 2018 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

17
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
17
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?