Hlavní navigace

Programovací jazyk Go a assembler (3.část)

Dnes se již potřetí budeme zabývat použitím assembleru v aplikacích, které jsou z větší části psané v Go. Ukážeme si využití programových smyček, ale i moderních instrukcí použitelných pro urychlení vybraných algoritmů.
Pavel Tišnovský 13. 2. 2020
Doba čtení: 42 minut

Sdílet

11. Výsledky benchmarků

12. Použití „řetězcových“ operací typu REP STOS

13. Opět výsledky benchmarků

14. Plyn až na podlahu: instrukce MOVDQU a VMOVNTDQ

15. Použití knihovny go-memset

16. Zázrak se ovšem nekoná neboli opět benchmarky

17. Malá odbočka na závěr – změna barvy pixelů vysokoúrovňovým kódem

18. Poslední výsledky benchmarků a shrnutí na závěr

19. Repositář s demonstračními příklady

20. Odkazy na Internetu

1. Programovací jazyk Go a assembler (3.část)

Na předchozí články, v nichž jsme si popsali některé vlastnosti poněkud specifického assembleru dodávaného společně se základními nástroji Go, dnes navážeme. Ukážeme si totiž, jak lze urychlit některé základní manipulace s rastrovými obrázky. Z dále popsaných demonstračních příkladů bude patrné, že použití assembleru skutečně může vést k mnohdy velmi výraznému urychlení některých operací, pochopitelně ovšem za cenu zkomplikování a zpomalení vývoje. Je tomu tak z toho důvodu, že překladač programovacího jazyka Go nedokáže (alespoň v jeho současné verzi) aplikovat některé optimalizace a současně samotný jazyk Go v mnoha případech nenabízí vhodnou sémantiku pro popis některých operací (resp. přesněji řečeno to někdy možné je, ovšem za předpokladu použití balíčku unsafe a podobných spíše nízkoúrovňových a potenciálně nebezpečných postupů).

Poznámka: nutno poznamenat, že je dosti nepravděpodobné, že by překladač jazyka Go v dohledné době prováděl některé časově náročné optimalizace (ty jsou mnohdy založeny na trasování spuštěného kódu). Jde to proti filozofii, které se Go drží, tj. snažit se, aby se Go, což je překládaný jazyk, používalo stejně snadno a rychle, jako jazyky interpretované.
Poznámka2: prozatím prosím nečekejte od stále ještě úvodního článku pokročilé triky. K těm se dostaneme příště.

2. Malá rozcvička: interní reprezentace řezů (slices)

Nejdříve si povězme, jakým způsobem se v programovacím jazyce Go pracuje s takzvanými řezy (slices). S řezy jsme se již pochopitelně seznámili, protože se jedná o významný prvek jazyka Go, bez něhož by nebylo možné elegantně pracovat s kolekcemi s měnitelnou kapacitou a počtem uložených prvků.

Interně se jedná o referenci na automaticky vytvořené pole nebo na pole, které je explicitně „nasalámováno“ operací řezu [od:do]. Každý řez je v operační paměti uložen ve formě trojice hodnot (jde o záznam – struct či record):

  1. Ukazatele (reference) na zvolený prvek pole s daty, ke kterým přes řez přistupujeme.
  2. Délky řezu, tj. počtu prvků.
  3. Kapacity řezu (do jaké míry může řez narůstat v důsledku přidávání dalších prvků).

Tato interní struktura řezů s sebou přináší několik zajímavých důsledků. Je totiž možné, aby existovalo větší množství řezů ukazujících na obecně různé prvky jediného pole. Pokud nyní změníme prvek v jednom řezu, znamená to, že se vlastně modifikuje obsah původního pole a i ostatní řezy nový prvek uvidí. Co je však užitečnější – s řezy jako s datovým typem se velmi snadno pracuje; řezy mohou být předávány do funkcí, vráceny z funkcí atd.

Podívejme se ovšem na řezy z pohledu programátora, který by řezy používal v assembleru. Řez je i při tomto pohledu stále tvořen trojicí hodnot – ukazatele na první pohled, kapacity a aktuálně využité kapacity řezu (tedy jeho délky). Tyto hodnoty jsou do volané funkce předány jako trojice, protože se řez předává hodnotou, ostatně podobně, jako další datové typy jazyka Go. V případě, že se používá 64bitová platforma (tedy x86–64/AMD64 či AArch64), má ukazatel šířku 64 bitů, kapacita je uložena v 32 bitech a délka taktéž v 32 bitech.

Způsob předání zmíněných tří hodnot si otestujeme na následujícím velmi jednoduchém demonstračním příkladu, v němž je implementována dvojice funkcí, z nichž jedna vrací kapacitu řezu a druhá počet skutečně obsazených prvků. Zdrojový kód tohoto příkladu vypadá následovně:

package main
 
func GetLen(b []byte) int {
        return len(b)
}
 
func GetCap(b []byte) int {
        return cap(b)
}
 
func main() {
        var x []byte = []byte{1, 2, 3}
        println(GetLen(x))
        println(GetCap(x))
}

3. Předávání řezů v zásobníkovém rámci do volaných funkcí

Zajímat nás nyní bude způsob volání těchto funkcí i princip předávání tří hodnot popisujících řez. Demonstrační příklad tedy přeložíme, ovšem takovým způsobem, aby nedošlo k inliningu volaných funkcí:

$ go build -gcflags '-l' slices.go

Nyní se již můžeme podívat na sekvenci instrukcí, do nichž se přeložily zdrojové kódy funkcí GetLen a GetCap. Pro získání sekvence instrukcí v lidsky čitelné podobě použijeme známý a již popsaný nástroj nazvaný objdump:

$ go tool objdump -S -s GetLen ./slices 
 
TEXT main.GetLen(SB) /home/ptisnovs/src/go-root/article_56/slices.go
        return len(b)
  0x452330              488b442410              MOVQ 0x10(SP), AX
  0x452335              4889442420              MOVQ AX, 0x20(SP)
  0x45233a              c3                      RET

a:

$ go tool objdump -S -s GetCap ./slices 
TEXT main.GetCap(SB) /home/ptisnovs/src/go-root/article_56/slices.go
        return cap(b)
  0x452340              488b442418              MOVQ 0x18(SP), AX
  0x452345              4889442420              MOVQ AX, 0x20(SP)
  0x45234a              c3                      RET

Z disassemblovaného kódu obou funkcí lze vydedukovat způsob předávání struktury popisující řez:

  1. Ukazatel na první prvek v řezu má šířku 64 bitů a je předán na zásobníkovém rámci na offsetu 8 (osm bajtů zabírá návratová adresa).
  2. Aktuálně zapsaný počet prvků řezu má šířku 32 bitů a je předán na zásobníkovém rámci na offsetu 16 (8+8).
  3. Využitelná kapacita řezu má šířku taktéž 32 bitů a je předán na zásobníkovém rámci na offsetu 24 (8+8+4+4 align).
Poznámka: znovu zopakujme, že výše uvedené informace platí pro 64bitovou platformu x86–64. Na jiných platformách bude předávání vypadat odlišně (například se nepoužije zásobníkový rámec) nebo bude ukazatel pouze 32bitový.

4. Vyplnění obrázku konstantní barvou

Nyní, když již víme, jak se pracuje s řezy, se můžeme podívat na další demonstrační příklad. Ten slouží k vytvoření plnobarevného rastrového obrázku o rozlišení 256×256 pixelů, který je následně vyplněn bílou neprůhlednou barvou a uložen do externího souboru ve formátu PNG. Připomeňme si, že u plnobarevných obrázků (RGBA) je barva tvořena čtveřicí hodnot red, green, blue a alpha, přičemž u alfa kanálu (alpha) značí 0 plnou průhlednost zatímco 255 úplnou neprůhlednost (ovšem v jiných jazycích a knihovnách je tomu přesně naopak).

Rastrový obrázek se vytvoří konstruktorem:

destinationImage := image.NewRGBA(image.Rect(0, 0, 256, 256))

Ve výchozím stavu má obrázek všechny pixely černé a současně průhledné. Budeme ho tedy muset vyplnit. Využijeme přitom toho faktu, že hodnoty všech pixelů jsou uloženy v kontinuálním řezu typu []byte, tj. k pixelům a jejich barvám je možné přistupovat na dosti nízké úrovni (což je ostatně pro mnoho algoritmů jen dobře). Vyplnění celého obrázku tedy můžeme realizovat následující funkcí:

func fillPixels(pixels []uint8) {
        for i := 0; i < len(pixels); i++ {
                pixels[i] = 255
        }
}

U této funkce se na chvíli zastavme a zjistěme, jestli je skutečně legální takto k pixelům přistupovat. Struktura popisující celobarevný obrázek totiž vypadá následovně:

// RGBA64 is an in-memory image whose At method returns color.RGBA64 values.
type RGBA64 struct {
        // Pix holds the image's pixels, in R, G, B, A order and big-endian format. The pixel at
        // (x, y) starts at Pix[(y-Rect.Min.Y)*Stride + (x-Rect.Min.X)*8].
        Pix []uint8
        // Stride is the Pix stride (in bytes) between vertically adjacent pixels.
        Stride int
        // Rect is the image's bounds.
        Rect Rectangle
}

Pokud znáte nějaké další (nízkoúrovňové) knihovny pro práci s rastrovými obrázky, například původní SDL verze 1, možná budete předpokládat, že existence atributu Stride znamená, že jednotlivé obrazové řádky nemusí být uloženy ihned za sebou. Jinými slovy – mezi posledním pixelem na jednom řádku a prvním pixelem na následujícím řádku může být nevyužité místo. Ve skutečnosti tomu však v tomto případě není, o čemž se snadno přesvědčíme pohledem do zdrojového kódu samotného balíčku image:

