-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmain.slide
223 lines (145 loc) · 5.93 KB
/
main.slide
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
# 1,2, já e 3!
Sincronizando código concorrente
Alex Rios
alexrios.dev
## Quem sou eu?
.image avatar-redondo.png _ 300
- Community Organizer - Golang CWB
- Tech Reviewer - Manning Publications
- Tech Lead - Wildlife Studios
## Desafios no dia-a-dia
- APIs com 100k+ RPM
- 800 TB de dados sendo processados todos os dias
- Milhões de jogadores conectados simultaneamente em partidas multiplayer em todo planeta
.image wildlife-logo.png _ 400
## Go Design
Go was designed at Google in 2007 to improve programming productivity in an era of **multicore**, networked machines and large codebases.
## Concorrência
Já ouviu falar alguma vez no contexo de concorrência?
- Wait Groups
- Mutexes
- Channels
## Data Race
.image data_race.png _ 500
## Qual retorno dessa função?
.play -numbers -edit datarace/1-1-naive/datarace.go /^func Datarace/,/^}/
## Dá para confiar?
.play -numbers -edit datarace/1-2-naive-test/datarace.go /^func Test/,/^}/
## Por que funciona na maioria das vezes?
CPU BOUND
Esse algoritimo normalmente **não** vai ser interrompido ao menos que aconteça algum ruído ou apenas preempção mesmo.
O cenário mais comum com extremo ruído é um ambiente na cloud.
## Criando ruído
.play -numbers -edit datarace/2-noise/datarace.go /^func Datarace/,/^}/
## OK! syscall
.play -numbers -edit datarace/3-common-noise/datarace.go /^func Datarace/,/^}/
## Por que falha mais constantemente?
Syscall, nesse caso I/O é uma excelente dica para o Go Scheduler entender que pode aproveitar
melhor o tempo executando outros processos que precisam do processador mais que apenas ficar esperando.
Dentro da sua App isso seria uma chamada para o banco de dados, outro serviço, escrita de logs e etc.
(SIM! log aumenta latência do seu programa)
## Por que o scheduler interrompe uma go routine durante a syscall?
Quando sua App:
- manda um pacote BRA - USA - BRA leva 150ms
- lê sequencialmente 1MB de um SSD leva 1ms
- faz um roundtrip no mesmo datacenter isso leva 0.5ms
- manda 1KB numa rede gigabit leva 10µs
10µs é incrivelmente rápido, certo?
## Por que o scheduler interrompe uma go routine durante a syscall?
ex: Intel i7 com 3GHz com 4 cores.
**3GHz (3 clock cycles/ns) * 4 instructions per cycle = 12 instructions per ns!**
Ou seja:
```
1 ns ............. 1 ns .............. 12 instructions (one)
1 µs .......... 1000 ns .......... 12,000 instructions (thousand)
1 ms ..... 1,000,000 ns ...... 12,000,000 instructions (million)
1 s .. 1,000,000,000 ns .. 12,000,000,000 instructions (billion)
```
## Por que o scheduler interrompe uma go routine durante a syscall?
Com 10µs desperdiçamos 120.000 instruções do processador!
## Atomic Structures
## Atomic Structures
.play -numbers -edit datarace/4-1-atomic/datarace.go /^func Datarace/,/^}/
## Mutexes
Eu não quero mudar a lógica do meu programa.
Eu quero apenas delimitar quando acessos simultâneos podem acontecer.
## Mutex
.play -numbers datarace/5-mutex/mutex.go /^func Datarace/,/^}/
## Tive uma ideia genial
.play -numbers datarace/5-mutex/mutex.go /^func DataraceMultiple/,/^}/
## Essas interrupções tem um preço
.image datarace/5-mutex/benchmark-mutexes.png _ 1000
## De 2 para 50_000 go routines
.image datarace/6-mutex-latency/latencia-mutex.png _ 1000
Toda essa sincronização gera back pressure (latência).
## Proverbs
Don't communicate by sharing memory, share memory by communicating.
Comunicação nesse contexto é mesma coisa que sinalização.
E como fazemos sinalização em Go?
## Channels
Channels não são filas de mensagens, mas canais de sinalização.
Para falar de channels temos que pensar primeiro na garantia da recepção do sinal.
## Garantias
Uma go routine **PRECISA** que outra receba a sinalização?
Pense num aperto de mãos.
Uma pessoa sinaliza que quer fazer apertar a sua mão, mas sem outra pessoa receber esse aperto de mão, você fica em *deadlock*.
## Garantias
Ter essa garantia gera latência, pois uma go routine **não vai continuar** seu trabalho sem a sinalização acontecer.
Quando queremos ter essa garantia usamos o unbuffered channel:
```
handshaker := make(chan bool)
```
## Diminuindo a latência
Go routine 1 (volley machine)
Go routine 2 (Humano recarregando)
.image volleyball-machine.jpeg _ 400
```
superShooter := make(chan Ball, 6) // Não temos garantia
```
```
singleShooter := make(chan Ball, 1) // Garantia atrasada
```
## Tudo começa aqui
- Esperando pela tarefa
- Esperando pelo resultado
- Esperando pela finalização
## Esperando pela tarefa
Passa a tarefa para uma go routine e espera ela acabar.
Não temos garantia da ordem que serão executados o sender ou o receiver.
.play -numbers channels/waiting-for-task/main.go /^func waitForTask/,/^}/
## Esperando pelo resultado
a go routine já conhece a tarefa que deve ser feita e esperamos por ela acabar.
Não temos garantia da ordem que serão executados o sender ou o receiver.
.play -numbers channels/waiting-for-result/main.go /^func waitForResult/,/^}/
## Patterns
Pattern básico:
- pooling
Patterns avançados:
- fan out semaphore
- bounded work pooling
- drop
Outros:
- cancellation
- retry timeout
- channel cancellation
## Esperando pela finalização
Nesse pattern não estamos interessados no resultado, apenas na sua finalização.
.play -numbers channels/waiting-for-finished/main.go /^func waitForFinished/,/^}/
*Esse é o típico caso que podemos resolver com um `sync.WaitGroup`.*
## Considerações finais: devo usar mutexes ou channels?
Os mecanismos de lock da linguagem estão no pacote `sync`.
A maioria dos problemas podem ser resolvido tanto com locks quanto com channels.
## Qual escolher?
O que for o mais expressivo e/ou o mais simples.
Se mutex for melhor para o seu problema, use!
**PREFIRA O PRAGMATISMO!**
## Não me ajudou em nada, tem algum guideline?
TEM!
**use channel para**:
- passar ownership do dado
- distribuir unidades de trabalho
- comunicar resultados de forma assíncrona
**use mutex para**:
- caches
- estado