collapse

* Posts Recentes

Emulador NES em ESP32 por dropes
[13 de Março de 2024, 21:19]


Escolher Osciloscópio por jm_araujo
[06 de Fevereiro de 2024, 23:07]


TP4056 - Dúvida por dropes
[31 de Janeiro de 2024, 14:13]


Leitura de dados por Porta Serie por jm_araujo
[22 de Janeiro de 2024, 14:00]


Distancia Cabo por jm_araujo
[08 de Janeiro de 2024, 16:30]


Meu novo robô por josecarlos
[06 de Janeiro de 2024, 16:46]


Laser Engraver - Alguém tem? por almamater
[16 de Dezembro de 2023, 14:23]


Focos LED SMD por almamater
[16 de Dezembro de 2023, 14:12]


I Belive por dropes
[15 de Dezembro de 2023, 13:59]


Carga de corrente eletrónica ZPB30A1 60W por jm_araujo
[11 de Dezembro de 2023, 13:27]

Autor Tópico: Como obter o máximo de performance do vosso computador.  (Lida 1505 vezes)

0 Membros e 1 Visitante estão a ver este tópico.

Offline blabla

  • Mini Robot
  • *
  • Mensagens: 257
Como obter o máximo de performance do vosso computador.
« em: 03 de Outubro de 2021, 12:35 »
Bom dia a todos,

Os computadores atuais são incrivelmente rápidos, têm dezenas de milhares de milhões de transistors, são super escalares (tendo várias unidades de execução dentro de cada core e funcionando todas em pipeline e em paralelo, com múltiplas instruções em transito em simultâneo. Nestes pipelines pretendemos o mínimo de bolhas, pois as bolhas são oportunidades perdidas de aproveitar ao máximo a performance que têm disponível no vosso CPU), têm múltiplos cores, 8 ou 16 já são comuns, mas já existem CPU’s de 64 cores e de 128 cores dentro de um único chip. Cada core é hyper threaded, para poder aproveitar melhor as suas unidades de execução e para esconder subtilmente os tempos de acesso à memoria, tem instruções SIMD (Single Instruction Multiple Data) como as AVX512 que fazem 16 instruções FMA (Fused Muiltiply and Accumulate) f32 por ciclo de clock, tem pelo menos 3 níveis de cache, L1 (instructions, data), L2 e L3, permitem fazer prefetch de linhas de cache e assim ter os dados prontos na L2 ou L1 quando chegar a vez de os usar no código, têm um grande número de registos, com register renaming, permitem usar TLB’s de tradução de endereços de memoria virtual com dimensões muito grandes, quando o kernel do sistema operativo assim está configurado (Linux vem por omissão).

Todo este manancial significa que mesmo no CPU, sem programar para o GPU de uma placa gráfica (onde podem ter até 40 tera flops de fp32 ), um programador dito “normal”, consegue obter ganhos de performance muito grandes se estudar um pouco e se ganhar alguma experiência a fazer estas optimizações.

Todos os programadores sejam eles de Javascript, sejam eles de Python ou de Ruby, ou de outra linguagem qualquer high level, deveriam pensar bem neste slide.

Não podemos dar-nos ao luxo de estarmos a desperdiçar o poder de cálculo que já conseguimos ter nos nosso CPU’s actuais, pela utilização de linguagens menos eficientes para resolver problemas que exijam alta performance, ou que possam ser feitos com pouco esforço com mecanismos de alta performance.

Vejam o seguinte slide do Professor Patterson e pensem bem nele:

Ver a imagem em anexo:
Patterson_slide.png


Aqui está a ser dito que o mesmo algoritmo de multiplicação de matrizes feito em Python vs feito em C otimizado é mais lento em Python 62.806 x vezes!!!

O que é que isso equivale?

Equivale a que se as linguagens fossem à mesma velocidade se estivesse a usar um core a 5 GHz (5_000_000 KHz) de um computador actual em C Vs é como se em Python se estivesse a usar um core a  80 Kilo Hertz, ou seja era o equivalente a um chip de 2021 vs um CPU da década de 1970 ou 1960 de há 50 anos atrás!

E isto é na mesma máquina, só escolhendo uma linguagem e programação diferente e fazendo umas optimizações!