// NewRGBA returns a new RGBA image with the given bounds.
func NewRGBA(r Rectangle) *RGBA {
        w, h := r.Dx(), r.Dy()
        buf := make([]uint8, 4*w*h)
        return &RGBA{buf, 4 * w, r}
}

Úplný zdrojový kód dnešního druhého demonstračního příkladu vypadá takto:

package main
 
import (
        "image"
        "image/png"
        "log"
        "os"
)
 
const DestinationImageFileName = "empty.png"
 
func saveImage(filename string, img image.Image) error {
        outfile, err := os.Create(filename)
        if err != nil {
                return err
        }
        defer outfile.Close()
 
        png.Encode(outfile, img)
        return nil
}
 
func fillPixels(pixels []uint8) {
        for i := 0; i < len(pixels); i++ {
                pixels[i] = 255
        }
}
 
func main() {
        destinationImage := image.NewRGBA(image.Rect(0, 0, 256, 256))
 
        fillPixels(destinationImage.Pix)
 
        err := saveImage(DestinationImageFileName, destinationImage)
        if err != nil {
                log.Fatal(err)
        }
}

Funkci pro vyplnění všech pixelů rastrového obrázku tedy máme naprogramovanou a díky tomu, že je v ní použit (relativně) nízkoúrovňový výstup je možné předpokládat, že bude přeložena poměrně rozumným způsobem. Můžeme si to ostatně ověřit na optimalizované variantě:

