Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Otimização em Go: fomos longe demais?

Otimização em Go: fomos longe demais?

Código Go é código eficiente.

A linguagem é feita para rodar rápido, com overhead baixo, consumindo poucos recursos da máquina. Múltiplos casos de sucesso existem de sistemas refeitos em Go vindo de diversas outras linguagem onde os custos caíram, os tempos de resposta ficaram melhores e se usou menos hardware. A standard library é repleta de exemplos de código feito com performance em mente, escovando bytes e ciclos de CPU. Sistemas críticos suportando aplicações diversas como Docker e Kubernetes são escritos em Go.

O foco da linguagem em performance traz enormes benefícios para nós, programadores Go, que só pela escolha da linguagem naturalmente criamos sistemas eficientes e rápidos sem fazer grande esforço. No entanto, continuamos buscando maneiras mais inteligentes de escrever nossos programas para economizar ainda mais tempo e memória, mesmo quando nossas aplicações não tem uma demanda de performance tão alta. Por quê?

A promessa da alta performance leva à busca de ainda mais performance, em muitos casos cegamente e desnecessariamente, mas a otimização não vem de graça. Código otimizado frequentemente é código mais difícil de ler e de dar manutenção. Ganhamos ciclos de máquina e perdemos horas lutando contra um código mais complicado. Quando faz sentido fazer a escolha por melhor performance?

Código deve ser escrito em primeiro lugar para seres humanos e só em segundo lugar para máquinas. Leitura de código e manutenção de código são as principais atividades do dia a dia de uma pessoa desenvolvedora. Go nos dá as ferramentas para medir performance, para criar benchmarks, para entender gargalos nas nossas aplicações e nós devemos usá-las. Código bom e legível é possível sem sacrificar performance.

Vitor De Mario

September 28, 2019
Tweet

More Decks by Vitor De Mario

Other Decks in Programming

