Go 컴파일러가 코드를 최적화합니까?
이 코드에서:
package main import "time" func main() { i := 1 go func() { for { i++ } }() <-time.After(1 * time.Second) println(i) }
출력은 항상 1입니다. 그러나 for 루프가 여러 번 반복되기에는 1초면 충분하다는 것이 놀랍습니다. 그 이유는 Go 컴파일러가 코드를 최적화하기 때문입니다.
Go 메모리 모델은 한 고루틴의 변수 읽기가 동일한 변수에 대한 쓰기에 의해 생성된 값을 관찰하도록 보장할 수 있는 조건을 지정합니다. 다른 고루틴. i 증분(i = i 1)을 통한 i 할당에는 동기화 이벤트가 따르지 않으므로 다른 고루틴에서 관찰된다는 보장이 없습니다. 실제로 공격적인 컴파일러는 전체 i 문을 삭제할 수도 있습니다.
예를 들어 이 코드에서:
package main import "time" func main() { i := 1 go func() { for { i++ } }() <-time.After(1 * time.Millisecond) println(i) }
출력은 1입니다. 고루틴은 다음과 같이 줄어듭니다.
"".main.func1 STEXT nosplit size=2 args=0x8 locals=0x0 0x0000 00000 (elide.go:7) TEXT "".main.func1(SB), NOSPLIT, <pre class="brush:php;toolbar:false">for { i++ }-8 0x0000 00000 (elide.go:7) FUNCDATA
package main import "time" func main() { i := 1 go func() { for { i++ println("+1") } }() <-time.After(1 * time.Millisecond) println(i) }, gclocals·2a5305abe05176240e61b8620e19a815(SB) 0x0000 00000 (elide.go:7) FUNCDATA , gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB) 0x0000 00000 (elide.go:9) JMP 0
컴파일러에서는 레지스터를 영원히 증가시켜 for 루프를 구현할 수 있습니다. 이는 기본적으로 아무런 작업도 수행하지 않는 for 루프입니다.
+1 +1 << SNIP >> +1 +1 432
print 문을 삽입한 후
"".main.func1 STEXT size=81 args=0x8 locals=0x18 0x0000 00000 (elide.go:7) TEXT "".main.func1(SB), -8 0x0000 00000 (elide.go:7) MOVQ (TLS), CX 0x0009 00009 (elide.go:7) CMPQ SP, 16(CX) 0x000d 00013 (elide.go:7) JLS 74 0x000f 00015 (elide.go:7) SUBQ , SP 0x0013 00019 (elide.go:7) MOVQ BP, 16(SP) 0x0018 00024 (elide.go:7) LEAQ 16(SP), BP 0x001d 00029 (elide.go:7) FUNCDATA <pre class="brush:php;toolbar:false">================== WARNING: DATA RACE Read at 0x00c420094000 by main goroutine: main.main() /home/peter/gopath/src/lucky.go:14 +0xac Previous write at 0x00c420094000 by goroutine 5: main.main.func1() /home/peter/gopath/src/lucky.go:9 +0x4e Goroutine 5 (running) created at: main.main() /home/peter/gopath/src/lucky.go:7 +0x7a ==================, gclocals·a36216b97439c93dafebe03e7f0808b5(SB) 0x001d 00029 (elide.go:7) FUNCDATA , gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB) 0x001d 00029 (elide.go:8) MOVQ "".&i+32(SP), AX 0x0022 00034 (elide.go:9) INCQ (AX) 0x0025 00037 (elide.go:10) PCDATA
package main import ( "sync" "time" ) func main() { mx := new(sync.Mutex) i := 1 go func() { for { mx.Lock() i++ mx.Unlock() } }() <-time.After(1 * time.Second) mx.Lock() println(i) mx.Unlock() },
418078380x0025 00037 (elide.go:10) CALL runtime.printlock(SB) 0x002a 00042 (elide.go:10) LEAQ go.string."+1\n"(SB), AX 0x0031 00049 (elide.go:10) MOVQ AX, (SP) 0x0035 00053 (elide.go:10) MOVQ , 8(SP) 0x003e 00062 (elide.go:10) PCDATA , 0x003e 00062 (elide.go:10) CALL runtime.printstring(SB) 0x0043 00067 (elide.go:10) PCDATA , 0x0043 00067 (elide.go:10) CALL runtime.printunlock(SB) 0x0048 00072 (elide.go:9) JMP 29 0x004a 00074 (elide.go:9) NOP 0x004a 00074 (elide.go:7) PCDATA , $-1 0x004a 00074 (elide.go:7) CALL runtime.morestack_noctxt(SB) 0x004f 00079 (elide.go:7) JMP 0
출력은 다음과 같습니다.
고루틴은 다음으로 확장됩니다.
고루틴의 복잡성이 증가한다는 것은 컴파일러가 더 이상 다음 값에 대한 레지스터 전용을 고려하지 않는다는 것을 의미합니다. 나. i의 메모리 내 값이 증가하여 데이터 경쟁을 통해 업데이트가 기본 고루틴에 표시됩니다.
예상된 결과를 얻으려면 몇 가지 동기화를 추가하세요.
출력:
위 내용은 Go 컴파일러는 goroutine에서 변수를 증가시키는 코드를 최적화합니까?의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!