LoginSignup
0
0

【Go言語学習】ポインタのレシーバの実験

Posted at

レシーバがポインタとそうでないと何が違うのだろうと思ったので、
基本的には大きいデータの構造体はポインタで扱うのが良いというのと、ポインタで扱う場合は、副作用に注意する必要があるということ。
副作用は、現状アイディアが無いので、とりあえず処理時間とメモリの使用量を比べてみることに...
この実験は結論から言うと、面白くない結果です...アイディアが乏しいのが原因かな...悔しい

ポインタの時

func (g *Group) Go(f func() error)の場合、gはポインタなので、同じオブジェクトを複数のゴルーチンで使用すると、それらのゴルーチンがオブジェクトの状態を同時に変更しようとすると競合状態が発生する可能性ある。
同じオブジェクトを複数のゴルーチンで使用する場合は、適切な同期メカニズム(例えば、ミューテックス)を使用して、同時アクセスを制御することが重要。(今回何もしていなので、何かすれば間違いなく結果は変わるでしょう)

package main

import (
	"fmt"
	"runtime"
	"sync"
	"time"
)

type Group struct {
	cancel  func()
	wg      sync.WaitGroup //各ゴルーチンの完了を待たせるためのやつ
	errOnce sync.Once      //指定した関数を一度だけ実行したいときに便利なやつ
	err     error
	str     string
}

func (g *Group) Go1(f func() error) {
	g.wg.Add(1) //Goメソッドが呼び出されるたびに、+1カウント

	go func() {
		defer g.wg.Done() //抜ける時に-1カウント...厳密には、無名関数かゴルーチンを抜けた時なのか同じことか

		if err := f(); err != nil {
			g.errOnce.Do(func() { //ここで無名関数を実装し一度だけ実行される、この場合エラー出なきゃ一度も実行されない
				g.err = err
				if g.cancel != nil {
					g.cancel() //見えない関数かメソッドが実装されてる!!継承!?←違うそうです!!
				}
			})
		}
	}()
}