Transcript

  1. 6

  2. 7

  3. 8

  4. 9

  5. 10

  6. 11

  7. 12

  8. 13

  9. 14

  10. Histórias de terror - o caso defer - Lock/unlock de

    um mutex com e sem defer: BenchmarkMutexDeferUnlock-8 20000000 96.6 ns/op BenchmarkMutexUnlock-8 100000000 19.5 ns/op Using defer is almost five times slower in this test. To be fair, we’re looking at a difference of 77 nanoseconds, but in a tight loop on a critical path, this adds up. - 77 nanosegundos mais rápido. "This adds up". Para acumular 1 segundo de ganho tem que rodar 130 milhões de vezes. 15
  11. Histórias de terror - json e reflection - encoding/json usa

    reflection e existem outras libs fora da stdlib que fazem encoding de json mais rápido. É fácil ter ganhos em tese trocando a lib mas também é fácil continuar "melhorando" e perder o controle. 16
  12. Histórias de terror - json e reflection - encoding/json usa

    reflection e existem outras libs fora da stdlib que fazem encoding de json mais rápido. É fácil ter ganhos em tese trocando a lib mas também é fácil continuar "melhorando" e perder o controle: Of course, if we’re concerned about performance, we should really avoid JSON altogether. MessagePack is a better option with serialization code that can also be generated. 17
  13. Histórias de terror - json e reflection - encoding/json usa

    reflection e existem outras libs fora da stdlib que fazem encoding de json mais rápido. É fácil ter ganhos em tese trocando a lib mas também é fácil continuar "melhorando" e perder o controle: Of course, if we’re concerned about performance, we should really avoid JSON altogether. MessagePack is a better option with serialization code that can also be generated. - E depois do encoding você manda tudo pela rede o que é ordens de grandeza mais lento que o encoding. 18
  14. Histórias de terror - json e reflection - Ou você

    pode perder completamente o controle também: If we’re really trying to micro-optimize, we should also be careful to avoid using interfaces 19
  15. Histórias de terror - pointeiros vs cópias, a revanche Naturally,

    passing by reference is faster than passing by value since the former requires copying only a pointer while the latter requires copying values. The difference is negligible with the struct used in these benchmarks 20
  16. Histórias de terror - pointeiros vs cópias, a revanche Naturally,

    passing by reference is faster than passing by value since the former requires copying only a pointer while the latter requires copying values. The difference is negligible with the struct used in these benchmarks - Spoiler: a diferença é insignificante quase sempre. 21
  17. Como começou - Um ano atrás, logo depois da GopherCon

    Brasil 2018. - Conversas sobre preocupação excessiva com otimização. - Um tweet recomendando uma otimização obscura com slices que gerava um código ilegível. Infelizmente não mais disponível. 24
  18. Performance na linguagem - 570 commits procurando por performance -

    https://github.com/golang/go/search?q=performance&type=Commits - 754 commits procurando por optimization ou optimize - https://github.com/golang/go/search?q=optimize&unscoped_q=optimize& type=Commits 28
  19. História - Premature optimization is the root of all evil

    - Knuth - Measure twice, cut once - https://dzone.com/articles/a-short-history-of-performance- engineering 32
  20. Premature optimization is the root of all evil "Programmers waste

    enormous amounts of time thinking about, or worrying about, the speed of noncritical parts of their programs, and these attempts at efficiency actually have a strong negative impact when debugging and maintenance are considered. We should forget about small efficiencies, say about 97% of the time: premature optimization is the root of all evil." - Donald Knuth 33
  21. Como podemos fazer melhor - Readability vem antes de performance.

    - Ser capaz de dar manutenção vem antes de performance. 35
  22. Como podemos fazer melhor - Readability vem antes de performance.

    - Ser capaz de dar manutenção vem antes de performance. - Medir. 36
  23. Como podemos fazer melhor - Readability vem antes de performance.

    - Ser capaz de dar manutenção vem antes de performance. - Medir. - Medir! 37
  24. Como podemos fazer melhor - Readability vem antes de performance.

    - Ser capaz de dar manutenção vem antes de performance. - Medir. - Medir! - Mediiiiiiiiiiiir!!!111 38
  25. Como podemos fazer melhor - Nunca chute que algo vai

    ser mais lento ou mais rápido. 39
  26. Como podemos fazer melhor - Nunca chute que algo vai

    ser mais lento ou mais rápido. - Faça o código mais claro que você puder para seres humanos. 40
  27. Como podemos fazer melhor - Nunca chute que algo vai

    ser mais lento ou mais rápido. - Faça o código mais claro que você puder para seres humanos. - Meça se está lento ou não e use benchmarks. 41
  28. Como podemos fazer melhor - Nunca chute que algo vai

    ser mais lento ou mais rápido. - Faça o código mais claro que você puder para seres humanos. - Meça se está lento ou não e use benchmarks. - Se nesse ponto for necessário faça profiling e/ou tracing. 42
  29. Como podemos fazer melhor - Nunca chute que algo vai

    ser mais lento ou mais rápido. - Faça o código mais claro que você puder para seres humanos. - Meça se está lento ou não e use benchmarks. - Se nesse ponto for necessário faça profiling e/ou tracing. - Aí sim mude o código para melhorar a performance no lugar certo. 43
  30. Como podemos fazer melhor - documentação - Documentação tem que

    acompanhar qualquer otimização. - Explique por que o código mudou. - Mostre os benchmarks que indicam o ganho de performance. - Deixe claro os trade-offs na compreensão do código que a otimização causou. 44
  31. Como podemos fazer melhor - Arquitetura importa mais que escovar

    bits. - Estruturas de dados importam mais que escovar bits. - Estruturas de dados importam mais que algoritmos. - Localidade dos dados passa na frente de tudo isso. 45
  32. go-perfbook 47 - Toda otimização tem um custo. - Geralmente

    esse custo se manifesta como complexidade. - Seu tempo é valioso. O tempo gasto na otimização podia ser usado para uma nova feature, uma correção de bug, uma melhoria de UX. - Otimize o seu tempo.
  33. Como medir - Pprof - profiling nativo - Flame graphs

    - profiling visual interativo e fácil de fazer drill down - Benchmarks - benchmarks vivem junto com testes unitários, use-os - Benchcmp - compare o antes e depois dos benchmarks - Benchstat - as diferenças entre os benchmarks são estatisticamente significativas? - Go tool trace - tracing do paralelismo, concorrência, uso de CPUs 48
  34. Referências - pprof: https://jvns.ca/blog/2017/09/24/profiling-go-with-pprof/ - pprof: https://blog.golang.org/profiling-go-programs - flame graphs:

    https://matoski.com/article/golang-profiling-flamegraphs/ - benchcmp: https://godoc.org/golang.org/x/tools/cmd/benchcmp - go-perfbook: https://github.com/dgryski/go-perfbook - benchstat: https://godoc.org/golang.org/x/perf/cmd/benchstat 53
  35. Referências - Handbook of mature optimization: https://web.facebook.com/notes/facebook-engineering/the-mature-optimizatio n-handbook/10151784131623920 - Palestra

    de tracing do Rhys Hiltner na gophercon de 2017: - https://speakerdeck.com/rhysh/an-introduction-to-go-tool-trace - https://www.youtube.com/watch?v=V74JnrGTwKA - O céu é o limite na utilização de Go: - https://imasters.com.br/apis-microsservicos/o-ceu-e-o-limite-na-utilizacao -de-golang 54