みんなもつかってね!
…なーんてプログラム使う人居ないと思うので、書いてた時に学んだことを共有しようかなというお話。
■コンソール出力すると極端に遅くなる
golangが早いのはご存知なことだと思うのですが、思ったよりコンソール出力が遅いです。
超大量データを処理する際、毎行ログ出力するのは無駄が多いです。
100行に一回出力とか間引きした方がいいですよ、ということですね。
■Network I/Oはコネクションプーリング等の工夫が必要
処理が早いということで、CPU以外の部分にもボトルネックが来る可能性があります。
redisへのアクセスを毎回切断していたら、あっという間にTCP接続を使い切ってしまいました。
コネクションを使いまわす、を早い段階で考えたほうがいいですね。
■ifでgoroutineを呼び出すときに、ループ数が極端に多いと落ちる
goroutineはループに入る前にスレッド(みたいなもの)を事前に用意するっぽいです。10万ループとかをgoroutineで非同期化すると、「signal: killed」で落ちます。
…ということでどうするかという話なのですが、「gorc」いうパッケージを使うと回避できます。goroutineの同時並列実行数に制限がかけられるため、リソースを使い切るのが回避できます。すごい。
※追記)sync.Poolとかsync.Cond使うとsyncでもスロットル制御出来る気がします!書いたこと無いけど!
package main import ( "crypto/md5" "crypto/rand" "encoding/hex" "flag" "fmt" "github.com/garyburd/redigo/redis" "github.com/mr51m0n/gorc" "math/big" "runtime" "strconv" "time" ) var ( pool *redis.Pool host string = "localhost:6379" // redis-server接続情報 key int = 150000 // 投入するKEYの数 val int = 10000 // 文字列の長さ ) // redis ConnectionPooling func newPool(server string) *redis.Pool { return &redis.Pool{ MaxIdle: 3, IdleTimeout: 240 * time.Second, Dial: func() (redis.Conn, error) { c, err := redis.Dial("tcp", server) if err != nil { return nil, err } return c, err }, TestOnBorrow: func(c redis.Conn, t time.Time) error { _, err := c.Do("PING") return err }, } } // val用にrandomな文字列を生成する func random(length int) string { const base = 36 size := big.NewInt(base) n := make([]byte, length) for i, _ := range n { c, _ := rand.Int(rand.Reader, size) n[i] = strconv.FormatInt(c.Int64(), base)[0] } return string(n) } // gorc var gorc0 gorc.Gorc // メイン処理 func main() { runtime.GOMAXPROCS(runtime.NumCPU()) flag.Parse() // Timer Set start := time.Now() // redis Pooling Start pool = newPool(host) // Redis DataInsert for i := 0; i < key; i++ { gorc0.Inc() go setredis(i, val) gorc0.WaitLow(100) } // Log Print fmt.Print(key) fmt.Printf("Keys - Set Done.\n") // Timer Print end := time.Now() fmt.Printf("%f秒\n", (end.Sub(start)).Seconds()) } // stringからmd5から作る func GetMD5Hash(text string) string { hash := md5.Sum([]byte(text)) return hex.EncodeToString(hash[:]) } func init() { gorc0.Init() } // redisにデータを投入するだけの処理 func setredis(k, v int) { c := pool.Get() defer c.Close() defer gorc0.Dec() vals := random(v) keys := GetMD5Hash(vals) c.Do("SET", keys, vals) // 100処理ごとにログ表示(非同期なので順番が適当) if k%100 == 0 { fmt.Print(k) fmt.Printf("Keys - Set Done.\n") } }
0 件のコメント:
コメントを投稿