func main() {
	var mem runtime.MemStats
	var g Group
	g.str = "ある日、プログラミングの世界に迷い込んだカエルがいました。その名も「Go」。Goは、自分が何者なのかを知りたくて、コードの海を飛び回っていました。「私は何ができるのだろう?」とGoは思いました。そこで彼は、自分がどれだけ速く動けるか試すことにしました。彼は一生懸命にコードを書き始めました。しかし、彼が書いたコードはとても長く、彼自身も何を書いているのかわからなくなってしまいました。「これは一体何だろう?」とGoは思いました。しかし、彼は諦めませんでした。彼は再びコードを書き始めました。今度は、彼が書いたコードはとても短く、彼自身も何を書いているのかわからなくなってしまいました。「これは一体何だろう?」とGoは再び思いました。しかし、彼は再び諦めませんでした。彼は再々コードを書き始めました。今度は、彼が書いたコードはちょうど良い長さで、彼自身も何を書いているのかわからなくなってしまいました。「これは一体何だろう?」とGoは再々思いました。しかし、彼は再々諦めませんでした。彼は再々々コードを書き始めました。今度は、彼が書いたコードはちょうど良い長さで、彼自身も何を書いているのかわからなくなってしまいました。「これは一体何だろう?」とGoは再々々思いました。しかし、彼は再々々諦めませんでした。彼は再々々々コードを書き始めました。今度は、彼が書いたコードはちょうど良い長さで、彼自身も何を書いているのかわからなくなってしまいました。「これは一体何だろう?」とGoは再々々々思いました。しかし、彼は再々々々諦めませんでした。彼は再々々々々コードを書き始めました。今度は、彼が書いたコードはちょうど良い長さで、彼自身も何を書いているのかわからなくなってしまいました。「これは一体何だろう?」とGoは再々々々々思いました。しかし、彼は再々々々々諦めませんでした。彼は再々々々々々コードを書き始めました。今度は、彼が書いたコードはちょうど良い長さで、彼自身も何を書いているのかわからなくなってしまいました。「これは一体何だろう?」とGoは再々々々々々思いました。しかし、彼は再々々々々々諦めませんでした。彼は再々々々々々々コードを書き始めました。今度は、彼が書いたコードはちょうど良い長さで、彼自身も何を書いているのかわからなくなってしまいました。「これは一体何だろう?」とGoは再々々々々々々思いました。しかし、彼は再々々々々々々諦めませんでした。彼は再々々々々々々々コードを書き始めました。今度は、彼が書いたコードはちょうど良い長さで、彼自身も何を書いているのかわからなくなってしまいました。「これは一体何だろう?」とGoは再々々々々々々々思いました。しかし、彼は再々々々々々々々諦めませんでした。彼は再々々々々々々々々コードを書き始めました。今度は、彼が書いたコードはちょうど良い長さで、彼自身も何を書いているのかわからなくなってしまいました。「これは一体何だろう?」とGoは再々々々々々々々々思いました。しかし、彼は再々々々々々々々々諦めませんでした。彼は再々々々々々々々々々コードを書き始めました。今度は、彼が書いたコードはちょうど良い長さで、彼自身も何を書いているのかわからなくなってしまいました。「これは一体何だろう?」とGoは再々々々々々々々々々思いました。しかし、彼は再々々々々々々々々々諦めませんでした。彼は再々々々々々々々々々々コードを書き始めました。今度は、彼が書いたコードはちょうど良い長さで、彼自身も何を書いているのかわからなくなってしまいました。「これは一体何だろう?」とGoは再々々々々々々々々々々思いました。しかし、彼は再々々々々々々々々々々諦めませんでした。彼は再々々々々々々々々々々々コードを書き始めました。今度は、彼が書いたコードはちょうど良い長さで、彼自身も何を書いているのかわからなくなってしまいました。「これは一体何だろう?」とGoは再々々々々々々々々々々々思いました。しかし、彼は再々々々々々々々々々々々諦めませんでした。彼は再々々々々々々々々々々々々コードを書き始めました。今度は、彼が書いたコードはちょうど..."
	start := time.Now()

	g.Go1(func() error { //無名関数を参照アドレスポインタの値渡しである
		g.str = "That works!"
		fmt.Println(g.str)
		fmt.Println("That works!")
		g.str = ""
		return nil
	})
	g.Go1(func() error {
		g.str = "The best!"
		fmt.Println(g.str)
		fmt.Println("The best!")
		g.str = ""
		return nil
	})
	g.Go1(func() error {
		g.str = "It's too much fun!"
		fmt.Println(g.str)
		fmt.Println("It's too much fun!")
		g.str = ""
		return nil
	})
	g.Go1(func() error {
		g.str = "It's going well!"
		fmt.Println(g.str)
		fmt.Println("It's going well!")
		g.str = ""
		return nil
	})

	g.wg.Wait() //メインを待たせる
	if g.err != nil {
		fmt.Println("Encountered error:", g.err)
	}

	elapsed := time.Since(start)
	fmt.Printf("Go1処理時間: %s\n", elapsed)

	runtime.ReadMemStats(&mem)
	fmt.Printf("使用メモリ: %v KB\n", mem.Alloc/1024)
}

3回の実行結果

Go1処理時間: 2.3843ms
使用メモリ: 111 KB
Go1処理時間: 2.813ms
使用メモリ: 111 KB
Go1処理時間: 1.75ms
使用メモリ: 111 KB

ポインタでない時1

func (g Group) Go(f func() error)の場合、gは値なので、このメソッドが呼び出されるたびにGroup型の新しいコピーが作成される。
同じオブジェクトを複数のゴルーチンで使用しても、それぞれのゴルーチンは独自のGroupコピーを操作するため、競合状態は発生しない。
引き換えに、オブジェクトを構成するデータのやり取りが多いはず。

package main

import (
	"fmt"
	"runtime"
	"sync"
	"time"
)

type Group struct {
	cancel  func()
	errOnce sync.Once
	err     error
	str     string
}

func (g Group) Go2(f func() error) { //強引にポインタ渡しを解除した
	go func() {
		if err := f(); err != nil {
			g.errOnce.Do(func() {
				g.err = err
				if g.cancel != nil {
					g.cancel()
				}
			})
		}
	}()
}

