Интересни имплементационни детайли в Go
04.12.2014
gc toolchain-а наследява този на plan9
Туловете в него са от типа \d[acl]
:
И къде тук има go?
-x го кара да принтира какво всъщност прави:
-> go build -x github.com/Vladimiroff/vec2d WORK=/var/folders/t2/24j0lkxs1d93btzxnl71n_kw0000gn/T/go-build703616983 mkdir -p $WORK/github.com/Vladimiroff/vec2d/_obj/ mkdir -p $WORK/github.com/Vladimiroff/ cd /Users/kiril/go/src/github.com/Vladimiroff/vec2d /usr/local/Cellar/go/1.3.3/libexec/pkg/tool/darwin_amd64/6g -o $WORK/github.com/Vladimiroff/vec2d.a -trimpath $WORK -p github.com/Vladimiroff/vec2d -complete -D _/Users/kiril/go/src/github.com/Vladimiroff/vec2d -I $WORK -pack ./utils.go ./vector.go
var gopher int32 = 2014
type Location struct { // float64 -> 8 bytes X, Y, Z float64 // 24 bytes in total } var Locations [1000]Location // 24 * 1000 bytes stored *sequnetially*
=> По-добро използване на кеша
Бе върши се доста работа по темата.
package util func Max(a, b int) int { if a > b { return a } return b }
---
package main func double(a, b int) int { return 2 * util.Max(a, b) }
package main func double(a, b int) int { max := b if a > b { max = a } return 2 * max }
func LargeAndComplicatedAction() { if false { [...] } [...] }
func DebugChecks() bool { return debugBuildTagIsOn } func LargeAndComplicatedAction() { if DebugChecks() { [...] } [...] }
Кое къде отива в С, примерно?
malloc
отива на хийпаТолкова е просто. Хайде сега на Go
func SumFirst100() int { numbers := make([]int, 100) for i := range numbers { numbers[i] = i + 1 } var sum int for _, i := range numbers { sum += i } return sum }
numbers
?type Cursor struct { X, Y int } const width, height = 640, 480 func Center(c *Cursor) { c.X += width / 2 c.Y += height / 2 } func CenterCursor() (int, int) { c := new(Cursor) Center(c) return c.X, c.Y }
new(Cursor)
?-> go build -gcflags=-m esc.go # command-line-arguments ./esc.go:9: can inline Center ./esc.go:16: inlining call to Center ./esc.go:9: Center c does not escape ./esc.go:15: CenterCursor new(Cursor) does not escape ./esc.go:22: SumFirst100 make([]int, 100) does not escape
Cooperatively scheduled
Сценарий: Изпълняваме thread на C.
int ack(int m, int n) { if (m == 0) { return n + 1; } else if (m > 0 && n == 0) { return ack(m - 1, 1); } else { return ack(m - 1, ack(m, n - 1)); } } ack(4, 5); // segmentation fault
Рекурсия. Свърши ни паметта на стека.
Да, това ще понася по-дълбока рекурсия, но пък ще заделяме повече памет за
АБСОЛЮТНО всяка функция, която тя може да не използва.
Ще знаем точно колко голям стек да заделяме за всяка функция, но този анализ ще
отнема време, което води до бавна компилация с много налучкване или бавно
изпълнение на всяка функция
Това е начинът, по който Go до версия 1.3 управляваше стековете.
morestack
morestack
заделя нова памет за стек+---------------+ | | | unused | | stack | | space | +---------------+ | Foobar | | | +---------------+ +---------------+ | | +-->| Foobar | | lessstack | | | | +---------------+ | +---------------+ | Stack info |---+ | rest of stack | | | | | +---------------+ +---------------+
Но има и проблеми
В началото на тази година Dmitry Vyukov предложи редица оптимизации на каналите:
Отдолу каналите са три вида:
1. Sync channels
2. Async channels
3. Async channels with zero-sized elements
struct Hchan { Lock; bool closed; SudoG* sendq; // waiting senders SudoG* recvq; // waiting receivers };
truct Hchan { uint32 cap; // channel capacity Elem* buf; // ring buffer of size cap // send and receive positions, // low 32 bits represent position in the buffer, // high 32 bits represent the current “lap” over the ring buffer uint64 sendx; uint64 recvx; }; struct Elem { // current lap, // the element is ready for writing on laps 0, 2, 4, ... // for reading -- on laps 1, 3, 5, ... uint32 lap; T val; // user data };
Почти като обикновените async channels:
С тази разлика, че:
closed
флагаНе се опитва да хване всички mutex-и на всички канали