質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

87.61%

Go言語の関数で、スライスの値渡しとポインタ渡しの効率の違い

解決済

回答 1

投稿

  • 評価
  • クリップ 0
  • VIEW 654

score 20

構造体のスライス([]Person)を関数の引数として渡すとき、スライス要素の構造体のコピーが起こらないように、[]Personではなく[]*Personのようにしました。(ソースコードは1番下です)
調べてみたところ、スライスを引数に渡しても要素自体はコピーされないとのことだったのですが、引数の型の違いでベンチマークに違いが出ていました。
以下に4回分のベンチマークの結果を載せます。

BenchmarkA-6         565           2143826 ns/op         2196458 B/op      19401 allocs/op
BenchmarkB-6         549           2221813 ns/op         2016721 B/op      24241 allocs/op
BenchmarkC-6         561           2111836 ns/op         2196518 B/op      19403 allocs/op
BenchmarkD-6         540           2188856 ns/op         2016780 B/op      24243 allocs/op
BenchmarkA-6         538           2161718 ns/op         2196458 B/op      19401 allocs/op
BenchmarkB-6         541           2234250 ns/op         2016715 B/op      24241 allocs/op
BenchmarkC-6         566           2131299 ns/op         2196509 B/op      19403 allocs/op
BenchmarkD-6         538           2223253 ns/op         2016782 B/op      24243 allocs/op
BenchmarkA-6         552           2258949 ns/op         2196457 B/op      19401 allocs/op
BenchmarkB-6         511           2225532 ns/op         2016711 B/op      24241 allocs/op
BenchmarkC-6         537           2195281 ns/op         2196499 B/op      19403 allocs/op
BenchmarkD-6         535           2236069 ns/op         2016787 B/op      24243 allocs/op
BenchmarkA-6         554           2279453 ns/op         2196468 B/op      19401 allocs/op
BenchmarkB-6         525           2234598 ns/op         2016723 B/op      24241 allocs/op
BenchmarkC-6         544           2158688 ns/op         2196510 B/op      19403 allocs/op
BenchmarkD-6         523           2262048 ns/op         2016791 B/op      24243 allocs/op

割り当てメモリサイズはどちらも変わらないと思っていたのですが、ポインタ渡しの方が少なくなっています。
また、実行速度はポインタ渡しの方が遅くなっていますが、どのあたりで速度が落ちているのでしょうか。
メモリ割り当て回数が増えているのでそれが関係していると思うのですが、どのあたりでメモリ割り当てが起こっているのでしょうか?

以下にソースコードを載せます。
A:[]Person
B:[]*Person
C:*[]Person
D:*[]*Person

package test

import (
    "bufio"
    "fmt"
    "os"
    "strconv"
    "strings"
    "testing"
)

const fileName = "test.dat"

var bodyInfo []string = []string{"height", "weight", "age"}

type Person struct {
    name string
    body map[string]int
}

func inputA() []Person {
    var ps []Person
    f, _ := os.Open(fileName)
    defer f.Close()

    s := bufio.NewScanner(f)

    for s.Scan() {
        p := Person{}
        p.body = make(map[string]int)
        data := strings.Split(s.Text(), ",")
        p.name = data[0]
        for i := 0; i < 3; i++ {
            p.body[bodyInfo[i]], _ = strconv.Atoi(data[i+1])
        }
        ps = append(ps, p)
    }
    return ps
}

func inputB() []*Person {
    var ps []*Person
    f, _ := os.Open(fileName)
    defer f.Close()

    s := bufio.NewScanner(f)

    for s.Scan() {
        p := &Person{}
        p.body = make(map[string]int)
        data := strings.Split(s.Text(), ",")
        p.name = data[0]
        for i := 0; i < 3; i++ {
            p.body[bodyInfo[i]], _ = strconv.Atoi(data[i+1])
        }
        ps = append(ps, p)
    }
    return ps
}