Por isso está ao alcance de qualquer programador!

Por isso é que é tão importante a todos os programadores conhecerem linguagens como o C, ter umas luzes do C++ e quem sabe aprender Rust, que é uma forma boa de entrar nas linguagens de alta performance de forma suave e progressiva, mas sem ter de descer o nível de abstração.

O Python, que é uma linguagem que eu gosto particularmente, só consegue ser utilizado por muita gente, pois tem o trabalho de muitos programadores dedicados e fazerem libs e módulos de alta performance em C, C++ e Rust e que depois são usados em Python. Mas como é óbvio isso cria o problema das duas linguagens de programação e da dificuldade de debug em duas linguagens em simultâneo. 

A imagem do slide anterior foi extraída de uma palestra dada na ACM.
John Hennessy and David Patterson 2017 ACM A.M. Turing Award Lecture




Para perceberem a fundo como funciona um computador atual (CPU e não só) vejam os 3 livros dos professores John L. Hennessy e David A. Patterson, eles são as principais referências sobre o tema. Comecem por ver em RISC-V pelo facto da ISA (Instruction Set Architecture) ser mais simples, com menos instruções do que a x86_64 e mais fácil de perceber.

Computer Organization and Design RISC-V Edition: The Hardware Software Interface 2nd Ed.
by David Patterson, John L. Hennessy

Computer Architecture: A Quantitative Approach 6th Ed.
by John L. Hennessy, David A. Patterson

The RISC-V Reader: An Open Architecture Atlas 1st Ed
by David Patterson, Andrew Waterman

Depois existe um outro livro também muito importante, é uma visão mais focada no programador:

Computer Systems: A Programmer's Perspective, 3 Ed
by David R. O'Hallaron, Randal E. Bryant

As  optimizações iniciais devem ser sempre feitas usando os melhores algoritmos possíveis com código dito normal, sem preocupações de optimizar para um CPU moderno, quer seja em C, C++ ou Rust ou para este efeito em qualquer outra linguagem, isto antes de se passar para as optimizações para aproveitar ao máximo um determinado CPU genérico actual, CPU específico ou GPU.

Podem encontrar boa informação sobre algoritmos no link seguinte:

How to become dangerous in Algorithms
https://github.com/joaocarvalhoopen/How_to_become_dangerous_in_algorithms

Recentemente encontrei uma referencia fantástica com as aulas de Programação Paralela tanto a nível single thread, ensinar a aproveitar ao máximo cada um dos cores super escalares, como a nível multi-thread, e depois no final com programação de GPU’s. Isto para C, C++ e Rust.

Programming Parallel Computers - In depth lectures notes
https://ppc.cs.aalto.fi/

Comparing Parallel Rust and C++
https://parallel-rust-cpp.github.io/

Estas optimizações nunca são feitas em todo o programa, normalmente existe um chamado Hot Spot onde o programa passa a maior parte do seu tempo e é só essa parte que nos devemos focar. É essa parte que devemos atacar com todos os nosso esforços. Em Linux para saberem onde estão os vossos Hot Spots podemos usar o Perf.

Compilem o vosso programa em C, C++ ou Rust na versão optimizada seja -O3 em GCC ou G++ ou seja em Rust na versão de release (opção --release no cargo). Podem ainda escolher a opção para gerar código para o vosso processador nativo. No entanto não se esqueçam que têm de gerar o executável com a tabela de simbolos de debug, de modo a que depois vejam o assembly anotado com as instruções do vosso código C, C++ ou Rust, caso contrário é muito fácil de se perderem no assembly e terão de adicionar ao vosso código labels dummy de Assembly para se situarem. Mas se adicionares a geração da tabela de símbolos de Debug tudo é mais simples. Se não me engano em GCC é a opção -g e em Rust tem de acrescentar no ficheiro .toml as duas seguintes linhas e funciona quando compilarem a versão optimizada --release.

[profile.release]
debug = true

 
Para usarem o Perf tem de o instalar, por exemplo em Linux da Debian ou derivados (como o Ubuntu) com um simples apt-get e depois tem de ver a vossa versão do kernel e tem de instalar com o apt-get o package para a vossa versão especifica de kernel. Quando o Linux fizer um update à vossa versão de kernel, vão ter de instalar de novo o package para a nova versão de kernel, mas isso é simples e instantâneo.

