Интересни имплементационни детайли в Go

05.01.2016

Въпроси за мъфини

Въпрос за мъфин #1

Какво прави go vet ./...?

Въпрос за мъфин #2

Какво е Oracle? В контекста на golang.

Въпрос за мъфин #3

Как може да видим errno на C функция, която сме извикали?

Въпрос за мъфин #4 и #5

Какво прави select?

select {
case result := <-google:
    fmt.Printf("received %v from google\n", result)
case result := <-bing:
    fmt.Printf("received %v from bing\n", result)
case <- time.After(1 * time.Second):
    fmt.Printf("timed out\n")
}

Но преди това

go build

-x го кара да принтира какво всъщност прави:

-> go build -x github.com/Vladimiroff/vec2d
WORK=/tmp/go-build495023012
mkdir -p $WORK/github.com/Vladimiroff/vec2d/_obj/
mkdir -p $WORK/github.com/Vladimiroff/
cd /home/mstoykov/workspace/go/src/github.com/Vladimiroff/vec2d
/home/mstoykov/workspace/goroot/go/pkg/tool/linux_amd64/compile
    -o $WORK/github.com/Vladimiroff/vec2d.a -trimpath $WORK
    -p github.com/Vladimiroff/vec2d -complete
    -buildid 577672012d89431ef32536b66f5254f5f6f49189
    -D _/home/mstoykov/workspace/go/src/github.com/Vladimiroff/vec2d -I $WORK
    -pack ./utils.go ./vector.go

Стойности

var gopher int32 = 2014

CPU Cache

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)
}

Inlining

package main

func double(a, b int) int {
    max := b
    if a > b {
        max = a
    }
    return 2 * max
}

Dead code elimination

func LargeAndComplicatedAction() {
    if false {
        [...]
    }
    [...]
}

Debug checks

func DebugChecks() bool {
    return debugBuildTagIsOn
}

func LargeAndComplicatedAction() {
    if DebugChecks() {
        [...]
    }
    [...]
}

Escape analysis

Кое къде отива в С, примерно?

Толкова е просто. Хайде сега на Go

Stupid Sum

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
}

Cursor

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
}

Компилаторът на Go е комуникативно същество

-> go build -gcflags=-m esc.go
# command-line-arguments
./esc.go:9: can inline Center
./esc.go:14: can inline CenterCursor
./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

Cooperatively scheduled

Да си поговорим за C

Сценарий: Изпълняваме thread на C.

Unless...

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

Рекурсия. Свърши ни паметта на стека.

Как можем да го решим това?

Да, това ще понася по-дълбока рекурсия, но пък ще заделяме повече памет за
АБСОЛЮТНО всяка функция, която тя може да не използва.

Ще знаем точно колко голям стек да заделяме за всяка функция, но този анализ ще
отнема време, което води до бавна компилация с много налучкване или бавно
изпълнение на всяка функция

Адресно пространство

Guard page

Thread stacks and guard pages

Segmented Stacks

Това е начинът, по който Go до версия 1.3 управляваше стековете.

Stack Split

+---------------+
|               |
|   unused      |
|   stack       |
|   space       |
+---------------+
|    Foobar     |
|               |
+---------------+       +---------------+
|               |   +-->|    Foobar     |
|  lessstack    |   |   |               |
+---------------+   |   +---------------+
| Stack info    |---+   | rest of stack |
|               |       |               |
+---------------+       +---------------+

lessstsack

Супер яко, нали?

Но има и проблеми

Stack copying

Канали

Отдолу имплементацията на кратко (и значително опрoстено):
- цикличен буфер за стойностите на канала
- една опашка с писари
- една опашка с четци
- един lock
- един bool дали е затворен
- малко допълнителни неща за по-бързо (грозна) имплементация

Бързи операции

Били са открити някои неща, които се случват МНОГО често. Те са били оптимизирани.

Яки коментари

Поради нужди на имплементацията на Go има едно известно количесто ... 'яки' коментари.
Те са такива защото правят неща които няма как да се направят с другите части на езика и същевремнно лъхат на развалено кисело зеле.

Въпроси?