func inputC() *[]Person {
    var ps []Person
    f, _ := os.Open(fileName)
    defer f.Close()

    s := bufio.NewScanner(f)

    for s.Scan() {
        p := Person{}
        p.body = make(map[string]int)
        data := strings.Split(s.Text(), ",")
        p.name = data[0]
        for i := 0; i < 3; i++ {
            p.body[bodyInfo[i]], _ = strconv.Atoi(data[i+1])
        }
        ps = append(ps, p)
    }
    return &ps
}

func inputD() *[]*Person {
    var ps []*Person
    f, _ := os.Open(fileName)
    defer f.Close()

    s := bufio.NewScanner(f)

    for s.Scan() {
        p := &Person{}
        p.body = make(map[string]int)
        data := strings.Split(s.Text(), ",")
        p.name = data[0]
        for i := 0; i < 3; i++ {
            p.body[bodyInfo[i]], _ = strconv.Atoi(data[i+1])
        }
        ps = append(ps, p)
    }
    return &ps
}

func A(ps []Person) []Person {
    var nps []Person

    for _, v := range ps {
        if v.body["height"] >= 180 && v.body["weight"] >= 80 && v.body["age"] >= 20 {
            nps = append(nps, v)
        }
    }
    return nps
}

func B(ps []*Person) []*Person {
    var nps []*Person

    for _, v := range ps {
        if v.body["height"] >= 180 && v.body["weight"] >= 80 && v.body["age"] >= 20 {
            nps = append(nps, v)
        }
    }
    return nps
}

func C(ps *[]Person) *[]Person {
    var nps []Person

    for _, v := range *ps {
        if v.body["height"] >= 180 && v.body["weight"] >= 80 && v.body["age"] >= 20 {
            nps = append(nps, v)
        }
    }
    return &nps
}

func D(ps *[]*Person) *[]*Person {
    var nps []*Person

    for _, v := range *ps {
        if v.body["height"] >= 180 && v.body["weight"] >= 80 && v.body["age"] >= 20 {
            nps = append(nps, v)
        }
    }
    return &nps
}

func BenchmarkA(b *testing.B) {
    b.ResetTimer()
    for i := 0; i < b.N; i++ {
        ps := inputA()
        nps := A(ps)
        f, _ := os.Create(fmt.Sprintf("./A/%d.dat", i))
        w := bufio.NewWriter(f)
        for _, v := range nps {
            w.WriteString(v.name + "\n")
        }
        w.Flush()
        f.Close()
    }
}

func BenchmarkB(b *testing.B) {
    b.ResetTimer()
    for i := 0; i < b.N; i++ {
        ps := inputB()
        nps := B(ps)
        f, _ := os.Create(fmt.Sprintf("./B/%d.dat", i))
        w := bufio.NewWriter(f)
        for _, v := range nps {
            w.WriteString(v.name + "\n")
        }
        w.Flush()
        f.Close()
    }
}

func BenchmarkC(b *testing.B) {
    b.ResetTimer()
    for i := 0; i < b.N; i++ {
        ps := inputC()
        nps := C(ps)
        f, _ := os.Create(fmt.Sprintf("./C/%d.dat", i))
        w := bufio.NewWriter(f)
        for _, v := range *nps {
            w.WriteString(v.name + "\n")
        }
        w.Flush()
        f.Close()
    }
}

func BenchmarkD(b *testing.B) {
    b.ResetTimer()
    for i := 0; i < b.N; i++ {
        ps := inputD()
        nps := D(ps)
        f, _ := os.Create(fmt.Sprintf("./D/%d.dat", i))
        w := bufio.NewWriter(f)
        for _, v := range *nps {
            w.WriteString(v.name + "\n")
        }
        w.Flush()
        f.Close()
    }
}
  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 1

checkベストアンサー

0

いま例示しているベンチマークの数値は5%前後の差で微差に見える結果です。
メモリ操作のベンチマークにファイル操作を混在させるのは比較したいものに対して適切ではありません。
一般にメモリ操作に対してファイル操作の処理時間はうん十倍のオーダーなので。
メモリ操作だけの処理にするともっとはっきりと差が出ると思います。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 87.61%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る