func main() {
	var mem runtime.MemStats
	var wg sync.WaitGroup
	var g Group
	g.str = "ある日、プログラミングの世界に迷い込んだカエルがいました。その名も「Go」。Goは、自分が何者なのかを知りたくて、コードの海を飛び回っていました。「私は何ができるのだろう?」とGoは思いました。そこで彼は、自分がどれだけ速く動けるか試すことにしました。彼は一生懸命にコードを書き始めました。しかし、彼が書いたコードはとても長く、彼自身も何を書いているのかわからなくなってしまいました。「これは一体何だろう?」とGoは思いました。しかし、彼は諦めませんでした。彼は再びコードを書き始めました。今度は、彼が書いたコードはとても短く、彼自身も何を書いているのかわからなくなってしまいました。「これは一体何だろう?」とGoは再び思いました。しかし、彼は再び諦めませんでした。彼は再々コードを書き始めました。今度は、彼が書いたコードはちょうど良い長さで、彼自身も何を書いているのかわからなくなってしまいました。「これは一体何だろう?」とGoは再々思いました。しかし、彼は再々諦めませんでした。彼は再々々コードを書き始めました。今度は、彼が書いたコードはちょうど良い長さで、彼自身も何を書いているのかわからなくなってしまいました。「これは一体何だろう?」とGoは再々々思いました。しかし、彼は再々々諦めませんでした。彼は再々々々コードを書き始めました。今度は、彼が書いたコードはちょうど良い長さで、彼自身も何を書いているのかわからなくなってしまいました。「これは一体何だろう?」とGoは再々々々思いました。しかし、彼は再々々々諦めませんでした。彼は再々々々々コードを書き始めました。今度は、彼が書いたコードはちょうど良い長さで、彼自身も何を書いているのかわからなくなってしまいました。「これは一体何だろう?」とGoは再々々々々思いました。しかし、彼は再々々々々諦めませんでした。彼は再々々々々々コードを書き始めました。今度は、彼が書いたコードはちょうど良い長さで、彼自身も何を書いているのかわからなくなってしまいました。「これは一体何だろう?」とGoは再々々々々々思いました。しかし、彼は再々々々々々諦めませんでした。彼は再々々々々々々コードを書き始めました。今度は、彼が書いたコードはちょうど良い長さで、彼自身も何を書いているのかわからなくなってしまいました。「これは一体何だろう?」とGoは再々々々々々々思いました。しかし、彼は再々々々々々々諦めませんでした。彼は再々々々々々々々コードを書き始めました。今度は、彼が書いたコードはちょうど良い長さで、彼自身も何を書いているのかわからなくなってしまいました。「これは一体何だろう?」とGoは再々々々々々々々思いました。しかし、彼は再々々々々々々々諦めませんでした。彼は再々々々々々々々々コードを書き始めました。今度は、彼が書いたコードはちょうど良い長さで、彼自身も何を書いているのかわからなくなってしまいました。「これは一体何だろう?」とGoは再々々々々々々々々思いました。しかし、彼は再々々々々々々々々諦めませんでした。彼は再々々々々々々々々々コードを書き始めました。今度は、彼が書いたコードはちょうど良い長さで、彼自身も何を書いているのかわからなくなってしまいました。「これは一体何だろう?」とGoは再々々々々々々々々々思いました。しかし、彼は再々々々々々々々々々諦めませんでした。彼は再々々々々々々々々々々コードを書き始めました。今度は、彼が書いたコードはちょうど良い長さで、彼自身も何を書いているのかわからなくなってしまいました。「これは一体何だろう?」とGoは再々々々々々々々々々々思いました。しかし、彼は再々々々々々々々々々々諦めませんでした。彼は再々々々々々々々々々々々コードを書き始めました。今度は、彼が書いたコードはちょうど良い長さで、彼自身も何を書いているのかわからなくなってしまいました。「これは一体何だろう?」とGoは再々々々々々々々々々々々思いました。しかし、彼は再々々々々々々々々々々々諦めませんでした。彼は再々々々々々々々々々々々々コードを書き始めました。今度は、彼が書いたコードはちょうど..."
	start := time.Now()

	wg.Add(4) //ここでカウント
	g.Go2(func() error {
		defer wg.Done() //各-1カウント
		g.str = "That works!"
		fmt.Println(g.str)
		fmt.Println("That works!")
		g.str = ""
		return nil
	})
	g.Go2(func() error {
		defer wg.Done()
		g.str = "The best!"
		fmt.Println(g.str)
		fmt.Println("The best!")
		g.str = ""
		return nil
	})
	g.Go2(func() error {
		defer wg.Done() //各-1カウント
		g.str = "It's too much fun!"
		fmt.Println(g.str)
		fmt.Println("It's too much fun!")
		g.str = ""
		return nil
	})
	g.Go2(func() error {
		defer wg.Done()
		g.str = "It's going well!"
		fmt.Println(g.str)
		fmt.Println("It's going well!")
		g.str = ""
		return nil
	})
	wg.Wait()
	if g.err != nil {
		fmt.Println("Encountered error:", g.err)
	}

	elapsed := time.Since(start)
	fmt.Printf("Go2処理時間: %s\n", elapsed)

	runtime.ReadMemStats(&mem)
	fmt.Printf("使用メモリ: %v KB\n", mem.Alloc/1024)
}

