1. ホーム
  2. スクリプト・コラム
  3. ゴラン

golangにおける効率的なコーディングの詳細

2022-02-14 02:20:16

xdm、我々はgolangが本質的に高度に並列化され、効率的なコンパイル言語であることを知っています。

しかし、優れたツールも正しく使わなければ意味がないことも知っています。そこで、よくある2つの経路をたどって、その感触を確かめてみましょう

構造体と地図はどちらを使いますか?

計算量が少ないうちは、一時構造体とマップの使い分けがわからないかもしれませんが、計算量が多くなるにつれて、その差は歴然とし、計算量が多くなるにつれて、その差は歴然とします

キーも値も固定できるような状況では、マップよりも構造体を選んだ方がはるかに効率的です

  • ループを1億回シミュレートして、それぞれのデータ構造を使ってどれだけの時間がかかるか見てみましょう。
  • ループ前の現在時刻を計算する
  • ループ後の現在時刻を計算する 最後に、2つの時間の差を計算します。ここではミリ秒を使用します。
func main() {
 t1 := time.Now().UnixNano()/1e6
 for i := 0; i < 100000000; i++ {
  var test struct {
   name string
   hobby string
  hobby string }
  test.Name = "xiaomotong"
  test.hobby = "program"
 t2 := time.}
 t2 := time.Now().UnixNano()/1e6
 fmt.Println("t2 - t1 == ", t2-t1)
}



プログラムを実行して効果を確認します。

<ブロッククオート

# go run main.go
t1 == 1634377149185
t2 == 1634377149221
t2 - t1 == 36

structのアプローチで36msかかるというのは、どんな感じなのでしょうか?

マップのアプローチを見てみましょう。

func main() {
 t1 := time.Now().UnixNano()/1e6
 fmt.Println("t1 == ", t1)

 for i := 0; i < 100000000; i++ {
  var test = map[string]interface{}{}
  test["name"] = "xiaomotong"
  test["hobby"] = "program"
 }
 t2 := time.Now().UnixNano()/1e6
 fmt.Println("t2 == ", t2)
 fmt.Println("t2 - t1 == ", t2-t1)
}



プログラムを実行して効果を確認します。

<ブロッククオート

# go run main.go
t1 == 1634377365927
t2 == 1634377373525
t2 - t1 == 7598

構造体のアプローチを使用すると、7598msかかります。

同じデータに対してmapとstructを使った場合の時間差は212倍ですが、上記のシナリオでコーディングする場合、あなたならどちらのデータ構造を選びますか?

なぜこれほどまでに差が出るかというと

フィールドを決定できるところでは、実行時に動的にコンテンツを割り当てる必要のない一時的なStructsを使用しています。

これはmapの場合とは異なり、インデックスをチェックする必要があるため、非常に時間のかかる作業となります

文字列はどのように継ぎ足されるのですか?

仕事でxdmをコーディングする際、文字列のスプライシングをどのように実装していますか?私たちのツールでは、今のところ次のようなものが提供されています。

  • を使った方法
  • fmtを使ったSprintf()。
  • strings.Joinを使用する
  • bufferメソッドを使用する

これを見て、自分なりの答えが出るかもしれませんが、同じ文字列のスティッチングのケースで、それぞれどのような処理に時間がかかるのか、実際に見てみましょう

のアプローチでは

文字列を50万回追加するループを計算し、その時間を見てみましょう。

func main() {

 t1 := time.Now().UnixNano() / 1e6
 fmt.Println("t1 == ", t1)

 s := "xiao"
 for i := 0; i < 500000; i++ {
  s += "motong"
 }

 t2 := time.Now().UnixNano() / 1e6
 fmt.Println("t2 == ", t2)
 fmt.Println("t2 - t1 == ", t2-t1)
}



の効果を見るためのプログラムが実行されます。

# go run main.go
t1 == 1634378595642
t2 == 1634378743119
t2 - t1 == 147477

このデータxdmを見ると、あまりの遅さにショックを受けます。147477ms、つまり2分27秒もかかっているのです!?

Goの+付き文字列の処理は、データからわかるように、非常にパフォーマンスが高いです

Sprintf()をfmtとする。

func main() {

 t1 := time.Now().UnixNano() / 1e6
 fmt.Println("t1 == ", t1)

 s := "xiao"
 for i := 0; i < 500000; i++ {
  s = fmt.Sprintf("%s%s",s,"motong")
 }

 t2 := time.Now().UnixNano() / 1e6
 fmt.Println("t2 == ", t2)
 fmt.Println("t2 - t1 == ", t2-t1)
}

プログラムを実行して効果を確認します。

<ブロッククオート

# go run main.go
t1 == 1634378977361
t2 == 1634379240292
t2 - t1 == 262931

このデータを見て、262931ms、合計4分22秒かかっていることにも唖然。xdmは、fmt.Sprintfを使ったほうが、+を使ったときより遅くなるとは思っていなかったのでしょう。

文字列.Joinを使用する

func main() {

 t1 := time.Now().UnixNano() / 1e6
 fmt.Println("t1 == ", t1)

 s := "xiao"
 for i := 0; i < 500000; i++ {
  s = strings.Join([]string{s,"motong"},"")
 }

 t2 := time.Now().UnixNano() / 1e6
 fmt.Println("t2 == ", t2)
 fmt.Println("t2 - t1 == ", t2-t1)
}



プログラムが実行され、効果を確認することができます。

<ブロッククオート

# go run main.go
t1 == 1634379455304
t2 == 1634379598227
t2 - t1 == 142923

142923ms、つまり2分22秒かかっており、これは+方式を使った場合と同等である

バッファー方式を使用した場合

バッファの使い方は、本来は

func main() {

 t1 := time.Now().UnixNano() / 1e6
 fmt.Println("t1 == ", t1)

 s := bytes.NewBufferString("xiao")
 for i := 0; i < 500000; i++ {
  s.WriteString("motong")
 }

 t2 := time.Now().UnixNano() / 1e6
 fmt.Println("t2 == ", t2)
 fmt.Println("t2 - t1 == ", t2-t1)
}


# go run main.go
t1 == 1634378506021
t2 == 1634378506030
t2 - t1 == 9

上記のデータから、同じものを50万回スプライシングすると

  • 1回目は、+方式で、147,477msかかります。
  • 2番目はfmt.Sprintf()を使用した場合で、262931msです。
  • 3番目はstrings.Joinを使用した場合で、142923ミリ秒かかります。
  • 4番目は、バッファを使用した場合で、9msです。

バッファリング方式は、1番目と比較して16,386倍、2番目と比較して29,214倍、3番目と比較して15,880倍速い

xdmさん、あなたが上記のシナリオになったら、どの方法を選びますか?

golangの効率的なコーディングの詳細についてのこの記事は以上です。golangでの効率的なコーディングの詳細については、Scripting Houseの過去の記事を検索するか、以下の記事を引き続き閲覧してください。