Para correrem temporariamente o perf vão ter que alterar a flag perf_event_paranoid para dar permissão de acesso do perf aos eventos do Kernel:

> echo -1 | sudo tee /proc/sys/kernel/perf_event_paranoid

Para fazerem o profilling do vosso executável no perf basta fazerem o seguinte, que ele gera um ficheiro .data com os dados do profilling em binário:

> perf record -F99 --call-graph dwarf <path para o executável>

Depois para verem o relatório iterativo dos dados gerados basta fazerem:

> perf report

Neste podem usar as teclas up/down das setas, para escolher a vossa função que está ordenada de-crescentemente da mais intensiva em CPU para a menos intensiva. Podem usar a tecla “+” para expandir os nós e depois podem usar a tecla “A” para ver as anotações para uma determinada função onde vai aparecer o assembly dessa função anotado com a percentagem do tempo gasta em cada bloco de instruções assembly e em que vão ver a que linhas do vosso código corresponde cada bloco de instruções assembly.   

O Perf tem muitos comandos e por exemplo o “stat” dá coisas como o IPC – Instruction Per Clock Cycle, que é a média.

Podem depois também instalar coisas como o flamegraph que usa o Perf e o DTrace para terem uma representação mais visual em formato SVG dos hot spots do vosso código.

Para saberem mais sobre Perf vejam:

perf Examples
See also the lecture.
https://www.brendangregg.com/perf.html

perf: Linux profiling with performance counters
https://perf.wiki.kernel.org/index.php/Main_Page

Systems Performance Enterprise and the Cloud 2nd Ed
by Brendan Gregg

The Rust Performance Book
https://nnethercote.github.io/perf-book/title-page.html

Para saberem a memória máxima ocupada por um processo podem usar o:

> /usr/bin/time -v <path para o executável>

Ter em atenção que este time é diferente do time da bash pelo que tem de colocar o full path para /usr/bin .

Para saberem mais sobre Rust ou Linux com muitas tips vejam:

How to Learn Modern Rust.
https://github.com/joaocarvalhoopen/How_to_learn_modern_Rust

Ou

How to learn modern Linux
https://github.com/joaocarvalhoopen/How_to_learn_modern_Linux


Obrigado,

Cumprimentos e votos de um bom fim-de-semana a todos,
João
« Última modificação: 03 de Outubro de 2021, 12:37 por blabla »

Offline jm_araujo

  • Mini Robot
  • *
  • Mensagens: 2.943
  • NERD!
Re: Como obter o máximo de performance do vosso computador.
« Responder #1 em: 03 de Outubro de 2021, 13:11 »

Diria que o senhor percebe do que está a falar ;) ,para quem não conhece:
https://en.m.wikipedia.org/wiki/Donald_Knuth

A otimização tem o seu lugar. Se vais multiplicar matrizes, não me parece razoável estar desperdiçar man hours otimizar para uma arquitetura de CPU particular (porque Intel e AMD quando entras em optimizações hard-core são diferentes), que pode quebrar com um upgrade de arquitectura, quando pelo mesmo custo podes meter uma RTX e fazer em cuda, mantendo o código portável para  várias plataformas (incluído cloud).

No emprego não sou programador, e com a constante redução de quadros, o tempo que tenho livre para tarefas fora da "rotina" é cada vez menos. Uso Python , VBA , vbs, PHP e JavaScript para automatizar tarefas e resolver problemas repetitivos. Quero lá saber se demora 10s em vez de 50us um programa que demoro 2 horas a desenvolver e testar e me poupa um par de dias de trabalho por mês. Tempo perdido a optimizar nunca seria recuperado.

Offline blabla

  • Mini Robot
  • *
  • Mensagens: 257
Re: Como obter o máximo de performance do vosso computador.
« Responder #2 em: 03 de Outubro de 2021, 14:18 »
Boa tarde jm_araujo, como é óbvio depende muito do que se está a desenvolver ou a fazer em cada momento, e para que finalidade estamos a programar. Se estamos a fazer um script para automatizar uma tarefa que já corre a uma velocidade com a qual estamos confortáveis é uma coisa, mas se estamos a desenvolver algo que é de processamento muito intenso, como por exemplo para servidores em grande escala, e em que o custo que nós pagamos a escolher bons e eficientes algoritmos vale a pena, bem como pode valer a pena, depois disso, fazer e optimizar o código para tirar partido do CPU’s modernos e ou GPU’s. Nesse caso a situação muda de figura. Para além disso alguém teve de fazer esse código Cuda que está optimizado bem como as boas libs de Python em C, C++ ou Rust que se usam em Python e essas pessoas também como é óbvio são programadores.

