2012/09/15

文字列結合、ふたたび

計測が不十分だったかな

前回の計測は、
  • bytes.Buffer を使用するにしても事前にバイト数を算出したほうがいいのか?
  • append()で結合したらどうなん?
などが抜けていたので、改めて計測。

コード

package main

import (
 "bytes"
 "fmt"
 "time"
)

var tests = []string{
 "Lorem ipsum dolor sit amet",
 "consectetur adipisicing elit",
 "sed do eiusmod tempor incididunt ut labore et dolore magna aliqua",
 "Ut enim ad minim veniam",
 "quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat",
}

func concat(times int) string {
 c := len(tests)
 s := ""
 for n := 0; n < times; n++ {
  s += tests[n%c]
 }
 return s
}

func useBuffer256(times int) string {
 c := len(tests)

 // 結合後の byte 数を得る
 size := 0
 for n := 0; n < times; n++ {
  size += len(tests[n%c])
 }

 buffer := bytes.NewBuffer(make([]byte, 0, 256))
 for n := 0; n < times; n++ {
  buffer.WriteString(tests[n%c])
 }
 return buffer.String()
}

func useBuffer(times int) string {
 c := len(tests)

 // 結合後の byte 数を得る
 size := 0
 for n := 0; n < times; n++ {
  size += len(tests[n%c])
 }

 buffer := bytes.NewBuffer(make([]byte, 0, size))
 for n := 0; n < times; n++ {
  buffer.WriteString(tests[n%c])
 }
 return buffer.String()
}

func likeJoin(times int) string {
 c := len(tests)

 // 結合後の byte 数を得る
 size := 0
 for n := 0; n < times; n++ {
  size += len(tests[n%c])
 }

 // バッファを必要な分確保する
 buffer := make([]byte, size)

 // 文字列のコピー
 bp := 0
 for n := 0; n < times; n++ {
  bp += copy(buffer[bp:], tests[n%c])
 }

 return string(buffer)
}

func useAppend(times int) string {
 c := len(tests)

 // 結合後の byte 数を得る
 size := 0
 for n := 0; n < times; n++ {
  size += len(tests[n%c])
 }

 // バッファを必要な分確保する
 buffer := make([]byte, 0, size)

 // 文字列のコピー
 for n := 0; n < times; n++ {
  buffer = append(buffer, tests[n%c]...)
 }

 return string(buffer)
}

func measure(loop, times int) {
 fmt.Println("--- loop:", loop, "times:", times, "---")

 var (
  start time.Time
  end   time.Time
 )

 start = time.Now()
 for n := 0; n < loop; n++ {
  concat(times)
 }
 end = time.Now()
 fmt.Println("concat()", end.Sub(start).Nanoseconds()/1000, "us")

 start = time.Now()
 for n := 0; n < loop; n++ {
  useBuffer256(times)
 }
 end = time.Now()
 fmt.Println("useBuffer256()", end.Sub(start).Nanoseconds()/1000, "us")

 start = time.Now()
 for n := 0; n < loop; n++ {
  useBuffer(times)
 }
 end = time.Now()
 fmt.Println("useBuffer()", end.Sub(start).Nanoseconds()/1000, "us")

 start = time.Now()
 for n := 0; n < loop; n++ {
  likeJoin(times)
 }
 end = time.Now()
 fmt.Println("likeJoin()", end.Sub(start).Nanoseconds()/1000, "us")

 start = time.Now()
 for n := 0; n < loop; n++ {
  useAppend(times)
 }
 end = time.Now()
 fmt.Println("useAppend()", end.Sub(start).Nanoseconds()/1000, "us")
}

func main() {
 measure(10, 10000)
 measure(10000, 10)
}

結果

loop: 10 times: 10000

concat()
5,288,925 us
useBuffer256()
8,536 us
useBuffer()
6,480 us
likeJoin()
6,122 us
useAppend()
5,664 us

loop: 10000 times: 10

concat()
27,830 us
useBuffer256()
17,895 us
useBuffer()
12,487 us
likeJoin()
10,746 us
useAppend()
11,550 us
concat()は、まぁ選択から除外するとして。
やっぱり、余計なコードが走らないコードが一番速い。
bytes.Buffer を使用するにしても、やはり事前にバイト数を計算したほうが速い likeJoin()とuseAppend()の差はもはや誤差の範囲かな。
あとは
  • バッファから溢れたとき拡張して欲しければ append()
  • バッファを拡張して欲しくないときは copy()
で使い分けるのが吉かと。

0 件のコメント:

コメントを投稿