3回の実行結果

Go2処理時間: 2.1325ms
使用メモリ: 111 KB
Go2処理時間: 2.4778ms
使用メモリ: 109 KB
Go2処理時間: 2.3171ms
使用メモリ: 111 KB

ポインタでない時2

sync.WaitGroupのポインタを引数に追加

package main

import (
	"fmt"
	"runtime"
	"sync"
	"time"
)

type Group struct {
	cancel  func()
	errOnce sync.Once
	err     error
	str     string
}

func (g Group) Go3(f func() error, wg *sync.WaitGroup) {
	wg.Add(1) //Goメソッドが呼び出されるたびに、+1カウント

	go func() {
		defer wg.Done() //こっちのメソッドはAddとDoneをメソッド内で呼び出す
		if err := f(); err != nil {
			g.errOnce.Do(func() {
				g.err = err
				if g.cancel != nil {
					g.cancel()
				}
			})
		}
	}()
}

func main() {
	var mem runtime.MemStats
	var wg sync.WaitGroup
	var g Group
	g.str = "ある日、プログラミングの世界に迷い込んだカエルがいました。その名も「Go」。Goは、自分が何者なのかを知りたくて、コードの海を飛び回っていました。「私は何ができるのだろう?」とGoは思いました。そこで彼は、自分がどれだけ速く動けるか試すことにしました。彼は一生懸命にコードを書き始めました。しかし、彼が書いたコードはとても長く、彼自身も何を書いているのかわからなくなってしまいました。「これは一体何だろう?」とGoは思いました。しかし、彼は諦めませんでした。彼は再びコードを書き始めました。今度は、彼が書いたコードはとても短く、彼自身も何を書いているのかわからなくなってしまいました。「これは一体何だろう?」とGoは再び思いました。しかし、彼は再び諦めませんでした。彼は再々コードを書き始めました。今度は、彼が書いたコードはちょうど良い長さで、彼自身も何を書いているのかわからなくなってしまいました。「これは一体何だろう?」とGoは再々思いました。しかし、彼は再々諦めませんでした。彼は再々々コードを書き始めました。今度は、彼が書いたコードはちょうど良い長さで、彼自身も何を書いているのかわからなくなってしまいました。「これは一体何だろう?」とGoは再々々思いました。しかし、彼は再々々諦めませんでした。彼は再々々々コードを書き始めました。今度は、彼が書いたコードはちょうど良い長さで、彼自身も何を書いているのかわからなくなってしまいました。「これは一体何だろう?」とGoは再々々々思いました。しかし、彼は再々々々諦めませんでした。彼は再々々々々コードを書き始めました。今度は、彼が書いたコードはちょうど良い長さで、彼自身も何を書いているのかわからなくなってしまいました。「これは一体何だろう?」とGoは再々々々々思いました。しかし、彼は再々々々々諦めませんでした。彼は再々々々々々コードを書き始めました。今度は、彼が書いたコードはちょうど良い長さで、彼自身も何を書いているのかわからなくなってしまいました。「これは一体何だろう?」とGoは再々々々々々思いました。しかし、彼は再々々々々々諦めませんでした。彼は再々々々々々々コードを書き始めました。今度は、彼が書いたコードはちょうど良い長さで、彼自身も何を書いているのかわからなくなってしまいました。「これは一体何だろう?」とGoは再々々々々々々思いました。しかし、彼は再々々々々々々諦めませんでした。彼は再々々々々々々々コードを書き始めました。今度は、彼が書いたコードはちょうど良い長さで、彼自身も何を書いているのかわからなくなってしまいました。「これは一体何だろう?」とGoは再々々々々々々々思いました。しかし、彼は再々々々々々々々諦めませんでした。彼は再々々々々々々々々コードを書き始めました。今度は、彼が書いたコードはちょうど良い長さで、彼自身も何を書いているのかわからなくなってしまいました。「これは一体何だろう?」とGoは再々々々々々々々々思いました。しかし、彼は再々々々々々々々々諦めませんでした。彼は再々々々々々々々々々コードを書き始めました。今度は、彼が書いたコードはちょうど良い長さで、彼自身も何を書いているのかわからなくなってしまいました。「これは一体何だろう?」とGoは再々々々々々々々々々思いました。しかし、彼は再々々々々々々々々々諦めませんでした。彼は再々々々々々々々々々々コードを書き始めました。今度は、彼が書いたコードはちょうど良い長さで、彼自身も何を書いているのかわからなくなってしまいました。「これは一体何だろう?」とGoは再々々々々々々々々々々思いました。しかし、彼は再々々々々々々々々々々諦めませんでした。彼は再々々々々々々々々々々々コードを書き始めました。今度は、彼が書いたコードはちょうど良い長さで、彼自身も何を書いているのかわからなくなってしまいました。「これは一体何だろう?」とGoは再々々々々々々々々々々々思いました。しかし、彼は再々々々々々々々々々々々諦めませんでした。彼は再々々々々々々々々々々々々コードを書き始めました。今度は、彼が書いたコードはちょうど..."
	start := time.Now()

	g.Go3(func() error {
		g.str = "That works!"
		fmt.Println(g.str)
		fmt.Println("That works!")
		g.str = ""
		return nil
	}, &wg) //値の参照アドレスポインタを渡す
	g.Go3(func() error {
		g.str = "The best!"
		fmt.Println(g.str)
		fmt.Println("The best!")
		g.str = ""
		return nil
	}, &wg)
	g.Go3(func() error {
		g.str = "It's too much fun!"
		fmt.Println(g.str)
		fmt.Println("It's too much fun!")
		g.str = ""
		return nil
	}, &wg)
	g.Go3(func() error {
		g.str = "It's going well!"
		fmt.Println(g.str)
		fmt.Println("It's going well!")
		g.str = ""
		return nil
	}, &wg)

	wg.Wait()
	if g.err != nil {
		fmt.Println("Encountered error:", g.err)
	}

	elapsed := time.Since(start)
	fmt.Printf("Go3処理時間: %s\n", elapsed)

	runtime.ReadMemStats(&mem)
	fmt.Printf("使用メモリ: %v KB\n", mem.Alloc/1024)
}

3回の実行結果

Go3処理時間: 2.3606ms
使用メモリ: 111 KB
Go3処理時間: 3.0001ms
使用メモリ: 111 KB
Go3処理時間: 2.3244ms
使用メモリ: 111 KB

比較結果

面白くな~い結果。またなんか仕様を勘違いしてるかもしれないな

Go1処理時間: 2.3843ms
使用メモリ: 111 KB
Go1処理時間: 2.813ms
使用メモリ: 111 KB
Go1処理時間: 1.75ms
使用メモリ: 111 KB
Go3処理時間: 2.3606ms
使用メモリ: 111 KB
Go3処理時間: 3.0001ms
使用メモリ: 111 KB
Go3処理時間: 2.3244ms
使用メモリ: 111 KB
Go2処理時間: 2.1325ms
使用メモリ: 111 KB
Go2処理時間: 2.4778ms
使用メモリ: 109 KB
Go2処理時間: 2.3171ms
使用メモリ: 111 KB
0
0
0

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
0
0