É tudo uma questão do detalhe e do nível até onde se pretende descer para tirar partido do hardware que que se está a usar num determinado projeto.
O exemplo do calculo da multiplicação de matrizes é só um exemplo, poderia ser outra coisa qualquer, como é óbvio esse usar multiplicações em fp32 em grande escala e como tal pode facilmente tomar partido das 16 multiplicações de fp32 das instruções AVX512. Noutros, o aumento de performance pode ser menos agressivo, no entanto no link que eu também mencionei:

Programming Parallel Computers - In depth lectures notes
https://ppc.cs.aalto.fi/

no capitulo dois, é ensinado algumas técnicas simples de como se poder ter ganhos de 200 e tal vezes com um CPU normal de meros 4 cores. Isto é ensinado passo a passo e considero este recurso de grande qualidade. Um ganho de 200x de performance num aplicação de C que use vários servidores e que faça diminuir o número de servidores necessários, e que esses servidores estejam a ser alugados e pagos por cabeça, não é de se deitar fora e em muitos casos pode compensar ao programador percorrer a milha extra e usar mais algum tempo a escolher um algoritmo melhor. A fazer experiências e testes e depois de já ter um bom algoritmo como base, então, optimizar para um processador recente genérico, mesmo que não seja para um processador especifico. Mesmo assim vai ver grandes ganhos.

Antigamente quando o hardware era muito mais lento, e se usavam mais linguagens como o C (sem ser em embebidos ou no low level), muitos dos programadores eram muito bons a fazer optimização depois de terem desenvolvidos os seus programas, de terem escolhido os melhores algoritmos e de terem medido onde eles gastavam a maior parte do seu tempo.

Atualmente quando se usam muitas linguagens como linguagens Python para tudo (que já tem uns 25 a 30 anos), entre outras, e que uma grande fatia dos programadores nunca se preocupa com optimização. Por outro lado, durante muitos anos os programadores de todo o mundo estiveram habituados que para um programa ser mais rápido bastava esperar alguns anos e ele seria automaticamente mais rápido, mas isso já terminou à mais de 10 anos :-), neste momento os processadores aumentam a sua performance a passo de caracol e por isso é que faz sentido como nunca antes aproveitar o hardware que já se tem ou a que temos acesso para um determinado projeto da forma melhor possível. Contudo como é óbvio depende do projeto e os ganhos tem de ser medidos, se eles não forem mensuráveis, não faz sentido.

Um exemplo muito recente de como a optimização tanto de algoritmos como de implementação, pode atingir patamares de performance inimagináveis, é por exemplo o Unreal Engine 5. Se procurar na net por uma demo do Unreal Engine 5 vai ver que eles conseguiram fazer um motor gráfico em que as imagem da cena estão a ser geradas em tempo real e que tem uma qualidade cinematográfica nunca antes vista num jogo em tempo real. Normalmente essa qualidade estava só presente em filmes que tinham sido renderizados em Server Farms com muito CPU’s e GPU’s. Esse é um excelente exemplo do que apesar de estarmos em 2021 ainda se consegue obter de ganhos usando bons algoritmos e boas implementações.   

Outro exemplo muito gritante de que novas abordagens algorítmicas podem trazer ganhos muito grandes, é a aplicação de técnicas de Machine Learning nomeadamente de Deep Learning a criar modelos do comportamento de simulações de CFD - Computational Fluid Dynamics em que já se tem um simulador  (normal e lento) para gerar os dados de treino para a rede neuronal. Depois a inferência sobre esse modelo para uma determinada simulação é muito mais rápida do que fazer uma simulação CFD convencional. Esta trend está a ser usada inclusive para grandes simulações meteorológicas, obtendo maior precisão ou necessitando de menores recursos computacionais.

Cumprimentos,
João