Hack Your Design!

RubyとGolang で並行処理のパフォーマンス比較してみた

Rubyで書いたコードがGoで書いたらどれくらい早くなるかを検証したくてやってみた。画像ファイルをダウンロードするだけの単純な処理での比較。(複雑な処理になるとまた全然違ってくると思います)

Rubyの場合

イメージを100個ダウンロードするコードをサンプルにやってみました。

直列にダウンロード

何も考えず1つ1つダウンロードするコード。

require 'open-uri'

(1..100).each do |i|
  open("./images/#{i}.png", 'wb') do |f|
   f.write open("http://dummyimage.com/600x400").read
  end
end
$ time ruby no_thread.rb
ruby no_thread.rb  0.25s user 0.10s system 1% cpu 29.813 total

約30秒。遅い。そしてCPUコストは低い。

Threadで並行ダウンロード

Thread使って並行に処理してみる。

require 'open-uri'

thr = []
(1..100).each do |i|
  thr << Thread.new do
    open("./images/#{i}.png", 'wb') do |f|
      f.write open("http://dummyimage.com/600x400").read
    end
  end
end
thr.map(&:join)
$ time ruby thread.rb
ruby thread.rb  0.20s user 0.10s system 27% cpu 1.105 total

1.1秒。劇的な改善。CPUは30%くらい使ってる。

Goの場合

Goで書きなおしてみる。

こちらを参考に書いてみた。

package main

import (
	"fmt"
	"io"
	"net/http"
	"os"
	"sync"
)

func main() {
	var wg sync.WaitGroup

	for i := 0; i < 100; i++ {
		wg.Add(1)
		var url string = "http://dummyimage.com/600x400"

		//ファイルを開く
		file, err := os.Create("images/" + fmt.Sprint(i) + ".png")
		if err != nil {
			panic(err)
		}

		go func() {
			response, err := http.Get(url)
			if err != nil {
				panic(err)
			}
			//レスポンスのボディを閉じる関数の遅延実行指定
			defer response.Body.Close()

			//fmt.Println("status:", response.Status)

			//ファイルを閉じる関数の遅延実行指定
			defer file.Close()

			//レスポンスのボディから読み込みつつファイルに書き出す。
			io.Copy(file, response.Body)
			wg.Done()
		}()
	}

	wg.Wait()
}

Go run で実行

コンパイルせずにgo runで実行してみる。

$ time go run sample.go
go run sample.go  0.50s user 0.21s system 50% cpu 1.413 total

1.4秒。Thread使ったRubyのコードと同じくらい。

Go build :arrow_right: Run

buildして実行する。

$ go build sample.go
$ time ./sample
./sample  0.06s user 0.07s system 16% cpu 0.789 total

約0.8秒。Rubyで書いたコードの3/4の時間で完了した。

今回は単純な処理なのでRubyとGoの間で大きな差は生まれなかったものの、比較するロジックが複雑化すればするほどGoのパフォーマンスの良さがガッツリと効いてきそう。CPUコストがRubyより半分程度に済んでいるところもナイス。


【番外編】Shell Script で実行

for i in `seq 1 100`; do
  wget --background --quiet "http://dummyimage.com/600x400" -O images/$i.png > /dev/null
done
$ time bash bash_script.bash
bash bash_script.bash  0.15s user 0.24s system 72% cpu 0.547 total

約0.5秒。はやい。 :astonished:

CPUも超使ってる。

  • このエントリーをはてなブックマークに追加
comments powered by Disqus