LoginSignup
4
2

More than 5 years have passed since last update.

fmt.Print(str) v.s. fmt.Printf(str)

Last updated at Posted at 2017-06-04

Go で文字列をフォーマットせずに出力する時普段は fmt.Print を使っています.

Print("hello")

ですが,fmt.Print は引き数に interface{} を取るので string を直接第1引数に取れる fmt.Printf のほうが速いのでは…とふと思いました.

Printf("hello")

というわけでベンチマークをとってみました.

package main

import (
    "fmt"
    "io/ioutil"
    "testing"
)

func BenchmarkPrint(b *testing.B) {
    for i := 0; i < b.N; i++ {
        fmt.Fprint(ioutil.Discard, "hello, hello")
    }
}

func BenchmarkPrintf(b *testing.B) {
    for i := 0; i < b.N; i++ {
        fmt.Fprintf(ioutil.Discard, "hello, hello")
    }
}

結果:

BenchmarkPrint-8        20000000            92.4 ns/op
BenchmarkPrintf-8       30000000            46.8 ns/op
PASS
ok      command-line-arguments  3.402s

呼び出しコストは Print のほうが2倍ぐらい大きいようです.では Printf が良いのでしょうか?もうちょっと見てみます.

大きい文字列を入力にしてみます.

package main

import (
    "fmt"
    "io/ioutil"
    "strings"
    "testing"
)

var input = strings.Repeat("hello, ", 1000)

func BenchmarkPrint(b *testing.B) {
    for i := 0; i < b.N; i++ {
        fmt.Fprint(ioutil.Discard, input)
    }
}

func BenchmarkPrintf(b *testing.B) {
    for i := 0; i < b.N; i++ {
        fmt.Fprintf(ioutil.Discard, input)
    }
}

結果:

BenchmarkPrint-8        10000000           167 ns/op
BenchmarkPrintf-8         500000          2892 ns/op
PASS
ok      command-line-arguments  3.331s

今度は Printf のほうがかなり遅い結果になりました.Printf はフォーマット文字列を走査しないといけないからでしょうか?しかしフォーマット文字列の後の可変長部分は空なのでそれは不要のはずです.おかしいですね…

と思ったら,どうやら可変長部分が空でもフォーマット文字列を走査していました.それは遅い…

結論

  • 小さい文字列を繰り返し print するときは Printf が良く,大きめの文字列を一度に print するときは Print が良く,それ以外の場合はどちらでも良い
  • Print の呼び出しコストは高々2倍なので,よほどのことが無い限り当初通り Print を使っておけば良い
  • Go はベンチマーク取りやすくて良い
4
2
5

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
2