TEXT main.fillPixels(SB) /home/ptisnovs/src/go-root/article_56/01_no_op_filter.go
        for i := 0; i < len(pixels); i++ {
  0x4b87b0              488b442410              MOVQ 0x10(SP), AX
  0x4b87b5              488b4c2408              MOVQ 0x8(SP), CX
  0x4b87ba              31d2                    XORL DX, DX
  0x4b87bc              eb07                    JMP 0x4b87c5
                pixels[i] = 255
  0x4b87be              c60411ff                MOVB $0xff, 0(CX)(DX*1)
        for i := 0; i < len(pixels); i++ {
  0x4b87c2              48ffc2                  INCQ DX
  0x4b87c5              4839c2                  CMPQ AX, DX
  0x4b87c8              7cf4                    JL 0x4b87be
  0x4b87ca              c3                      RET

Z předchozího kódu je patrné, že se pixely vyplňují po jednotlivých bajtech, což pravděpodobně nebude nejrychlejší řešení.

Poznámka: zajímavé je, že pokud bychom řez vyplňovali nulami, použil by překladač velmi rychlou variantu, k níž se dostaneme v dalším textu.

5. Benchmark pro funkci vyplňující rastrový obrázek

O tom, jak rychlé či naopak pomalé je vyplňování obrázku realizované v dnešním druhém demonstračním příkladu, se přesvědčíme prakticky, a to konkrétně vytvořením vhodného benchmarku. Podporou benchmarků jsme se prozatím v tomto seriálu nezabývali do větší hloubky, ovšem pro účely dnešního článku postačuje vědět, že se jedná o součást standardního testovacího frameworku programovacího jazyka Go a že v implementaci benchmarku (funkce Run) typicky používáme smyčku prováděnou od 0 do b.N, přičemž ono N je do benchmarku předáváno samotným testovacím frameworkem.

Náš benchmark bude relativně jednoduchý – postupně zkonstruuje obrázky se zvětšujícím se rozlišením a bude měřit, jak dlouho trvá jejich vyplnění konstantní barvou:

package main
 
import (
        "fmt"
        "image"
        "testing"
)
 
var sizes = []int{32, 128, 256, 512, 1024, 2048}
 
func BenchmarkFillPixels(b *testing.B) {
        for _, size := range sizes {
                sizeStr := fmt.Sprintf("%dx%d", size, size)
                b.Run(sizeStr, func(b *testing.B) {
                        destinationImage := image.NewRGBA(image.Rect(0, 0, size, size))
 
                        b.ResetTimer()
 
                        for i := 0; i < b.N; i++ {
                                fillPixels(destinationImage.Pix)
                        }
                })
        }
}

Takto vytvořený benchmark se spustí příkazem:

20:20 $ go test -bench=.

Z výsledků benchmarku je patrné, jak dlouho trvá jedno vyplnění pro obrázek zvolené velikosti i kolik vyplnění bylo za zvolený časový rámec provedeno:

goos: linux
goarch: amd64
BenchmarkFillPixels/32x32-8               985530              1194 ns/op
BenchmarkFillPixels/128x128-8              57682             20843 ns/op
BenchmarkFillPixels/256x256-8              14215             83921 ns/op
BenchmarkFillPixels/512x512-8               3368            337089 ns/op
BenchmarkFillPixels/1024x1024-8              866           1369216 ns/op
BenchmarkFillPixels/2048x2048-8              218           5427221 ns/op
PASS
ok      _/home/ptisnovs/src/go-root/article_56/01_empty_image_go        9.810s

Pro zajímavost ještě spustíme benchmark pro kód, který nebyl optimalizován:

20:22 $ go test -gcflags '-l' -bench=.
 
goos: linux
goarch: amd64
BenchmarkFillPixels/32x32-8               493587              2390 ns/op
BenchmarkFillPixels/128x128-8              31555             38150 ns/op
BenchmarkFillPixels/256x256-8               7726            152297 ns/op
BenchmarkFillPixels/512x512-8               1915            609991 ns/op
BenchmarkFillPixels/1024x1024-8              482           2444177 ns/op
BenchmarkFillPixels/2048x2048-8              120           9837822 ns/op
PASS
ok      _/home/ptisnovs/src/go-root/article_56/01_empty_image_go        9.372s

6. Naivní implementace vyplňování v assembleru

Nyní si již můžeme ukázat použití assembleru. Nejdříve nepatrně upravíme samotný zdrojový kód příkladu, v němž vynecháme tělo funkce fillPixels:

package main
 
import (
        "image"
        "image/png"
        "log"
        "os"
)
 
const DestinationImageFileName = "empty.png"
 
func saveImage(filename string, img image.Image) error {
        outfile, err := os.Create(filename)
        if err != nil {
                return err
        }
        defer outfile.Close()
 
        png.Encode(outfile, img)
        return nil
}
 
func fillPixels(pixels []uint8)
 
func main() {
        destinationImage := image.NewRGBA(image.Rect(0, 0, 256, 256))
 
        fillPixels(destinationImage.Pix)
 
        err := saveImage(DestinationImageFileName, destinationImage)
        if err != nil {
                log.Fatal(err)
        }
}

Chybějící tělo funkce bude vytvořeno v assembleru. První varianta je napsána dosti naivním způsobem a je odvozena z kódu, který by generoval samotný překladač programovacího jazyka Go:

TEXT ·fillPixels(SB),7,$0
        MOVQ pix_data+0(FP), CX  // adresa
        MOVQ pix_len+8(FP), AX   // delka
        XORL DX, DX              // pocitadlo
        JMP  NEXT                // reseni problemu len(pixels)==0
 
LOOP:
        MOVB $0xff, 0(CX)(DX*1)  // zapis bajtu
        INCQ DX                  // zvyseni hodnoty pocitadla
NEXT:   CMPQ DX, AX              // porovnani s delkou rezu
        JL LOOP                  // pocitadlo mensi? ok, skok
        RET

Povšimněte si, jak tato funkce pracuje: je v ní použito počitadlo realizované registrem EDX, který se používá i pro adresování pixelu. Dále zde můžeme vidět registr RCX, do něhož se uložila adresa prvního prvku v řezu (rozlišení, který registr se použije, je patrné ze suffixu instrukce). A konečně se do registru RAX uložil počet prvků v řezu, což v našem případě konkrétně znamená počet pixelů vynásobených čtyřmi. V programové smyčce se kontroluje, zda již počitadlo (postupně zvyšované o jedničku) nedosáhlo počtu prvků v řezu. Pokud tomu tak je, je funkce ukončena, jinak je proveden skok na začátek smyčky (JL = Jump if Less than). Navíc je funkce navržena takovým způsobem, že pracuje korektně i za předpokladu, že má obrázek nulovou velikost a tedy že neobsahuje žádné pixely (skok doprostřed smyčky na začátku).

7. Výsledky benchmarků

Relativně krátká funkce naprogramovaná v assembleru s velkou pravděpodobností nebude příliš rychlá, protože k pixelům stále přistupujeme po jednotlivých bajtech (zápis instrukcí MOVB). O tom, jak dobře či špatně jsme na tom v porovnání s původním příkladem, se opět přesvědčíme benchmarkem:

20:23 $ go test -bench=.
 
goos: linux
goarch: amd64
pkg: empty_image
BenchmarkFillPixels/32x32-8               422341              2420 ns/op
BenchmarkFillPixels/128x128-8              31546             38293 ns/op
BenchmarkFillPixels/256x256-8               7755            152968 ns/op
BenchmarkFillPixels/512x512-8               1930            608001 ns/op
BenchmarkFillPixels/1024x1024-8              483           2444272 ns/op
BenchmarkFillPixels/2048x2048-8              120           9858778 ns/op
PASS
ok      empty_image     8.292s
Poznámka: dosažené počty operací, resp. naopak jejich rychlosti nás prozatím příliš neohromí, ale pokračujme dále.

8. Reorganizace vnitřní smyčky naprogramované v assembleru

V případě, že budeme předpokládat, že vyplňovat se bude obrázek o nenulové velikosti, je možné programovou smyčku vytvořenou v assembleru přepsat a nepatrně ji tak urychlit. Použijeme zde dosti typickou kombinaci instrukcí DEC+JNZ. Upravená varianta vypadá následovně:

TEXT ·fillPixels(SB),7,$0
        MOVQ pix_data+0(FP), CX  // adresa
        MOVQ pix_len+8(FP), AX   // delka
        XORL DX, DX              // offset
 
LOOP:
        MOVB $0xff, 0(CX)(DX*1)  // zapis bajtu
        INCQ DX                  // zvyseni hodnoty offsetu
        DECQ AX                  // zmenseni pocitadla
        JNZ LOOP                 // pocitadlo vetsi nez 0? ok, skok
        RET
Poznámka: stále se jedná o naivně pojaté řešení, které je založeno na optimalizacích provedených na nejnižší úrovni bez přemýšlení o tom, že s pixely lze pracovat i jinak.

9. Výsledky benchmarků

Po spuštění benchmarků je patrné nepatrné urychlení, které ovšem bylo vykoupeno potenciální nebezpečností implementované funkce:

20:24 $ go test -bench=.
 
goos: linux
goarch: amd64
pkg: empty_image
BenchmarkFillPixels/32x32-8               498303              2380 ns/op
BenchmarkFillPixels/128x128-8              31570             38094 ns/op
BenchmarkFillPixels/256x256-8               7450            152076 ns/op
BenchmarkFillPixels/512x512-8               1927            610624 ns/op
BenchmarkFillPixels/1024x1024-8              478           2522056 ns/op
BenchmarkFillPixels/2048x2048-8              117           9990900 ns/op
PASS
ok      empty_image     9.467s

10. Vyplňování po čtyřbajtových slovech

Další úprava celé programové smyčky zapsané v assembleru je již mnohem významnější. Použijeme zde zápis celého pixelu nikoli po bajtech, ale po celých čtyřbajtových slovech. Vzhledem k tomu, že každý pixel má ve formátu RGBA šířku právě čtyř bajtů, je výpočet jednoduchý a nemusíme sledovat, kolik pixelů bitmapa obsahuje (zda je počet lichý, sudý, dělitelný čtyřmi atd. atd.). Jedna z možných variant smyčky ve formě, jak ji generují některé překladače (nikoli překladač jazyka Go!) vypadá takto:

TEXT ·fillPixels(SB),7,$0
        MOVQ pix_data+0(FP), CX  // adresa
        MOVQ pix_len+8(FP), AX   // delka
        XORL DX, DX              // offset
        MOVD $0xffffffff, BX     // zapisovana barva pixelu (RGBA)
 
LOOP:
        MOVD BX, 0(CX)(DX*1)     // zapis bajtu
        ADDQ $4, DX              // zvyseni hodnoty offsetu
        SUBQ $4, AX              // zmenseni pocitadla
        JNZ LOOP                 // pocitadlo vetsi nez 0? ok, skok
        RET

Povšimněte si zejména toho že se provede čtyřikrát méně operací. Počitadlo by bylo možné hned na začátku vydělit čtyřmi (což uděláme v dalším příkladu); taktéž by bylo možné smyčku nepatrně přeorganizovat a použít odlišný přístup ke kontrole, zda se již došlo nakonec celé smyčky (poslední iterace), ovšem výsledný čas běhu funkce zůstává přibližně stejný.

11. Výsledky benchmarků

Na výsledcích benchmarku je jasně patrný výkonnostní rozdíl mezi zápisem do rastrového obrázku po čtyřech bajtech v porovnání se zápisem po bajtech:

20:24 $ go test -bench=.
 
goos: linux
goarch: amd64
pkg: empty_image
BenchmarkFillPixels/32x32-8              3501494               324 ns/op
BenchmarkFillPixels/128x128-8             218844              5479 ns/op
BenchmarkFillPixels/256x256-8              52108             23002 ns/op
BenchmarkFillPixels/512x512-8              12303             97159 ns/op
BenchmarkFillPixels/1024x1024-8             3022            390965 ns/op
BenchmarkFillPixels/2048x2048-8              662           1746510 ns/op
PASS
ok      empty_image     8.928s

Dosažené zrychlení (pochopitelně platné pouze pro počítač s i5, na kterém testy běžely) je rovno:

9990900/1746510=5,72

Tento výsledek je zajímavý a možná neočekávaný, protože by se mělo jednat o přibližně čtyřnásobné urychlení. Ukazuje se, že na 64bitové (i 32bitové) platformě může být manipulace s jednotlivými bajty hodně pomalou operací.

Poznámka: toto je nutné mít na paměti i při dalším zpracování polí či řezů bajtů. Jedná se totiž o zcela obvyklé operace, zejména v případě webových serverů s REST API, u nichž se neustále pracuje s řetězci, provádí se deserializace JSONů a další podobné operace.

12. Použití „řetězcových“ operací typu REP STOS

Na platformě x86 i x86–64 jsou již od samého začátku existence této řady mikroprocesorů dostupné „řetězcové“ operace typu MOVS, CMPS, LODS a STOS. Tyto instrukce provádí čtení či zápis do paměťového místa současně se změnou offsetu; jejich typické použití je při kopiích polí, vyplňování bloků paměti atd. Instrukce LODS a STOS navíc pracují s různou šířkou dat – od bajtů přes 16bitová slova až po slova 64bitová (pochopitelně na nových procesorech):

Instrukce Význam v 64bitovém režimu
STOSB ulož AL na adresu v registru RDI nebo EDI, zvyš/sniž adresu o 1
STOSW ulož AX na adresu v registru RDI nebo EDI, zvyš/sniž adresu o 2
STOSD ulož EAX na adresu v registru RDI nebo EDI, zvyš/sniž adresu o 4
STOSQ ulož RAX na adresu v registru RDI nebo EDI, zvyš/sniž adresu o 8

Navíc se před tyto instrukce může vložit prefixová instrukce REP, která znamená „opakuj CX-krát“, resp. „opakuj ECX-krát“.

Naši smyčku tedy můžeme přepsat takovým způsobem, že se použije:

REP STOSD

Ovšem v assembleru programovacího jazyka Go se používá jiný způsob zápisu, kdy prefix figuruje jako samostatná instrukce a navíc se namísto STOSD použije STOSL (L=long). Nesmíme zapomenout na nastavení směru zápisu, což zajišťuje instrukce CLD (pokud ji nepoužijete, bude se zapisovat opačným směrem, což povede k pádu programu):

TEXT ·fillPixels(SB),7,$0
        MOVQ pix_data+0(FP), DI  // adresa
        MOVQ pix_len+8(FP), CX   // delka
        SHRQ $2, CX              // delime ctyrmi - protoze se zapisuji ctyri bajty soucasne
        MOVD $0xffffffff, AX     // zapisovana barva pixelu (RGBA)
 
        CLD                      // smer zapisu
        REP                      // opakovani CX-krat
        STOSL                    // zapis ctyrbajtoveho slova
        RET
Poznámka: více informací o instrukcích typu STOS naleznete například na stránce https://www.felixcloutier­.com/x86/stos:stosb:stosw:stos­d:stosq.

13. Opět výsledky benchmarků

Na výsledcích benchmarku je nyní patrné, že se prozatím jedná o nejrychlejší variantu smyčky pro vyplnění rastrového obrázku:

20:24 $ go test -bench=.
 
goos: linux
goarch: amd64
pkg: empty_image
BenchmarkFillPixels/32x32-8             22048845                47.9 ns/op
BenchmarkFillPixels/128x128-8            1239043               954 ns/op
BenchmarkFillPixels/256x256-8             263758              4494 ns/op
BenchmarkFillPixels/512x512-8              58672             20276 ns/op
BenchmarkFillPixels/1024x1024-8            14119             85268 ns/op
BenchmarkFillPixels/2048x2048-8             1090           1081754 ns/op
PASS
ok      empty_image     9.272s

14. Plyn až na podlahu: instrukce MOVDQU a VMOVNTDQ

Prozatím jsme pracovali „pouze“ se čtyřmi bajty současně, ovšem moderní 64bitové mikroprocesory nabízí i další možnosti, které v co největší míře využívají 64bitovou sběrnici. Jedná se o instrukce MOVDQU, popř. VMOVNTDQ (ta existuje ve více variantách). Podrobnější informace o těchto instrukcích lze nalézt například na stránce https://www.felixcloutier­.com/x86/movdqu:vmovdqu8:vmov­dqu16:vmovdqu32:vmovdqu64, popř. na https://www.felixcloutier­.com/x86/movntdq (další varianta).

Poznámka: instrukce začínající písmenem „V“ pracují s „vektorovými“ registry, tj. jedná se o nějakou formu SIMD operace. Naopak „U“ na konci znamená „unaligned“, tj. bude možné data přenášet z jakékoli adresy (za což obecně zaplatíme delším časem běhu).

Použití těchto instrukcí by mělo vést ke zdaleka nejrychlejšímu kódu, ovšem samotná implementace smyčky již bude mnohem složitější, neboť bude nutné zajistit, co se stane v případě, kdy počet zapisovaných dat nebude roven osmi, kdy nebudou data zarovnána atd. Ostatně se můžeme podívat, jak je tato problematika řešena v samotném jazyku Go při vyplňování polí nulami (ovšem jen nulami) – https://golang.org/src/run­time/memclr_amd64.s.

Poznámka: ve výše uvedeném – ručně optimalizovaném – kódu si povšimněte dosti masivního rozbalení smyčky, protože právě operace podmíněného skoku je obecně dosti pomalá, protože obecně porušuje tok instrukcí v instrukční pipeline.

15. Použití knihovny go-memset

Namísto zápisu optimalizované smyčky využijeme existující a řádně otestovaný kód, jenž lze nalézt na adrese https://github.com/tmthrgd/go-memset/blob/master/memset_amd64.s. Tento kód je součástí minibalíčku nazvaného go-memset, který použijeme v dalším demonstračním příkladu.

Balíček nejdříve nainstalujeme, a to klasicky:

$ go get github.com/tmthrgd/go-memset

A aplikujeme ho:

func fillPixels(pixels []uint8) {
        memset.Memset(pixels, 0xff)
}

O případné optimalizace, rozbalení smyčky atd. by se měl postarat kód ve funkci Memset:

package main
 
import (
        memset "github.com/tmthrgd/go-memset"
        "image"
        "image/png"
        "log"
        "os"
)
 
const DestinationImageFileName = "empty.png"
 
func saveImage(filename string, img image.Image) error {
        outfile, err := os.Create(filename)
        if err != nil {
                return err
        }
        defer outfile.Close()
 
        png.Encode(outfile, img)
        return nil
}
 
func fillPixels(pixels []uint8) {
        memset.Memset(pixels, 0xff)
}
 
func main() {
        destinationImage := image.NewRGBA(image.Rect(0, 0, 256, 256))
 
        fillPixels(destinationImage.Pix)
 
        err := saveImage(DestinationImageFileName, destinationImage)
        if err != nil {
                log.Fatal(err)
        }
}

16. Zázrak se ovšem nekoná neboli opět benchmarky

Na výsledku benchmarků je patrné, že se (kupodivu) příliš velké urychlení oproti REP STOSD nekoná, což je poněkud překvapivé:

$ go test -bench=.
 
goos: linux
goarch: amd64
pkg: empty_image
BenchmarkFillPixels/32x32-8             26100108                42.2 ns/op
BenchmarkFillPixels/128x128-8            1221976               986 ns/op
BenchmarkFillPixels/256x256-8             207972              5618 ns/op
BenchmarkFillPixels/512x512-8              41854             28681 ns/op
BenchmarkFillPixels/1024x1024-8            10000            115203 ns/op
BenchmarkFillPixels/2048x2048-8             1100           1077157 ns/op
PASS
ok      empty_image     8.542s
Poznámka: bylo by vhodné a poučné tyto benchmarky spustit i na serverových mikroprocesorech, jak od AMD, tak i od společnosti Intel.

17. Malá odbočka na závěr – změna barvy pixelů vysokoúrovňovým kódem

Na závěr se ještě podívejme na alternativní způsob vybarvení celého rastrového obrázku. Ten je založen na použití datové struktury color.RGBA, která se může předat do metody Image.SetRGBA. Jedná se o způsob, který sice nevyžaduje nízkoúrovňový přístup k obsahu rastrového obrázku, ovšem dá se předpokládat, že bude (mnohem) pomalejší. Ve funkci nyní předpočítáme barvu pixelu (jedinkrát) a posléze ji použijeme ve vnořené programové smyčce:

func fillPixels(img *image.RGBA) {
        clr := color.RGBA{255, 255, 255, 255}
        bounds := img.Bounds()
        width, height := bounds.Max.X, bounds.Max.Y
        for y := 0; y < height; y++ {
                for x := 0; x < width; x++ {
                        img.SetRGBA(x, y, clr)
                }
        }
}

Takto upravený demonstrační příklad naleznete na adrese https://github.com/tisnik/go-root/blob/master/article56/07_em­pty_image_high_level/empty_i­mage.go:

package main
 
import (
        "image"
        "image/color"
        "image/png"
        "log"
        "os"
)
 
const DestinationImageFileName = "empty.png"
 
func saveImage(filename string, img image.Image) error {
        outfile, err := os.Create(filename)
        if err != nil {
                return err
        }
        defer outfile.Close()

        png.Encode(outfile, img)
        return nil
}
 
func fillPixels(img *image.RGBA) {
        clr := color.RGBA{255, 255, 255, 255}
        bounds := img.Bounds()
        width, height := bounds.Max.X, bounds.Max.Y
        for y := 0; y < height; y++ {
                for x := 0; x < width; x++ {
                        img.SetRGBA(x, y, clr)
                }
        }
}
 
func main() {
        destinationImage := image.NewRGBA(image.Rect(0, 0, 256, 256))
 
        fillPixels(destinationImage)
 
        err := saveImage(DestinationImageFileName, destinationImage)
        if err != nil {
                log.Fatal(err)
        }
}

18. Poslední výsledky benchmarků a shrnutí na závěr

Naposledy se podívejme na výsledky benchmarků, tentokrát pro poslední demonstrační příklad popsaný v předchozí kapitole. Podle očekávání se jedná o nejpomalejší možný způsob:

MIF obecny

21:19 $ go test -bench=.
 
goos: linux
goarch: amd64
BenchmarkFillPixels/32x32-8               198350              5405 ns/op
BenchmarkFillPixels/128x128-8              14533             82587 ns/op
BenchmarkFillPixels/256x256-8               3627            330180 ns/op
BenchmarkFillPixels/512x512-8                910           1299179 ns/op
BenchmarkFillPixels/1024x1024-8              226           5240748 ns/op
BenchmarkFillPixels/2048x2048-8               52          20969468 ns/op
PASS
ok      _/home/ptisnovs/src/go-root/article_56/07_empty_image_high_level        8.557s

Výsledky přepsané do jediné tabulky, konkrétně pro rastrové obrázky o rozlišení 2048×2048 pixelů:

Metoda Čas (ns)
Implementace v Go, optimalizováno  5427221
Implementace v Go, neoptimalizováno  9837822
Naivní implementace v assembleru  9858778
Reorganizace vnitřní smyčky, přístup po bajtech  9990900
Přístup po čtyřbajtových slovech  1746510
Použití řetězcových operací  1081754
Vektorové instrukce  1077157
Vysokoúrovňový přístup 20969468

19. Repositář s demonstračními příklady

Zdrojové kódy všech dnes použitých demonstračních příkladů byly uloženy do Git repositáře, který je dostupný na adrese https://github.com/tisnik/go-root (stále na GitHubu :-). V případě, že nebudete chtít klonovat celý repositář (ten je ovšem – alespoň prozatím – velmi malý, dnes má přibližně šest až sedm megabajtů), můžete namísto toho použít odkazy na jednotlivé demonstrační příklady, které naleznete v následující tabulce:

# Příklad Stručný popis Cesta
1 01_empty_image_go/empty_image.go implementace vyplnění obrázku v Go https://github.com/tisnik/go-root/blob/master/article56/01_em­pty_image_go/empty_image.go
2 01_empty_image_go/empty_image_test.go benchmark ke zdrojovému kódu https://github.com/tisnik/go-root/blob/master/article56/01_em­pty_image_go/empty_image_tes­t.go
       
3 02_empty_image_asm/empty_image.go volání vyplnění obrázku v Go https://github.com/tisnik/go-root/blob/master/article56/02_em­pty_image_asm/empty_image­.go
4 02_empty_image_asm/empty_image_test.go benchmark ke zdrojovému kódu https://github.com/tisnik/go-root/blob/master/article56/02_em­pty_image_asm/empty_image_tes­t.go
5 02_empty_image_asm/fill_pixels_amd64.s implementace vyplnění obrázku v assembleru (po bajtech) https://github.com/tisnik/go-root/blob/master/article56/02_em­pty_image_asm/fill_pixels_am­d64.s
       
6 03_empty_image_asm/empty_image.go implementace vyplnění obrázku v Go https://github.com/tisnik/go-root/blob/master/article56/03_em­pty_image_asm/empty_image­.go
7 03_empty_image_asm/empty_image_test.go benchmark ke zdrojovému kódu https://github.com/tisnik/go-root/blob/master/article56/03_em­pty_image_asm/empty_image_tes­t.go
8 03_empty_image_asm/fill_pixels_amd64.s implementace vyplnění obrázku v assembleru (po bajtech) https://github.com/tisnik/go-root/blob/master/article56/03_em­pty_image_asm/fill_pixels_am­d64.s
       
9 04_empty_image_asm/empty_image.go implementace vyplnění obrázku v Go https://github.com/tisnik/go-root/blob/master/article56/04_em­pty_image_asm/empty_image­.go
10 04_empty_image_asm/empty_image_test.go benchmark ke zdrojovému kódu https://github.com/tisnik/go-root/blob/master/article56/04_em­pty_image_asm/empty_image_tes­t.go
11 04_empty_image_asm/fill_pixels_amd64.s implementace vyplnění obrázku v assembleru (po 4 bajtech) https://github.com/tisnik/go-root/blob/master/article56/04_em­pty_image_asm/fill_pixels_am­d64.s
       
12 05_empty_image_asm/empty_image.go implementace vyplnění obrázku v Go https://github.com/tisnik/go-root/blob/master/article56/05_em­pty_image_asm/empty_image­.go
13 05_empty_image_asm/empty_image_test.go benchmark ke zdrojovému kódu https://github.com/tisnik/go-root/blob/master/article56/05_em­pty_image_asm/empty_image_tes­t.go
14 05_empty_image_asm/fill_pixels_amd64.s implementace vyplnění obrázku v assembleru (rep stosq) https://github.com/tisnik/go-root/blob/master/article56/05_em­pty_image_asm/fill_pixels_am­d64.s
       
15 06_empty_image_go_memset/empty_image.go implementace vyplnění obrázku přes knihovnu go-memset https://github.com/tisnik/go-root/blob/master/article56/06_em­pty_image_go_memset/empty_i­mage.go
16 06_empty_image_go_memset/em­pty_image_test.go benchmark ke zdrojovému kódu https://github.com/tisnik/go-root/blob/master/article56/06_em­pty_image_go_memset/empty_i­mage_test.go
       
17 slices.go práce s řezy https://github.com/tisnik/go-root/blob/master/article56/slices.go

20. Odkazy na Internetu

  1. Golang Guide: A List of Top Golang Frameworks, IDEs & Tools
    https://blog.intelligentbe­e.com/2017/08/14/golang-guide-list-top-golang-frameworks-ides-tools/
  2. Repositář projektu Gift
    https://github.com/disinte­gration/gift
  3. Dokumentace k projektu Gift
    https://godoc.org/github.com/di­sintegration/gift
  4. Online x86 / x64 Assembler and Disassembler
    https://defuse.ca/online-x86-assembler.htm#disassembly2
  5. The Design of the Go Assembler
    https://talks.golang.org/2016/as­m.slide#1
  6. A Quick Guide to Go's Assembler
    https://golang.org/doc/asm
  7. AssemblyPolicy
    https://github.com/golang/go/wi­ki/AssemblyPolicy
  8. Geohash in Golang Assembly
    https://mmcloughlin.com/posts/geohash-assembly
  9. Command objdump
    https://golang.org/cmd/objdump/
  10. Assembly
    https://goroutines.com/asm
  11. Go & Assembly
    http://www.doxsey.net/blog/go-and-assembly
  12. A Foray Into Go Assembly Programming
    https://blog.sgmansfield.com/2017/04/a-foray-into-go-assembly-programming/
  13. Golang Capturing log.Println And fmt.Println Output
    https://medium.com/@hau12a1/golang-capturing-log-println-and-fmt-println-output-770209c791b4
  14. Stránka projektu plotly
    https://plot.ly/
  15. Plotly JavaScript Open Source Graphing Library
    https://plot.ly/javascript/
  16. Domain coloring
    https://en.wikipedia.org/wi­ki/Domain_coloring
  17. Michael Fogleman's projects
    https://www.michaelfogleman­.com/projects/tagged/grap­hics/
  18. Color Graphs of Complex Functions
    https://web.archive.org/web/20120511021419/htt­p://w.american.edu/cas/mat­hstat/lcrone/ComplexPlot.html
  19. A Gallery of Complex Functions
    http://wismuth.com/complex/ga­llery.html
  20. package glot
    https://godoc.org/github.com/A­rafatk/glot
  21. Gnuplotting: Output terminals
    http://www.gnuplotting.org/output-terminals/
  22. Introducing Glot the plotting library for Golang
    https://medium.com/@Arafat­./introducing-glot-the-plotting-library-for-golang-3133399948a1
  23. Introducing Glot the plotting library for Golang
    https://blog.gopheracademy.com/advent-2018/introducing-glot/
  24. Glot is a plotting library for Golang built on top of gnuplot
    https://github.com/Arafatk/glot
  25. Example plots (gonum/plot)
    https://github.com/gonum/plot/wi­ki/Example-plots
  26. A repository for plotting and visualizing data (gonum/plot)
    https://github.com/gonum/plot
  27. golang library to make https://chartjs.org/ plots
    https://github.com/brentp/go-chartjs
  28. Gorilla REPL: interaktivní prostředí pro programovací jazyk Clojure
    https://www.root.cz/clanky/gorilla-repl-interaktivni-prostredi-pro-programovaci-jazyk-clojure/
  29. The Gonum Numerical Computing Package
    https://www.gonum.org/pos­t/introtogonum/
  30. Gomacro na GitHubu
    https://github.com/cosmos72/gomacro
  31. gophernotes – Use Go in Jupyter notebooks and nteract
    https://github.com/gopher­data/gophernotes
  32. gonum
    https://github.com/gonum
  33. go-gota/gota – DataFrames and data wrangling in Go (Golang)
    https://porter.io/github.com/go-gota/gota
  34. A repository for plotting and visualizing data
    https://github.com/gonum/plot
  35. Gonum Numerical Packages
    https://www.gonum.org/
  36. Stránky projektu MinIO
    https://min.io/
  37. MinIO Quickstart Guide
    https://docs.min.io/docs/minio-quickstart-guide.html
  38. MinIO Go Client API Reference
    https://docs.min.io/docs/golang-client-api-reference
  39. MinIO Python Client API Reference
    https://docs.min.io/docs/python-client-api-reference.html
  40. Performance at Scale: MinIO Pushes Past 1.4 terabits per second with 256 NVMe Drives
    https://blog.min.io/performance-at-scale-minio-pushes-past-1–3-terabits-per-second-with-256-nvme-drives/
  41. Benchmarking MinIO vs. AWS S3 for Apache Spark
    https://blog.min.io/benchmarking-apache-spark-vs-aws-s3/
  42. MinIO Client Quickstart Guide
    https://docs.min.io/docs/minio-client-quickstart-guide.html
  43. Analýza kvality zdrojových kódů Minia
    https://goreportcard.com/re­port/github.com/minio/minio
  44. This is MinIO
    https://www.youtube.com/wat­ch?v=vF0lQh0XOCs
  45. Running MinIO Standalone
    https://www.youtube.com/wat­ch?v=dIQsPCHvHoM
  46. „Amazon S3 Compatible Storage in Kubernetes“ – Rob Girard, Principal Tech Marketing Engineer, Minio
    https://www.youtube.com/wat­ch?v=wlpn8K0jJ4U
  47. Ginkgo
    http://onsi.github.io/ginkgo/
  48. Gomega
    https://onsi.github.io/gomega/
  49. Ginkgo's Preferred Matcher Library na GitHubu
    https://github.com/onsi/gomega/
  50. Provided Matchers
    http://onsi.github.io/gomega/#provided-matchers
  51. Dokumentace k balíčku goexpect
    https://godoc.org/github.com/go­ogle/goexpect
  52. Balíček goexpect
    https://github.com/google/goexpect
  53. Balíček go-expect
    https://github.com/Netflix/go-expect
  54. Balíček gexpect
    https://github.com/Thomas­Rooney/gexpect
  55. Expect (originál naprogramovaný v TCL)
    https://core.tcl-lang.org/expect/index
  56. Expect (Wikipedia)
    https://en.wikipedia.org/wiki/Expect
  57. Pexpect
    https://pexpect.readthedoc­s.io/en/stable/
  58. Golang SSH Client: Multiple Commands, Crypto & Goexpect Examples
    http://networkbit.ch/golang-ssh-client/
  59. goblin na GitHubu
    https://github.com/franela/goblin
  60. Mocha framework
    https://mochajs.org/
  61. frisby na GitHubu
    https://github.com/verdverm/frisby
  62. package frisby
    https://godoc.org/github.com/ver­dverm/frisby
  63. Frisby alternatives and similar packages (generováno)
    https://go.libhunt.com/frisby-alternatives
  64. Cucumber for golang
    https://github.com/DATA-DOG/godog
  65. How to Use Godog for Behavior-driven Development in Go
    https://semaphoreci.com/com­munity/tutorials/how-to-use-godog-for-behavior-driven-development-in-go
  66. Comparative Analysis Of GoLang Testing Frameworks
    https://www.slideshare.net/Dushy­antBhalgami/comparative-analysis-of-golang-testing-frameworks
  67. A Quick Guide to Testing in Golang
    https://caitiem.com/2016/08/18/a-quick-guide-to-testing-in-golang/
  68. Tom's Obvious, Minimal Language.
    https://github.com/toml-lang/toml
  69. xml.org
    http://www.xml.org/
  70. Soubory .properties
    https://en.wikipedia.org/wi­ki/.properties
  71. Soubory INI
    https://en.wikipedia.org/wi­ki/INI_file
  72. JSON to YAML
    https://www.json2yaml.com/
  73. Data Format Converter
    https://toolkit.site/format.html
  74. Viper na GitHubu
    https://github.com/spf13/viper
  75. GoDotEnv na GitHubu
    https://github.com/joho/godotenv
  76. The fantastic ORM library for Golang
    http://gorm.io/
  77. Dokumentace k balíčku gorilla/mux
    https://godoc.org/github.com/go­rilla/mux
  78. Gorilla web toolkitk
    http://www.gorillatoolkit.org/
  79. Metric types
    https://prometheus.io/doc­s/concepts/metric_types/
  80. Histograms with Prometheus: A Tale of Woe
    http://linuxczar.net/blog/2017/06/15/pro­metheus-histogram-2/
  81. Why are Prometheus histograms cumulative?
    https://www.robustperception.io/why-are-prometheus-histograms-cumulative
  82. Histograms and summaries
    https://prometheus.io/doc­s/practices/histograms/
  83. Instrumenting Golang server in 5 min
    https://medium.com/@gsisi­mogang/instrumenting-golang-server-in-5-min-c1c32489add3
  84. Semantic Import Versioning in Go
    https://www.aaronzhuo.com/semantic-import-versioning-in-go/
  85. Sémantické verzování
    https://semver.org/
  86. Getting started with Go modules
    https://medium.com/@fonse­ka.live/getting-started-with-go-modules-b3dac652066d
  87. Create projects independent of $GOPATH using Go Modules
    https://medium.com/mindorks/create-projects-independent-of-gopath-using-go-modules-802260cdfb51o
  88. Anatomy of Modules in Go
    https://medium.com/rungo/anatomy-of-modules-in-go-c8274d215c16
  89. Modules
    https://github.com/golang/go/wi­ki/Modules
  90. Go Modules Tutorial
    https://tutorialedge.net/golang/go-modules-tutorial/
  91. Module support
    https://golang.org/cmd/go/#hdr-Module_support
  92. Go Lang: Memory Management and Garbage Collection
    https://vikash1976.wordpres­s.com/2017/03/26/go-lang-memory-management-and-garbage-collection/
  93. Golang Internals, Part 4: Object Files and Function Metadata
    https://blog.altoros.com/golang-part-4-object-files-and-function-metadata.html
  94. What is REPL?
    https://pythonprogramminglan­guage.com/repl/
  95. What is a REPL?
    https://codewith.mu/en/tu­torials/1.0/repl
  96. Programming at the REPL: Introduction
    https://clojure.org/guides/re­pl/introduction
  97. What is REPL? (Quora)
    https://www.quora.com/What-is-REPL
  98. Gorilla REPL: interaktivní prostředí pro programovací jazyk Clojure
    https://www.root.cz/clanky/gorilla-repl-interaktivni-prostredi-pro-programovaci-jazyk-clojure/
  99. Read-eval-print loop (Wikipedia)
    https://en.wikipedia.org/wi­ki/Read%E2%80%93eval%E2%80%93prin­t_loop
  100. Vim as a Go (Golang) IDE using LSP and vim-go
    https://octetz.com/posts/vim-as-go-ide
  101. gopls
    https://github.com/golang/go/wi­ki/gopls
  102. IDE Integration Guide
    https://github.com/stamble­rre/gocode/blob/master/doc­s/IDE_integration.md
  103. How to instrument Go code with custom expvar metrics
    https://sysdig.com/blog/golang-expvar-custom-metrics/
  104. Golang expvar metricset (Metricbeat Reference)
    https://www.elastic.co/gu­ide/en/beats/metricbeat/7­.x/metricbeat-metricset-golang-expvar.html
  105. Package expvar
    https://golang.org/pkg/expvar/#NewInt
  106. Java Platform Debugger Architecture: Overview
    https://docs.oracle.com/en/ja­va/javase/11/docs/specs/jpda/jpda­.html
  107. The JVM Tool Interface (JVM TI): How VM Agents Work
    https://www.oracle.com/technet­work/articles/javase/index-140680.html
  108. JVM Tool Interface Version 11.0
    https://docs.oracle.com/en/ja­va/javase/11/docs/specs/jvmti­.html
  109. Creating a Debugging and Profiling Agent with JVMTI
    http://www.oracle.com/technet­work/articles/javase/jvmti-136367.html
  110. JVM TI (Wikipedia)
    http://en.wikipedia.org/wiki/JVM_TI
  111. IBM JVMTI extensions
    http://publib.boulder.ibm­.com/infocenter/realtime/v2r0/in­dex.jsp?topic=%2Fcom.ibm.sof­trt.doc%2Fdiag%2Ftools%2Fjvmti_ex­tensions.html
  112. Go & cgo: integrating existing C code with Go
    http://akrennmair.github.io/golang-cgo-slides/#1
  113. Using cgo to call C code from within Go code
    https://wenzr.wordpress.com/2018/06/07/u­sing-cgo-to-call-c-code-from-within-go-code/
  114. Package trace
    https://golang.org/pkg/runtime/trace/
  115. Introducing HTTP Tracing
    https://blog.golang.org/http-tracing
  116. Command trace
    https://golang.org/cmd/trace/
  117. A StreamLike, Immutable, Lazy Loading and smart Golang Library to deal with slices
    https://github.com/wesovilabs/koazee
  118. Funkce vyššího řádu v knihovně Underscore
    https://www.root.cz/clanky/funkce-vyssiho-radu-v-knihovne-underscore/
  119. Delve: a debugger for the Go programming language.
    https://github.com/go-delve/delve
  120. Příkazy debuggeru Delve
    https://github.com/go-delve/delve/tree/master/Do­cumentation/cli
  121. Debuggery a jejich nadstavby v Linuxu
    http://mojefedora.cz/debuggery-a-jejich-nadstavby-v-linuxu/
  122. Debuggery a jejich nadstavby v Linuxu (2. část)
    http://mojefedora.cz/debuggery-a-jejich-nadstavby-v-linuxu-2-cast/
  123. Debuggery a jejich nadstavby v Linuxu (3): Nemiver
    http://mojefedora.cz/debuggery-a-jejich-nadstavby-v-linuxu-3-nemiver/
  124. Debuggery a jejich nadstavby v Linuxu (4): KDbg
    http://mojefedora.cz/debuggery-a-jejich-nadstavby-v-linuxu-4-kdbg/
  125. Debuggery a jejich nadstavby v Linuxu (5): ladění aplikací v editorech Emacs a Vim
    http://mojefedora.cz/debuggery-a-jejich-nadstavby-v-linuxu-5-ladeni-aplikaci-v-editorech-emacs-a-vim/
  126. Debugging Go Code with GDB
    https://golang.org/doc/gdb
  127. Debugging Go (golang) programs with gdb
    https://thornydev.blogspot­.com/2014/01/debugging-go-golang-programs-with-gdb.html
  128. GDB – Dokumentace
    http://sourceware.org/gdb/cu­rrent/onlinedocs/gdb/
  129. GDB – Supported Languages
    http://sourceware.org/gdb/cu­rrent/onlinedocs/gdb/Suppor­ted-Languages.html#Supported-Languages
  130. GNU Debugger (Wikipedia)
    https://en.wikipedia.org/wi­ki/GNU_Debugger
  131. The LLDB Debugger
    http://lldb.llvm.org/
  132. Debugger (Wikipedia)
    https://en.wikipedia.org/wi­ki/Debugger
  133. 13 Linux Debuggers for C++ Reviewed
    http://www.drdobbs.com/testing/13-linux-debuggers-for-c-reviewed/240156817
  134. Go is on a Trajectory to Become the Next Enterprise Programming Language
    https://hackernoon.com/go-is-on-a-trajectory-to-become-the-next-enterprise-programming-language-3b75d70544e
  135. Go Proverbs: Simple, Poetic, Pithy
    https://go-proverbs.github.io/
  136. Handling Sparse Files on Linux
    https://www.systutorials.com/136652/han­dling-sparse-files-on-linux/
  137. Gzip (Wikipedia)
    https://en.wikipedia.org/wiki/Gzip
  138. Deflate
    https://en.wikipedia.org/wiki/DEFLATE
  139. 10 tools written in Go that every developer needs to know
    https://gustavohenrique.net/en/2019/01/10-tools-written-in-go-that-every-dev-needs-to-know/
  140. Hexadecimální prohlížeče a editory s textovým uživatelským rozhraním
    https://www.root.cz/clanky/he­xadecimalni-prohlizece-a-editory-s-textovym-uzivatelskym-rozhranim/
  141. Hex dump
    https://en.wikipedia.org/wi­ki/Hex_dump
  142. Rozhraní io.ByteReader
    https://golang.org/pkg/io/#ByteReader
  143. Rozhraní io.RuneReader
    https://golang.org/pkg/io/#RuneReader
  144. Rozhraní io.ByteScanner
    https://golang.org/pkg/io/#By­teScanner
  145. Rozhraní io.RuneScanner
    https://golang.org/pkg/io/#Ru­neScanner
  146. Rozhraní io.Closer
    https://golang.org/pkg/io/#Closer
  147. Rozhraní io.Reader
    https://golang.org/pkg/io/#Reader
  148. Rozhraní io.Writer
    https://golang.org/pkg/io/#Writer
  149. Typ Strings.Reader
    https://golang.org/pkg/strin­gs/#Reader
  150. VACUUM (SQL)
    https://www.sqlite.org/lan­g_vacuum.html
  151. VACUUM (Postgres)
    https://www.postgresql.or­g/docs/8.4/sql-vacuum.html
  152. go-cron
    https://github.com/rk/go-cron
  153. gocron
    https://github.com/jasonlvhit/gocron
  154. clockwork
    https://github.com/whiteShtef/cloc­kwork
  155. clockwerk
    https://github.com/onatm/clockwerk
  156. JobRunner
    https://github.com/bamzi/jobrunner
  157. Rethinking Cron
    https://adam.herokuapp.com/pas­t/2010/4/13/rethinking_cron/
  158. In the Beginning was the Command Line
    https://web.archive.org/web/20180218045352/htt­p://www.cryptonomicon.com/be­ginning.html
  159. repl.it (REPL pro různé jazyky)
    https://repl.it/languages
  160. GOCUI – Go Console User Interface (celé uživatelské prostředí, nejenom input box)
    https://github.com/jroimartin/gocui
  161. Read–eval–print loop
    https://en.wikipedia.org/wi­ki/Read%E2%80%93eval%E2%80%93prin­t_loop
  162. go-prompt
    https://github.com/c-bata/go-prompt
  163. readline
    https://github.com/chzyer/readline
  164. A pure golang implementation for GNU-Readline kind library
    https://golangexample.com/a-pure-golang-implementation-for-gnu-readline-kind-library/
  165. go-readline
    https://github.com/fiorix/go-readline
  166. 4 Python libraries for building great command-line user interfaces
    https://opensource.com/article/17/5/4-practical-python-libraries
  167. prompt_toolkit 2.0.3 na PyPi
    https://pypi.org/project/prom­pt_toolkit/
  168. python-prompt-toolkit na GitHubu
    https://github.com/jonathan­slenders/python-prompt-toolkit
  169. The GNU Readline Library
    https://tiswww.case.edu/php/chet/re­adline/rltop.html
  170. GNU Readline (Wikipedia)
    https://en.wikipedia.org/wi­ki/GNU_Readline
  171. readline — GNU readline interface (Python 3.x)
    https://docs.python.org/3/li­brary/readline.html
  172. readline — GNU readline interface (Python 2.x)
    https://docs.python.org/2/li­brary/readline.html
  173. GNU Readline Library – command line editing
    https://tiswww.cwru.edu/php/chet/re­adline/readline.html
  174. gnureadline 6.3.8 na PyPi
    https://pypi.org/project/gnureadline/
  175. Editline Library (libedit)
    http://thrysoee.dk/editline/
  176. Comparing Python Command-Line Parsing Libraries – Argparse, Docopt, and Click
    https://realpython.com/comparing-python-command-line-parsing-libraries-argparse-docopt-click/
  177. libedit or editline
    http://www.cs.utah.edu/~bi­gler/code/libedit.html
  178. WinEditLine
    http://mingweditline.sourceforge.net/
  179. rlcompleter — Completion function for GNU readline
    https://docs.python.org/3/li­brary/rlcompleter.html
  180. rlwrap na GitHubu
    https://github.com/hanslub42/rlwrap
  181. rlwrap(1) – Linux man page
    https://linux.die.net/man/1/rlwrap
  182. readline(3) – Linux man page
    https://linux.die.net/man/3/readline
  183. history(3) – Linux man page
    https://linux.die.net/man/3/history
  184. Dokumentace k balíčku oglematchers
    https://godoc.org/github.com/ja­cobsa/oglematchers
  185. Balíček oglematchers
    https://github.com/jacobsa/o­glematchers
  186. Dokumentace k balíčku ogletest
    https://godoc.org/github.com/ja­cobsa/ogletest
  187. Balíček ogletest
    https://github.com/jacobsa/ogletest
  188. Dokumentace k balíčku assert
    https://godoc.org/github.com/stret­chr/testify/assert
  189. Testify – Thou Shalt Write Tests
    https://github.com/stretchr/testify/
  190. package testing
    https://golang.org/pkg/testing/
  191. Golang basics – writing unit tests
    https://blog.alexellis.io/golang-writing-unit-tests/
  192. An Introduction to Programming in Go / Testing
    https://www.golang-book.com/books/intro/12
  193. An Introduction to Testing in Go
    https://tutorialedge.net/golang/intro-testing-in-go/
  194. Advanced Go Testing Tutorial
    https://tutorialedge.net/go­lang/advanced-go-testing-tutorial/
  195. GoConvey
    http://goconvey.co/
  196. Testing Techniques
    https://talks.golang.org/2014/tes­ting.slide
  197. 5 simple tips and tricks for writing unit tests in #golang
    https://medium.com/@matryer/5-simple-tips-and-tricks-for-writing-unit-tests-in-golang-619653f90742
  198. Afinní transformace
    https://cs.wikibooks.org/wi­ki/Geometrie/Afinn%C3%AD_tran­sformace_sou%C5%99adnic
  199. package gg
    https://godoc.org/github.com/fo­gleman/gg
  200. Generate an animated GIF with Golang
    http://tech.nitoyon.com/en/blog/2016/01/07/­go-animated-gif-gen/
  201. Generate an image programmatically with Golang
    http://tech.nitoyon.com/en/blog/2015/12/31/­go-image-gen/
  202. The Go image package
    https://blog.golang.org/go-image-package
  203. Balíček draw2D: 2D rendering for different output (raster, pdf, svg)
    https://github.com/llgcode/draw2d
  204. Draw a rectangle in Golang?
    https://stackoverflow.com/qu­estions/28992396/draw-a-rectangle-in-golang
  205. YAML
    https://yaml.org/
  206. edn
    https://github.com/edn-format/edn
  207. Smile
    https://github.com/FasterXML/smile-format-specification
  208. Protocol-Buffers
    https://developers.google.com/protocol-buffers/
  209. Marshalling (computer science)
    https://en.wikipedia.org/wi­ki/Marshalling_(computer_sci­ence)
  210. Unmarshalling
    https://en.wikipedia.org/wi­ki/Unmarshalling
  211. Introducing JSON
    http://json.org/
  212. Package json
    https://golang.org/pkg/encoding/json/
  213. The Go Blog: JSON and Go
    https://blog.golang.org/json-and-go
  214. Go by Example: JSON
    https://gobyexample.com/json
  215. Writing Web Applications
    https://golang.org/doc/articles/wiki/
  216. Golang Web Apps
    https://www.reinbach.com/blog/golang-webapps-1/
  217. Build web application with Golang
    https://legacy.gitbook.com/bo­ok/astaxie/build-web-application-with-golang/details
  218. Golang Templates – Golang Web Pages
    https://www.youtube.com/wat­ch?v=TkNIETmF-RU
  219. Simple Golang HTTPS/TLS Examples
    https://github.com/denji/golang-tls
  220. Playing with images in HTTP response in golang
    https://www.sanarias.com/blog/1214Pla­yingwithimagesinHTTPrespon­seingolang
  221. MIME Types List
    https://www.freeformatter.com/mime-types-list.html
  222. Go Mutex Tutorial
    https://tutorialedge.net/golang/go-mutex-tutorial/
  223. Creating A Simple Web Server With Golang
    https://tutorialedge.net/go­lang/creating-simple-web-server-with-golang/
  224. Building a Web Server in Go
    https://thenewstack.io/building-a-web-server-in-go/
  225. How big is the pipe buffer?
    https://unix.stackexchange­.com/questions/11946/how-big-is-the-pipe-buffer
  226. How to turn off buffering of stdout in C
    https://stackoverflow.com/qu­estions/7876660/how-to-turn-off-buffering-of-stdout-in-c
  227. setbuf(3) – Linux man page
    https://linux.die.net/man/3/setbuf
  228. setvbuf(3) – Linux man page (stejný obsah jako předchozí stránka)
    https://linux.die.net/man/3/setvbuf
  229. Select waits on a group of channels
    https://yourbasic.org/golang/select-explained/
  230. Rob Pike: Simplicity is Complicated (video)
    http://www.golang.to/posts/dotgo-2015-rob-pike-simplicity-is-complicated-youtube-16893
  231. Algorithms to Go
    https://yourbasic.org/
  232. Využití knihovny Pygments (nejenom) pro obarvení zdrojových kódů
    https://www.root.cz/clanky/vyuziti-knihovny-pygments-nejenom-pro-obarveni-zdrojovych-kodu/
  233. Využití knihovny Pygments (nejenom) pro obarvení zdrojových kódů: vlastní filtry a lexery
    https://www.root.cz/clanky/vyuziti-knihovny-pygments-nejenom-pro-obarveni-zdrojovych-kodu-vlastni-filtry-a-lexery/
  234. Go Defer Simplified with Practical Visuals
    https://blog.learngoprogram­ming.com/golang-defer-simplified-77d3b2b817ff
  235. 5 More Gotchas of Defer in Go — Part II
    https://blog.learngoprogramming.com/5-gotchas-of-defer-in-go-golang-part-ii-cc550f6ad9aa
  236. The Go Blog: Defer, Panic, and Recover
    https://blog.golang.org/defer-panic-and-recover
  237. The defer keyword in Swift 2: try/finally done right
    https://www.hackingwithswift.com/new-syntax-swift-2-defer
  238. Swift Defer Statement
    https://andybargh.com/swift-defer-statement/
  239. Modulo operation (Wikipedia)
    https://en.wikipedia.org/wi­ki/Modulo_operation
  240. Node.js vs Golang: Battle of the Next-Gen Languages
    https://www.hostingadvice­.com/blog/nodejs-vs-golang/
  241. The Go Programming Language (home page)
    https://golang.org/
  242. GoDoc
    https://godoc.org/
  243. Go (programming language), Wikipedia
    https://en.wikipedia.org/wi­ki/Go_(programming_langua­ge)
  244. Go Books (kniha o jazyku Go)
    https://github.com/dariubs/GoBooks
  245. The Go Programming Language Specification
    https://golang.org/ref/spec
  246. Go: the Good, the Bad and the Ugly
    https://bluxte.net/musings/2018/04/10/go-good-bad-ugly/
  247. Package builtin
    https://golang.org/pkg/builtin/
  248. Package fmt
    https://golang.org/pkg/fmt/
  249. The Little Go Book (další kniha)
    https://github.com/dariubs/GoBooks
  250. The Go Programming Language by Brian W. Kernighan, Alan A. A. Donovan
    https://www.safaribookson­line.com/library/view/the-go-programming/9780134190570/e­book_split010.html
  251. Learning Go
    https://www.miek.nl/go/
  252. Go Bootcamp
    http://www.golangbootcamp.com/
  253. Programming in Go: Creating Applications for the 21st Century (další kniha o jazyku Go)
    http://www.informit.com/sto­re/programming-in-go-creating-applications-for-the-21st-9780321774637
  254. Introducing Go (Build Reliable, Scalable Programs)
    http://shop.oreilly.com/pro­duct/0636920046516.do
  255. Learning Go Programming
    https://www.packtpub.com/application-development/learning-go-programming
  256. The Go Blog
    https://blog.golang.org/
  257. Getting to Go: The Journey of Go's Garbage Collector
    https://blog.golang.org/ismmkeynote
  258. Go (programovací jazyk, Wikipedia)
    https://cs.wikipedia.org/wi­ki/Go_(programovac%C3%AD_ja­zyk)
  259. Rychle, rychleji až úplně nejrychleji s jazykem Go
    https://www.root.cz/clanky/rychle-rychleji-az-uplne-nejrychleji-s-jazykem-go/
  260. Installing Go on the Raspberry Pi
    https://dave.cheney.net/2012/09/25/in­stalling-go-on-the-raspberry-pi
  261. How the Go runtime implements maps efficiently (without generics)
    https://dave.cheney.net/2018/05/29/how-the-go-runtime-implements-maps-efficiently-without-generics
  262. Niečo málo o Go – Golang (slovensky)
    http://golangsk.logdown.com/
  263. How Many Go Developers Are There?
    https://research.swtch.com/gop­hercount
  264. Most Popular Technologies (Stack Overflow Survery 2018)
    https://insights.stackover­flow.com/survey/2018/#most-popular-technologies
  265. Most Popular Technologies (Stack Overflow Survery 2017)
    https://insights.stackover­flow.com/survey/2017#techno­logy
  266. JavaScript vs. Golang for IoT: Is Gopher Winning?
    https://www.iotforall.com/javascript-vs-golang-iot/
  267. The Go Programming Language: Release History
    https://golang.org/doc/de­vel/release.html
  268. Go 1.11 Release Notes
    https://golang.org/doc/go1.11
  269. Go 1.10 Release Notes
    https://golang.org/doc/go1.10
  270. Go 1.9 Release Notes (tato verze je stále používána)
    https://golang.org/doc/go1.9
  271. Go 1.8 Release Notes (i tato verze je stále používána)
    https://golang.org/doc/go1.8
  272. Go on Fedora
    https://developer.fedorapro­ject.org/tech/languages/go/go-installation.html
  273. Writing Go programs
    https://developer.fedorapro­ject.org/tech/languages/go/go-programs.html
  274. The GOPATH environment variable
    https://tip.golang.org/doc/co­de.html#GOPATH
  275. Command gofmt
    https://tip.golang.org/cmd/gofmt/
  276. The Go Blog: go fmt your code
    https://blog.golang.org/go-fmt-your-code
  277. C? Go? Cgo!
    https://blog.golang.org/c-go-cgo
  278. Spaces vs. Tabs: A 20-Year Debate Reignited by Google’s Golang
    https://thenewstack.io/spaces-vs-tabs-a-20-year-debate-and-now-this-what-the-hell-is-wrong-with-go/
  279. 400,000 GitHub repositories, 1 billion files, 14 terabytes of code: Spaces or Tabs?
    https://medium.com/@hoffa/400–000-github-repositories-1-billion-files-14-terabytes-of-code-spaces-or-tabs-7cfe0b5dd7fd
  280. Gofmt No Longer Allows Spaces. Tabs Only
    https://news.ycombinator.com/i­tem?id=7914523
  281. Why does Go „go fmt“ uses tabs instead of whitespaces?
    https://www.quora.com/Why-does-Go-go-fmt-uses-tabs-instead-of-whitespaces
  282. Interactive: The Top Programming Languages 2018
    https://spectrum.ieee.org/sta­tic/interactive-the-top-programming-languages-2018
  283. Go vs. Python
    https://www.peterbe.com/plog/govspy
  284. PackageManagementTools
    https://github.com/golang/go/wi­ki/PackageManagementTools
  285. A Tour of Go: Type inference
    https://tour.golang.org/basics/14
  286. Go Slices: usage and internals
    https://blog.golang.org/go-slices-usage-and-internals
  287. Go by Example: Slices
    https://gobyexample.com/slices
  288. What is the point of slice type in Go?
    https://stackoverflow.com/qu­estions/2098874/what-is-the-point-of-slice-type-in-go
  289. The curious case of Golang array and slices
    https://medium.com/@hackintoshrao/the-curious-case-of-golang-array-and-slices-2565491d4335
  290. Introduction to Slices in Golang
    https://www.callicoder.com/golang-slices/
  291. Golang: Understanding ‚null‘ and nil
    https://newfivefour.com/golang-null-nil.html
  292. What does nil mean in golang?
    https://stackoverflow.com/qu­estions/35983118/what-does-nil-mean-in-golang
  293. nils In Go
    https://go101.org/article/nil.html
  294. Go slices are not dynamic arrays
    https://appliedgo.net/slices/
  295. Go-is-no-good (nelze brát doslova)
    https://github.com/ksimka/go-is-not-good
  296. Rust vs. Go
    https://news.ycombinator.com/i­tem?id=13430108
  297. Seriál Programovací jazyk Rust
    https://www.root.cz/seria­ly/programovaci-jazyk-rust/
  298. Modern garbage collection: A look at the Go GC strategy
    https://blog.plan99.net/modern-garbage-collection-911ef4f8bd8e
  299. Go GC: Prioritizing low latency and simplicity
    https://blog.golang.org/go15gc
  300. Is Golang a good language for embedded systems?
    https://www.quora.com/Is-Golang-a-good-language-for-embedded-systems
  301. Running GoLang on an STM32 MCU. A quick tutorial.
    https://www.mickmake.com/post/running-golang-on-an-mcu-a-quick-tutorial
  302. Go, Robot, Go! Golang Powered Robotics
    https://gobot.io/
  303. Emgo: Bare metal Go (language for programming embedded systems)
    https://github.com/ziutek/emgo
  304. UTF-8 history
    https://www.cl.cam.ac.uk/~mgk25/uc­s/utf-8-history.txt
  305. Less is exponentially more
    https://commandcenter.blog­spot.com/2012/06/less-is-exponentially-more.html
  306. Should I Rust, or Should I Go
    https://codeburst.io/should-i-rust-or-should-i-go-59a298e00ea9
  307. Setting up and using gccgo
    https://golang.org/doc/install/gccgo
  308. Elastic Tabstops
    http://nickgravgaard.com/elastic-tabstops/
  309. Strings, bytes, runes and characters in Go
    https://blog.golang.org/strings
  310. Datový typ
    https://cs.wikipedia.org/wi­ki/Datov%C3%BD_typ
  311. Seriál o programovacím jazyku Rust: Základní (primitivní) datové typy
    https://www.root.cz/clanky/pro­gramovaci-jazyk-rust-nahrada-c-nebo-slepa-cesta/#k09
  312. Seriál o programovacím jazyku Rust: Vytvoření „řezu“ z pole
    https://www.root.cz/clanky/prace-s-poli-v-programovacim-jazyku-rust/#k06
  313. Seriál o programovacím jazyku Rust: Řezy (slice) vektoru
    https://www.root.cz/clanky/prace-s-vektory-v-programovacim-jazyku-rust/#k05
  314. Printf Format Strings
    https://www.cprogramming.com/tu­torial/printf-format-strings.html
  315. Java: String.format
    https://docs.oracle.com/ja­vase/8/docs/api/java/lang/Strin­g.html#format-java.lang.String-java.lang.Object…-
  316. Java: format string syntax
    https://docs.oracle.com/ja­vase/8/docs/api/java/util/For­matter.html#syntax
  317. Selectors
    https://golang.org/ref/spec#Selectors
  318. Calling Go code from Python code
    http://savorywatt.com/2015/09/18/ca­lling-go-code-from-python-code/
  319. Go Data Structures: Interfaces
    https://research.swtch.com/interfaces
  320. How to use interfaces in Go
    http://jordanorelli.com/pos­t/32665860244/how-to-use-interfaces-in-go
  321. Interfaces in Go (part I)
    https://medium.com/golangspec/in­terfaces-in-go-part-i-4ae53a97479c
  322. Part 21: Goroutines
    https://golangbot.com/goroutines/
  323. Part 22: Channels
    https://golangbot.com/channels/
  324. [Go] Lightweight eventbus with async compatibility for Go
    https://github.com/asaske­vich/EventBus
  325. What about Trait support in Golang?
    https://www.reddit.com/r/go­lang/comments/8mfykl/what_a­bout_trait_support_in_golan­g/
  326. Don't Get Bitten by Pointer vs Non-Pointer Method Receivers in Golang
    https://nathanleclaire.com/blog/2014/08/09/dont-get-bitten-by-pointer-vs-non-pointer-method-receivers-in-golang/
  327. Control Flow
    https://en.wikipedia.org/wi­ki/Control_flow
  328. Structured programming
    https://en.wikipedia.org/wi­ki/Structured_programming
  329. Control Structures
    https://www.golang-book.com/books/intro/5
  330. Control structures – Go if else statement
    http://golangtutorials.blog­spot.com/2011/06/control-structures-if-else-statement.html
  331. Control structures – Go switch case statement
    http://golangtutorials.blog­spot.com/2011/06/control-structures-go-switch-case.html
  332. Control structures – Go for loop, break, continue, range
    http://golangtutorials.blog­spot.com/2011/06/control-structures-go-for-loop-break.html
  333. Goroutine IDs
    https://blog.sgmansfield.com/2015/12/go­routine-ids/
  334. Different ways to pass channels as arguments in function in go (golang)
    https://stackoverflow.com/qu­estions/24868859/different-ways-to-pass-channels-as-arguments-in-function-in-go-golang
  335. justforfunc #22: using the Go execution tracer
    https://www.youtube.com/wat­ch?v=ySy3sR1LFCQ
  336. Single Function Exit Point
    http://wiki.c2.com/?Single­FunctionExitPoint
  337. Entry point
    https://en.wikipedia.org/wi­ki/Entry_point
  338. Why does Go have a GOTO statement?!
    https://www.reddit.com/r/go­lang/comments/kag5q/why_do­es_go_have_a_goto_statemen­t/
  339. Effective Go
    https://golang.org/doc/ef­fective_go.html
  340. GoClipse: an Eclipse IDE for the Go programming language
    http://goclipse.github.io/
  341. GoClipse Installation
    https://github.com/GoClip­se/goclipse/blob/latest/do­cumentation/Installation.md#in­stallation
  342. The zero value of a slice is not nil
    https://stackoverflow.com/qu­estions/30806931/the-zero-value-of-a-slice-is-not-nil
  343. Go-tcha: When nil != nil
    https://dev.to/pauljlucas/go-tcha-when-nil–nil-hic
  344. Nils in Go
    https://www.doxsey.net/blog/nils-in-go