GoはRubyよりも高速ですか?

ここ数年、私はソーシャルネットワーク用のゲームを作成しています。 バックエンドとして、Ruby + Sinatra + Redisを使用します。 Redisは唯一のデータベースとして使用されます。 単一のRedisデータベースのパフォーマンスでは十分でない場合が多いため、複数のデータベースのクラスターが使用されます。 この記事では、Redisデータベースのクラスターの形でソリューションがどのように作成されたかについて詳しく読むことができます。



最近、Goプログラミング言語は私にとって大きな関心を呼んでいます-その使用にはあまりにも多くの利点がプログラマに約束されています。 新しいゲームのバックエンドを作成したいのですが、既存のデバッグされたRubyコードベースがこれを防ぎます。



そこで、私は小さなイテレーションで移動することに決め、Goのゲームで使用されるマイクロサービスを書き直すことから始めました。





すべてのマイクロサービスには、Redisデータベースへの接続があります。 通常、ゲームがデータを保存する8つのRedisベースが使用されます。



Redisクエリの一部を含む文字列は、SHA-1を使用して16進数に変換されます。 次に、この数値を8で割った残りの部分があります。それに応じて、データは目的のデータベースに書き込まれます。



Rubyのオリジナルバージョン。Goで書き直すことにしました。

def node0(text) return Integer('0x'+Digest::SHA1.hexdigest(text))%8 end
      
      







残念ながらまたは幸いなことに、このGoの正面アプローチは機能しませんでした。 結果の16進数文字列が大きすぎました。 Int64はこの数値に適合しませんでした。



問題を解決する方法は?



8つの基数と16進数があり、そこから残りの8で割る必要があります。



答え。



頭に浮かぶ最初のもの。 整数を8で割った余りを計算するには、16進数の最後の文字を知るだけで十分です。



上記を考慮して、次のように、Rubyでソリューションを簡単に書き換えることができます。

 def node1(text) s = Digest::SHA1.hexdigest(text) return Integer('0x'+s[s.size-1, 1]) % 8 end
      
      







同じことですが、Goでも:

 func node1 (text string) int64 { h := sha1.New() h.Write([]byte(text)) sha1_hash := hex.EncodeToString(h.Sum(nil)) sha1_hash_len := len(sha1_hash) last := sha1_hash[sha1_hash_len-1:sha1_hash_len] value, _ := strconv.ParseInt(last, 16, 64) //int64 return value % 8 }
      
      







成功したことが判明した2番目のこと



上記のコードを作成する過程で、SHA-1の実行中にGoが数値を10進数のセットとして表し、16進数形式になることがわかりました。 除算の残りが必要なため、16進数の最後の文字の代わりに最後の10進数を取得し、ある数値システムから別の数値システムへの変換を節約できます。

 func node2 (text string) int64 { h := sha1.New() h.Write([]byte(text)) mas := h.Sum(nil) // "hello world" -> [42 174 108 53 201 79 207 180 21 219 233 95 64 139 156 233 30 232 70 237] return int64(mas[len(mas)-1]) % 8 //    .    .       8 }
      
      







このフェイントにより、必要なものをより迅速に計算できました。 しかし、Rubyでは、このようなトリックは失敗しました。 したがって、Rubyのnode1とGoのnode1のパフォーマンスを比較する方が正確です。



それで、提示されたすべての速度は何ですか?



テストする方法:



100万人のユーザーそれぞれに関連するデータをどのデータベースに配置するかを計算します。



Rubyの場合:

 for i in 1..1000000 do node1("user:"+i.to_s) end
      
      







および移動中:

 for i := 1; i <= 1000000; i++ { node1("user:"+string(i)) }
      
      







完全なコードはgithubで表示できます。



実験の純度のために、各スクリプトを10回実行しました。平均値を以下に示します。



コードは、バージョンRuby 1.8.7および2.1.3で実行されました。 Goバージョンは1.4.2でした



秒単位の値。 少ないほど良い。



Ruby 1.8.7

node0-22.32

node1-17.24

node11-16.81



Ruby 2.1.3

node0-15.46

node1-11.15

node11-11.05



ゴー1.4.2

node1-6.15

node2-4.36



なぜ古いバージョンのRubyなのでしょうか?



4〜5年前に多くのプロジェクトのコードを作成し始めました。 可能な限り、新しいバージョンのRubyに移植しました。 特に、2.1.3で。 Ruby 2.2.2の最新バージョンでは、結果は良くなるかもしれませんが、明らかに2回ではないことに同意します。



結論






All Articles