Obsah
1. Programovací jazyk Go a assembler (3.část)
2. Malá rozcvička: interní reprezentace řezů (slices)
3. Předávání řezů v zásobníkovém rámci do volaných funkcí
4. Vyplnění obrázku konstantní barvou
5. Benchmark pro funkci vyplňující rastrový obrázek
6. Naivní implementace vyplňování v assembleru
8. Reorganizace vnitřní smyčky naprogramované v assembleru
10. Vyplňování po čtyřbajtových slovech
12. Použití „řetězcových“ operací typu REP STOS
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
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ů).
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):
- Ukazatele (reference) na zvolený prvek pole s daty, ke kterým přes řez přistupujeme.
- Délky řezu, tj. počtu prvků.
- 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:
- 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).
- 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).
- 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).
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í.
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
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
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í.
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
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:vmovdqu16:vmovdqu32:vmovdqu64, popř. na https://www.felixcloutier.com/x86/movntdq (další varianta).
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/runtime/memclr_amd64.s.
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
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_empty_image_high_level/empty_image.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:
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:
20. Odkazy na Internetu
- Golang Guide: A List of Top Golang Frameworks, IDEs & Tools
https://blog.intelligentbee.com/2017/08/14/golang-guide-list-top-golang-frameworks-ides-tools/ - Repositář projektu Gift
https://github.com/disintegration/gift - Dokumentace k projektu Gift
https://godoc.org/github.com/disintegration/gift - Online x86 / x64 Assembler and Disassembler
https://defuse.ca/online-x86-assembler.htm#disassembly2 - The Design of the Go Assembler
https://talks.golang.org/2016/asm.slide#1 - A Quick Guide to Go's Assembler
https://golang.org/doc/asm - AssemblyPolicy
https://github.com/golang/go/wiki/AssemblyPolicy - Geohash in Golang Assembly
https://mmcloughlin.com/posts/geohash-assembly - Command objdump
https://golang.org/cmd/objdump/ - Assembly
https://goroutines.com/asm - Go & Assembly
http://www.doxsey.net/blog/go-and-assembly - A Foray Into Go Assembly Programming
https://blog.sgmansfield.com/2017/04/a-foray-into-go-assembly-programming/ - Golang Capturing log.Println And fmt.Println Output
https://medium.com/@hau12a1/golang-capturing-log-println-and-fmt-println-output-770209c791b4 - Stránka projektu plotly
https://plot.ly/ - Plotly JavaScript Open Source Graphing Library
https://plot.ly/javascript/ - Domain coloring
https://en.wikipedia.org/wiki/Domain_coloring - Michael Fogleman's projects
https://www.michaelfogleman.com/projects/tagged/graphics/ - Color Graphs of Complex Functions
https://web.archive.org/web/20120511021419/http://w.american.edu/cas/mathstat/lcrone/ComplexPlot.html - A Gallery of Complex Functions
http://wismuth.com/complex/gallery.html - package glot
https://godoc.org/github.com/Arafatk/glot - Gnuplotting: Output terminals
http://www.gnuplotting.org/output-terminals/ - Introducing Glot the plotting library for Golang
https://medium.com/@Arafat./introducing-glot-the-plotting-library-for-golang-3133399948a1 - Introducing Glot the plotting library for Golang
https://blog.gopheracademy.com/advent-2018/introducing-glot/ - Glot is a plotting library for Golang built on top of gnuplot
https://github.com/Arafatk/glot - Example plots (gonum/plot)
https://github.com/gonum/plot/wiki/Example-plots - A repository for plotting and visualizing data (gonum/plot)
https://github.com/gonum/plot - golang library to make https://chartjs.org/ plots
https://github.com/brentp/go-chartjs - Gorilla REPL: interaktivní prostředí pro programovací jazyk Clojure
https://www.root.cz/clanky/gorilla-repl-interaktivni-prostredi-pro-programovaci-jazyk-clojure/ - The Gonum Numerical Computing Package
https://www.gonum.org/post/introtogonum/ - Gomacro na GitHubu
https://github.com/cosmos72/gomacro - gophernotes – Use Go in Jupyter notebooks and nteract
https://github.com/gopherdata/gophernotes - gonum
https://github.com/gonum - go-gota/gota – DataFrames and data wrangling in Go (Golang)
https://porter.io/github.com/go-gota/gota - A repository for plotting and visualizing data
https://github.com/gonum/plot - Gonum Numerical Packages
https://www.gonum.org/ - Stránky projektu MinIO
https://min.io/ - MinIO Quickstart Guide
https://docs.min.io/docs/minio-quickstart-guide.html - MinIO Go Client API Reference
https://docs.min.io/docs/golang-client-api-reference - MinIO Python Client API Reference
https://docs.min.io/docs/python-client-api-reference.html - 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/ - Benchmarking MinIO vs. AWS S3 for Apache Spark
https://blog.min.io/benchmarking-apache-spark-vs-aws-s3/ - MinIO Client Quickstart Guide
https://docs.min.io/docs/minio-client-quickstart-guide.html - Analýza kvality zdrojových kódů Minia
https://goreportcard.com/report/github.com/minio/minio - This is MinIO
https://www.youtube.com/watch?v=vF0lQh0XOCs - Running MinIO Standalone
https://www.youtube.com/watch?v=dIQsPCHvHoM - „Amazon S3 Compatible Storage in Kubernetes“ – Rob Girard, Principal Tech Marketing Engineer, Minio
https://www.youtube.com/watch?v=wlpn8K0jJ4U - Ginkgo
http://onsi.github.io/ginkgo/ - Gomega
https://onsi.github.io/gomega/ - Ginkgo's Preferred Matcher Library na GitHubu
https://github.com/onsi/gomega/ - Provided Matchers
http://onsi.github.io/gomega/#provided-matchers - Dokumentace k balíčku goexpect
https://godoc.org/github.com/google/goexpect - Balíček goexpect
https://github.com/google/goexpect - Balíček go-expect
https://github.com/Netflix/go-expect - Balíček gexpect
https://github.com/ThomasRooney/gexpect - Expect (originál naprogramovaný v TCL)
https://core.tcl-lang.org/expect/index - Expect (Wikipedia)
https://en.wikipedia.org/wiki/Expect - Pexpect
https://pexpect.readthedocs.io/en/stable/ - Golang SSH Client: Multiple Commands, Crypto & Goexpect Examples
http://networkbit.ch/golang-ssh-client/ - goblin na GitHubu
https://github.com/franela/goblin - Mocha framework
https://mochajs.org/ - frisby na GitHubu
https://github.com/verdverm/frisby - package frisby
https://godoc.org/github.com/verdverm/frisby - Frisby alternatives and similar packages (generováno)
https://go.libhunt.com/frisby-alternatives - Cucumber for golang
https://github.com/DATA-DOG/godog - How to Use Godog for Behavior-driven Development in Go
https://semaphoreci.com/community/tutorials/how-to-use-godog-for-behavior-driven-development-in-go - Comparative Analysis Of GoLang Testing Frameworks
https://www.slideshare.net/DushyantBhalgami/comparative-analysis-of-golang-testing-frameworks - A Quick Guide to Testing in Golang
https://caitiem.com/2016/08/18/a-quick-guide-to-testing-in-golang/ - Tom's Obvious, Minimal Language.
https://github.com/toml-lang/toml - xml.org
http://www.xml.org/ - Soubory .properties
https://en.wikipedia.org/wiki/.properties - Soubory INI
https://en.wikipedia.org/wiki/INI_file - JSON to YAML
https://www.json2yaml.com/ - Data Format Converter
https://toolkit.site/format.html - Viper na GitHubu
https://github.com/spf13/viper - GoDotEnv na GitHubu
https://github.com/joho/godotenv - The fantastic ORM library for Golang
http://gorm.io/ - Dokumentace k balíčku gorilla/mux
https://godoc.org/github.com/gorilla/mux - Gorilla web toolkitk
http://www.gorillatoolkit.org/ - Metric types
https://prometheus.io/docs/concepts/metric_types/ - Histograms with Prometheus: A Tale of Woe
http://linuxczar.net/blog/2017/06/15/prometheus-histogram-2/ - Why are Prometheus histograms cumulative?
https://www.robustperception.io/why-are-prometheus-histograms-cumulative - Histograms and summaries
https://prometheus.io/docs/practices/histograms/ - Instrumenting Golang server in 5 min
https://medium.com/@gsisimogang/instrumenting-golang-server-in-5-min-c1c32489add3 - Semantic Import Versioning in Go
https://www.aaronzhuo.com/semantic-import-versioning-in-go/ - Sémantické verzování
https://semver.org/ - Getting started with Go modules
https://medium.com/@fonseka.live/getting-started-with-go-modules-b3dac652066d - Create projects independent of $GOPATH using Go Modules
https://medium.com/mindorks/create-projects-independent-of-gopath-using-go-modules-802260cdfb51o - Anatomy of Modules in Go
https://medium.com/rungo/anatomy-of-modules-in-go-c8274d215c16 - Modules
https://github.com/golang/go/wiki/Modules - Go Modules Tutorial
https://tutorialedge.net/golang/go-modules-tutorial/ - Module support
https://golang.org/cmd/go/#hdr-Module_support - Go Lang: Memory Management and Garbage Collection
https://vikash1976.wordpress.com/2017/03/26/go-lang-memory-management-and-garbage-collection/ - Golang Internals, Part 4: Object Files and Function Metadata
https://blog.altoros.com/golang-part-4-object-files-and-function-metadata.html - What is REPL?
https://pythonprogramminglanguage.com/repl/ - What is a REPL?
https://codewith.mu/en/tutorials/1.0/repl - Programming at the REPL: Introduction
https://clojure.org/guides/repl/introduction - What is REPL? (Quora)
https://www.quora.com/What-is-REPL - Gorilla REPL: interaktivní prostředí pro programovací jazyk Clojure
https://www.root.cz/clanky/gorilla-repl-interaktivni-prostredi-pro-programovaci-jazyk-clojure/ - Read-eval-print loop (Wikipedia)
https://en.wikipedia.org/wiki/Read%E2%80%93eval%E2%80%93print_loop - Vim as a Go (Golang) IDE using LSP and vim-go
https://octetz.com/posts/vim-as-go-ide - gopls
https://github.com/golang/go/wiki/gopls - IDE Integration Guide
https://github.com/stamblerre/gocode/blob/master/docs/IDE_integration.md - How to instrument Go code with custom expvar metrics
https://sysdig.com/blog/golang-expvar-custom-metrics/ - Golang expvar metricset (Metricbeat Reference)
https://www.elastic.co/guide/en/beats/metricbeat/7.x/metricbeat-metricset-golang-expvar.html - Package expvar
https://golang.org/pkg/expvar/#NewInt - Java Platform Debugger Architecture: Overview
https://docs.oracle.com/en/java/javase/11/docs/specs/jpda/jpda.html - The JVM Tool Interface (JVM TI): How VM Agents Work
https://www.oracle.com/technetwork/articles/javase/index-140680.html - JVM Tool Interface Version 11.0
https://docs.oracle.com/en/java/javase/11/docs/specs/jvmti.html - Creating a Debugging and Profiling Agent with JVMTI
http://www.oracle.com/technetwork/articles/javase/jvmti-136367.html - JVM TI (Wikipedia)
http://en.wikipedia.org/wiki/JVM_TI - IBM JVMTI extensions
http://publib.boulder.ibm.com/infocenter/realtime/v2r0/index.jsp?topic=%2Fcom.ibm.softrt.doc%2Fdiag%2Ftools%2Fjvmti_extensions.html - Go & cgo: integrating existing C code with Go
http://akrennmair.github.io/golang-cgo-slides/#1 - Using cgo to call C code from within Go code
https://wenzr.wordpress.com/2018/06/07/using-cgo-to-call-c-code-from-within-go-code/ - Package trace
https://golang.org/pkg/runtime/trace/ - Introducing HTTP Tracing
https://blog.golang.org/http-tracing - Command trace
https://golang.org/cmd/trace/ - A StreamLike, Immutable, Lazy Loading and smart Golang Library to deal with slices
https://github.com/wesovilabs/koazee - Funkce vyššího řádu v knihovně Underscore
https://www.root.cz/clanky/funkce-vyssiho-radu-v-knihovne-underscore/ - Delve: a debugger for the Go programming language.
https://github.com/go-delve/delve - Příkazy debuggeru Delve
https://github.com/go-delve/delve/tree/master/Documentation/cli - Debuggery a jejich nadstavby v Linuxu
http://mojefedora.cz/debuggery-a-jejich-nadstavby-v-linuxu/ - Debuggery a jejich nadstavby v Linuxu (2. část)
http://mojefedora.cz/debuggery-a-jejich-nadstavby-v-linuxu-2-cast/ - Debuggery a jejich nadstavby v Linuxu (3): Nemiver
http://mojefedora.cz/debuggery-a-jejich-nadstavby-v-linuxu-3-nemiver/ - Debuggery a jejich nadstavby v Linuxu (4): KDbg
http://mojefedora.cz/debuggery-a-jejich-nadstavby-v-linuxu-4-kdbg/ - 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/ - Debugging Go Code with GDB
https://golang.org/doc/gdb - Debugging Go (golang) programs with gdb
https://thornydev.blogspot.com/2014/01/debugging-go-golang-programs-with-gdb.html - GDB – Dokumentace
http://sourceware.org/gdb/current/onlinedocs/gdb/ - GDB – Supported Languages
http://sourceware.org/gdb/current/onlinedocs/gdb/Supported-Languages.html#Supported-Languages - GNU Debugger (Wikipedia)
https://en.wikipedia.org/wiki/GNU_Debugger - The LLDB Debugger
http://lldb.llvm.org/ - Debugger (Wikipedia)
https://en.wikipedia.org/wiki/Debugger - 13 Linux Debuggers for C++ Reviewed
http://www.drdobbs.com/testing/13-linux-debuggers-for-c-reviewed/240156817 - 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 - Go Proverbs: Simple, Poetic, Pithy
https://go-proverbs.github.io/ - Handling Sparse Files on Linux
https://www.systutorials.com/136652/handling-sparse-files-on-linux/ - Gzip (Wikipedia)
https://en.wikipedia.org/wiki/Gzip - Deflate
https://en.wikipedia.org/wiki/DEFLATE - 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/ - Hexadecimální prohlížeče a editory s textovým uživatelským rozhraním
https://www.root.cz/clanky/hexadecimalni-prohlizece-a-editory-s-textovym-uzivatelskym-rozhranim/ - Hex dump
https://en.wikipedia.org/wiki/Hex_dump - Rozhraní io.ByteReader
https://golang.org/pkg/io/#ByteReader - Rozhraní io.RuneReader
https://golang.org/pkg/io/#RuneReader - Rozhraní io.ByteScanner
https://golang.org/pkg/io/#ByteScanner - Rozhraní io.RuneScanner
https://golang.org/pkg/io/#RuneScanner - Rozhraní io.Closer
https://golang.org/pkg/io/#Closer - Rozhraní io.Reader
https://golang.org/pkg/io/#Reader - Rozhraní io.Writer
https://golang.org/pkg/io/#Writer - Typ Strings.Reader
https://golang.org/pkg/strings/#Reader - VACUUM (SQL)
https://www.sqlite.org/lang_vacuum.html - VACUUM (Postgres)
https://www.postgresql.org/docs/8.4/sql-vacuum.html - go-cron
https://github.com/rk/go-cron - gocron
https://github.com/jasonlvhit/gocron - clockwork
https://github.com/whiteShtef/clockwork - clockwerk
https://github.com/onatm/clockwerk - JobRunner
https://github.com/bamzi/jobrunner - Rethinking Cron
https://adam.herokuapp.com/past/2010/4/13/rethinking_cron/ - In the Beginning was the Command Line
https://web.archive.org/web/20180218045352/http://www.cryptonomicon.com/beginning.html - repl.it (REPL pro různé jazyky)
https://repl.it/languages - GOCUI – Go Console User Interface (celé uživatelské prostředí, nejenom input box)
https://github.com/jroimartin/gocui - Read–eval–print loop
https://en.wikipedia.org/wiki/Read%E2%80%93eval%E2%80%93print_loop - go-prompt
https://github.com/c-bata/go-prompt - readline
https://github.com/chzyer/readline - A pure golang implementation for GNU-Readline kind library
https://golangexample.com/a-pure-golang-implementation-for-gnu-readline-kind-library/ - go-readline
https://github.com/fiorix/go-readline - 4 Python libraries for building great command-line user interfaces
https://opensource.com/article/17/5/4-practical-python-libraries - prompt_toolkit 2.0.3 na PyPi
https://pypi.org/project/prompt_toolkit/ - python-prompt-toolkit na GitHubu
https://github.com/jonathanslenders/python-prompt-toolkit - The GNU Readline Library
https://tiswww.case.edu/php/chet/readline/rltop.html - GNU Readline (Wikipedia)
https://en.wikipedia.org/wiki/GNU_Readline - readline — GNU readline interface (Python 3.x)
https://docs.python.org/3/library/readline.html - readline — GNU readline interface (Python 2.x)
https://docs.python.org/2/library/readline.html - GNU Readline Library – command line editing
https://tiswww.cwru.edu/php/chet/readline/readline.html - gnureadline 6.3.8 na PyPi
https://pypi.org/project/gnureadline/ - Editline Library (libedit)
http://thrysoee.dk/editline/ - Comparing Python Command-Line Parsing Libraries – Argparse, Docopt, and Click
https://realpython.com/comparing-python-command-line-parsing-libraries-argparse-docopt-click/ - libedit or editline
http://www.cs.utah.edu/~bigler/code/libedit.html - WinEditLine
http://mingweditline.sourceforge.net/ - rlcompleter — Completion function for GNU readline
https://docs.python.org/3/library/rlcompleter.html - rlwrap na GitHubu
https://github.com/hanslub42/rlwrap - rlwrap(1) – Linux man page
https://linux.die.net/man/1/rlwrap - readline(3) – Linux man page
https://linux.die.net/man/3/readline - history(3) – Linux man page
https://linux.die.net/man/3/history - Dokumentace k balíčku oglematchers
https://godoc.org/github.com/jacobsa/oglematchers - Balíček oglematchers
https://github.com/jacobsa/oglematchers - Dokumentace k balíčku ogletest
https://godoc.org/github.com/jacobsa/ogletest - Balíček ogletest
https://github.com/jacobsa/ogletest - Dokumentace k balíčku assert
https://godoc.org/github.com/stretchr/testify/assert - Testify – Thou Shalt Write Tests
https://github.com/stretchr/testify/ - package testing
https://golang.org/pkg/testing/ - Golang basics – writing unit tests
https://blog.alexellis.io/golang-writing-unit-tests/ - An Introduction to Programming in Go / Testing
https://www.golang-book.com/books/intro/12 - An Introduction to Testing in Go
https://tutorialedge.net/golang/intro-testing-in-go/ - Advanced Go Testing Tutorial
https://tutorialedge.net/golang/advanced-go-testing-tutorial/ - GoConvey
http://goconvey.co/ - Testing Techniques
https://talks.golang.org/2014/testing.slide - 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 - Afinní transformace
https://cs.wikibooks.org/wiki/Geometrie/Afinn%C3%AD_transformace_sou%C5%99adnic - package gg
https://godoc.org/github.com/fogleman/gg - Generate an animated GIF with Golang
http://tech.nitoyon.com/en/blog/2016/01/07/go-animated-gif-gen/ - Generate an image programmatically with Golang
http://tech.nitoyon.com/en/blog/2015/12/31/go-image-gen/ - The Go image package
https://blog.golang.org/go-image-package - Balíček draw2D: 2D rendering for different output (raster, pdf, svg)
https://github.com/llgcode/draw2d - Draw a rectangle in Golang?
https://stackoverflow.com/questions/28992396/draw-a-rectangle-in-golang - YAML
https://yaml.org/ - edn
https://github.com/edn-format/edn - Smile
https://github.com/FasterXML/smile-format-specification - Protocol-Buffers
https://developers.google.com/protocol-buffers/ - Marshalling (computer science)
https://en.wikipedia.org/wiki/Marshalling_(computer_science) - Unmarshalling
https://en.wikipedia.org/wiki/Unmarshalling - Introducing JSON
http://json.org/ - Package json
https://golang.org/pkg/encoding/json/ - The Go Blog: JSON and Go
https://blog.golang.org/json-and-go - Go by Example: JSON
https://gobyexample.com/json - Writing Web Applications
https://golang.org/doc/articles/wiki/ - Golang Web Apps
https://www.reinbach.com/blog/golang-webapps-1/ - Build web application with Golang
https://legacy.gitbook.com/book/astaxie/build-web-application-with-golang/details - Golang Templates – Golang Web Pages
https://www.youtube.com/watch?v=TkNIETmF-RU - Simple Golang HTTPS/TLS Examples
https://github.com/denji/golang-tls - Playing with images in HTTP response in golang
https://www.sanarias.com/blog/1214PlayingwithimagesinHTTPresponseingolang - MIME Types List
https://www.freeformatter.com/mime-types-list.html - Go Mutex Tutorial
https://tutorialedge.net/golang/go-mutex-tutorial/ - Creating A Simple Web Server With Golang
https://tutorialedge.net/golang/creating-simple-web-server-with-golang/ - Building a Web Server in Go
https://thenewstack.io/building-a-web-server-in-go/ - How big is the pipe buffer?
https://unix.stackexchange.com/questions/11946/how-big-is-the-pipe-buffer - How to turn off buffering of stdout in C
https://stackoverflow.com/questions/7876660/how-to-turn-off-buffering-of-stdout-in-c - setbuf(3) – Linux man page
https://linux.die.net/man/3/setbuf - setvbuf(3) – Linux man page (stejný obsah jako předchozí stránka)
https://linux.die.net/man/3/setvbuf - Select waits on a group of channels
https://yourbasic.org/golang/select-explained/ - Rob Pike: Simplicity is Complicated (video)
http://www.golang.to/posts/dotgo-2015-rob-pike-simplicity-is-complicated-youtube-16893 - Algorithms to Go
https://yourbasic.org/ - Využití knihovny Pygments (nejenom) pro obarvení zdrojových kódů
https://www.root.cz/clanky/vyuziti-knihovny-pygments-nejenom-pro-obarveni-zdrojovych-kodu/ - 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/ - Go Defer Simplified with Practical Visuals
https://blog.learngoprogramming.com/golang-defer-simplified-77d3b2b817ff - 5 More Gotchas of Defer in Go — Part II
https://blog.learngoprogramming.com/5-gotchas-of-defer-in-go-golang-part-ii-cc550f6ad9aa - The Go Blog: Defer, Panic, and Recover
https://blog.golang.org/defer-panic-and-recover - The defer keyword in Swift 2: try/finally done right
https://www.hackingwithswift.com/new-syntax-swift-2-defer - Swift Defer Statement
https://andybargh.com/swift-defer-statement/ - Modulo operation (Wikipedia)
https://en.wikipedia.org/wiki/Modulo_operation - Node.js vs Golang: Battle of the Next-Gen Languages
https://www.hostingadvice.com/blog/nodejs-vs-golang/ - The Go Programming Language (home page)
https://golang.org/ - GoDoc
https://godoc.org/ - Go (programming language), Wikipedia
https://en.wikipedia.org/wiki/Go_(programming_language) - Go Books (kniha o jazyku Go)
https://github.com/dariubs/GoBooks - The Go Programming Language Specification
https://golang.org/ref/spec - Go: the Good, the Bad and the Ugly
https://bluxte.net/musings/2018/04/10/go-good-bad-ugly/ - Package builtin
https://golang.org/pkg/builtin/ - Package fmt
https://golang.org/pkg/fmt/ - The Little Go Book (další kniha)
https://github.com/dariubs/GoBooks - The Go Programming Language by Brian W. Kernighan, Alan A. A. Donovan
https://www.safaribooksonline.com/library/view/the-go-programming/9780134190570/ebook_split010.html - Learning Go
https://www.miek.nl/go/ - Go Bootcamp
http://www.golangbootcamp.com/ - Programming in Go: Creating Applications for the 21st Century (další kniha o jazyku Go)
http://www.informit.com/store/programming-in-go-creating-applications-for-the-21st-9780321774637 - Introducing Go (Build Reliable, Scalable Programs)
http://shop.oreilly.com/product/0636920046516.do - Learning Go Programming
https://www.packtpub.com/application-development/learning-go-programming - The Go Blog
https://blog.golang.org/ - Getting to Go: The Journey of Go's Garbage Collector
https://blog.golang.org/ismmkeynote - Go (programovací jazyk, Wikipedia)
https://cs.wikipedia.org/wiki/Go_(programovac%C3%AD_jazyk) - Rychle, rychleji až úplně nejrychleji s jazykem Go
https://www.root.cz/clanky/rychle-rychleji-az-uplne-nejrychleji-s-jazykem-go/ - Installing Go on the Raspberry Pi
https://dave.cheney.net/2012/09/25/installing-go-on-the-raspberry-pi - 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 - Niečo málo o Go – Golang (slovensky)
http://golangsk.logdown.com/ - How Many Go Developers Are There?
https://research.swtch.com/gophercount - Most Popular Technologies (Stack Overflow Survery 2018)
https://insights.stackoverflow.com/survey/2018/#most-popular-technologies - Most Popular Technologies (Stack Overflow Survery 2017)
https://insights.stackoverflow.com/survey/2017#technology - JavaScript vs. Golang for IoT: Is Gopher Winning?
https://www.iotforall.com/javascript-vs-golang-iot/ - The Go Programming Language: Release History
https://golang.org/doc/devel/release.html - Go 1.11 Release Notes
https://golang.org/doc/go1.11 - Go 1.10 Release Notes
https://golang.org/doc/go1.10 - Go 1.9 Release Notes (tato verze je stále používána)
https://golang.org/doc/go1.9 - Go 1.8 Release Notes (i tato verze je stále používána)
https://golang.org/doc/go1.8 - Go on Fedora
https://developer.fedoraproject.org/tech/languages/go/go-installation.html - Writing Go programs
https://developer.fedoraproject.org/tech/languages/go/go-programs.html - The GOPATH environment variable
https://tip.golang.org/doc/code.html#GOPATH - Command gofmt
https://tip.golang.org/cmd/gofmt/ - The Go Blog: go fmt your code
https://blog.golang.org/go-fmt-your-code - C? Go? Cgo!
https://blog.golang.org/c-go-cgo - 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/ - 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 - Gofmt No Longer Allows Spaces. Tabs Only
https://news.ycombinator.com/item?id=7914523 - Why does Go „go fmt“ uses tabs instead of whitespaces?
https://www.quora.com/Why-does-Go-go-fmt-uses-tabs-instead-of-whitespaces - Interactive: The Top Programming Languages 2018
https://spectrum.ieee.org/static/interactive-the-top-programming-languages-2018 - Go vs. Python
https://www.peterbe.com/plog/govspy - PackageManagementTools
https://github.com/golang/go/wiki/PackageManagementTools - A Tour of Go: Type inference
https://tour.golang.org/basics/14 - Go Slices: usage and internals
https://blog.golang.org/go-slices-usage-and-internals - Go by Example: Slices
https://gobyexample.com/slices - What is the point of slice type in Go?
https://stackoverflow.com/questions/2098874/what-is-the-point-of-slice-type-in-go - The curious case of Golang array and slices
https://medium.com/@hackintoshrao/the-curious-case-of-golang-array-and-slices-2565491d4335 - Introduction to Slices in Golang
https://www.callicoder.com/golang-slices/ - Golang: Understanding ‚null‘ and nil
https://newfivefour.com/golang-null-nil.html - What does nil mean in golang?
https://stackoverflow.com/questions/35983118/what-does-nil-mean-in-golang - nils In Go
https://go101.org/article/nil.html - Go slices are not dynamic arrays
https://appliedgo.net/slices/ - Go-is-no-good (nelze brát doslova)
https://github.com/ksimka/go-is-not-good - Rust vs. Go
https://news.ycombinator.com/item?id=13430108 - Seriál Programovací jazyk Rust
https://www.root.cz/serialy/programovaci-jazyk-rust/ - Modern garbage collection: A look at the Go GC strategy
https://blog.plan99.net/modern-garbage-collection-911ef4f8bd8e - Go GC: Prioritizing low latency and simplicity
https://blog.golang.org/go15gc - Is Golang a good language for embedded systems?
https://www.quora.com/Is-Golang-a-good-language-for-embedded-systems - Running GoLang on an STM32 MCU. A quick tutorial.
https://www.mickmake.com/post/running-golang-on-an-mcu-a-quick-tutorial - Go, Robot, Go! Golang Powered Robotics
https://gobot.io/ - Emgo: Bare metal Go (language for programming embedded systems)
https://github.com/ziutek/emgo - UTF-8 history
https://www.cl.cam.ac.uk/~mgk25/ucs/utf-8-history.txt - Less is exponentially more
https://commandcenter.blogspot.com/2012/06/less-is-exponentially-more.html - Should I Rust, or Should I Go
https://codeburst.io/should-i-rust-or-should-i-go-59a298e00ea9 - Setting up and using gccgo
https://golang.org/doc/install/gccgo - Elastic Tabstops
http://nickgravgaard.com/elastic-tabstops/ - Strings, bytes, runes and characters in Go
https://blog.golang.org/strings - Datový typ
https://cs.wikipedia.org/wiki/Datov%C3%BD_typ - Seriál o programovacím jazyku Rust: Základní (primitivní) datové typy
https://www.root.cz/clanky/programovaci-jazyk-rust-nahrada-c-nebo-slepa-cesta/#k09 - 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 - Seriál o programovacím jazyku Rust: Řezy (slice) vektoru
https://www.root.cz/clanky/prace-s-vektory-v-programovacim-jazyku-rust/#k05 - Printf Format Strings
https://www.cprogramming.com/tutorial/printf-format-strings.html - Java: String.format
https://docs.oracle.com/javase/8/docs/api/java/lang/String.html#format-java.lang.String-java.lang.Object…- - Java: format string syntax
https://docs.oracle.com/javase/8/docs/api/java/util/Formatter.html#syntax - Selectors
https://golang.org/ref/spec#Selectors - Calling Go code from Python code
http://savorywatt.com/2015/09/18/calling-go-code-from-python-code/ - Go Data Structures: Interfaces
https://research.swtch.com/interfaces - How to use interfaces in Go
http://jordanorelli.com/post/32665860244/how-to-use-interfaces-in-go - Interfaces in Go (part I)
https://medium.com/golangspec/interfaces-in-go-part-i-4ae53a97479c - Part 21: Goroutines
https://golangbot.com/goroutines/ - Part 22: Channels
https://golangbot.com/channels/ - [Go] Lightweight eventbus with async compatibility for Go
https://github.com/asaskevich/EventBus - What about Trait support in Golang?
https://www.reddit.com/r/golang/comments/8mfykl/what_about_trait_support_in_golang/ - 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/ - Control Flow
https://en.wikipedia.org/wiki/Control_flow - Structured programming
https://en.wikipedia.org/wiki/Structured_programming - Control Structures
https://www.golang-book.com/books/intro/5 - Control structures – Go if else statement
http://golangtutorials.blogspot.com/2011/06/control-structures-if-else-statement.html - Control structures – Go switch case statement
http://golangtutorials.blogspot.com/2011/06/control-structures-go-switch-case.html - Control structures – Go for loop, break, continue, range
http://golangtutorials.blogspot.com/2011/06/control-structures-go-for-loop-break.html - Goroutine IDs
https://blog.sgmansfield.com/2015/12/goroutine-ids/ - Different ways to pass channels as arguments in function in go (golang)
https://stackoverflow.com/questions/24868859/different-ways-to-pass-channels-as-arguments-in-function-in-go-golang - justforfunc #22: using the Go execution tracer
https://www.youtube.com/watch?v=ySy3sR1LFCQ - Single Function Exit Point
http://wiki.c2.com/?SingleFunctionExitPoint - Entry point
https://en.wikipedia.org/wiki/Entry_point - Why does Go have a GOTO statement?!
https://www.reddit.com/r/golang/comments/kag5q/why_does_go_have_a_goto_statement/ - Effective Go
https://golang.org/doc/effective_go.html - GoClipse: an Eclipse IDE for the Go programming language
http://goclipse.github.io/ - GoClipse Installation
https://github.com/GoClipse/goclipse/blob/latest/documentation/Installation.md#installation - The zero value of a slice is not nil
https://stackoverflow.com/questions/30806931/the-zero-value-of-a-slice-is-not-nil - Go-tcha: When nil != nil
https://dev.to/pauljlucas/go-tcha-when-nil–nil-hic - Nils in Go
https://www.doxsey.net/blog/nils-in-go