Estou escrevendo um programa para rodar em um ATmega 328 que roda em 16Mhz (é um Arduino Duemilanove, se você os conhece, é um chip AVR).
Eu tenho um processo de interrupção executando a cada 100 microssegundos. É impossível, eu diria, calcular quanto "código" você pode executar em um loop de 100 microssegundos (estou escrevendo em C, que presumivelmente é convertido em montagem e depois em uma imagem binária?).
Além disso, isso dependeria da complexidade do código (um liner gigante pode ser mais lento que várias linhas curtas, por exemplo).
Meu entendimento está correto, pois meu processador com uma taxa de clock ou 16Mhz executa 16 milhões de ciclos por segundo (isso significa 16 ciclos por microssegundo 16.000.000 / 1.000 / 1.000); E assim, se eu quiser fazer mais no meu loop de 100 microssegundos, comprar um modelo mais rápido como uma versão de 72Mhz me daria 72 ciclos por microssegundo (72.000.000 / 1.000 / 1.000)?
Atualmente, ele é um pouco lento demais, ou seja, está demorando um pouco mais de 100 microssegundos para fazer o loop (quanto tempo exatamente é muito difícil de dizer, mas gradualmente fica para trás) e eu gostaria que fizesse um pouco mais Esta é uma abordagem sã, ficando um chip mais rápido ou eu enlouqueci?
fonte
Respostas:
Em geral, o número de instruções de montagem que o dispositivo pode executar por segundo dependerá da combinação de instruções e de quantos ciclos cada tipo de instrução leva (CPI) para executar. Em teoria, você poderia contar o código do ciclo olhando o arquivo asm desmontado e a função que lhe interessa, contando todos os diferentes tipos de instruções nele e olhando as contagens do ciclo na folha de dados do seu processador de destino.
O problema de determinar o número efetivo de instruções por segundo é exacerbado em processadores mais complexos pelo fato de serem pipelined e terem caches e o que não. Este não é o caso de um dispositivo simples como um ATMega328, que é uma única instrução no processador de vôo.
Quanto a questões práticas, para um dispositivo simples como um AVR, minha resposta seria mais ou menos "sim". Dobrar a velocidade do relógio deve ter metade do tempo de execução de qualquer função. Para um AVR, no entanto, eles não rodam mais rápido que 20MHz, então você só pode "fazer overclock" do seu Arduino em outros 4MHz.
Este conselho não generaliza para um processador que possui recursos mais avançados. Dobrar a velocidade do relógio no processador Intel, na prática, não dobrará o número de instruções que ele executa por segundo (devido a erros de previsão de ramificação, falhas de cache e assim por diante).
fonte
A resposta de @ vicatcu é bastante abrangente. Uma coisa adicional a ser observada é que a CPU pode ficar em estado de espera (ciclos de CPU paralisados) ao acessar a E / S, incluindo programa e memória de dados.
Por exemplo, estamos usando um TI F28335 DSP; algumas áreas da RAM são estado de espera 0 para memória de programa e dados; portanto, quando você executa o código na RAM, ele é executado a 1 ciclo por instrução (exceto as instruções que demoram mais de 1 ciclo). Quando você executa código da memória FLASH (EEPROM embutida, mais ou menos), no entanto, ele não pode ser executado a 150MHz completos e é várias vezes mais lento.
Com relação ao código de interrupção de alta velocidade, você deve aprender várias coisas.
Primeiro, familiarize-se com seu compilador. Se o compilador faz um bom trabalho, não deve ser muito mais lento que o conjunto codificado manualmente para a maioria das coisas. (onde "muito mais lento": um fator de 2 seria bom para mim; um fator de 10 seria inaceitável) Você precisa aprender como (e quando) usar os sinalizadores de otimização do compilador e, de vez em quando, deve procurar na saída do compilador para ver como ele funciona.
Algumas outras coisas que você pode fazer o compilador para acelerar o código:
use funções embutidas (não lembro se o C suporta isso ou se é apenas um C ++), tanto para funções pequenas quanto para funções que serão executadas apenas uma ou duas vezes. A desvantagem é que as funções embutidas são difíceis de depurar, especialmente se a otimização do compilador estiver ativada. Mas eles economizam seqüências desnecessárias de chamada / retorno, especialmente se a abstração da "função" é para fins de design conceitual, e não para implementação de código.
Veja o manual do compilador para ver se ele possui funções intrínsecas - essas são funções internas dependentes do compilador que são mapeadas diretamente para as instruções de montagem do processador; alguns processadores possuem instruções de montagem que fazem coisas úteis como min / max / bit reverse e você pode economizar tempo fazendo isso.
Se você estiver fazendo cálculos numéricos, verifique se não está chamando as funções da biblioteca de matemática desnecessariamente. Tivemos um caso em que o código era algo parecido
y = (y+1) % 4
com um contador que tinha um período de 4, esperando que o compilador implementasse o módulo 4 como um AND bit a bit. Em vez disso, chamou a biblioteca de matemática. Então substituímos pory = (y+1) & 3
fazer o que queríamos.Familiarize-se com a página de hackers que movimentam os bits . Eu garanto que você usará pelo menos um desses com frequência.
Você também deve usar o (s) periférico (s) de timer da CPU para medir o tempo de execução do código - a maioria deles possui um timer / contador que pode ser configurado para ser executado na frequência do clock da CPU. Capture uma cópia do contador no início e no final do seu código crítico e você poderá ver quanto tempo leva. Se você não puder fazer isso, outra alternativa é abaixar um pino de saída no início do seu código e aumentá-lo no final, e observe essa saída em um osciloscópio para cronometrar a execução. Existem vantagens e desvantagens em cada abordagem: o cronômetro / contador interno é mais flexível (é possível cronometrar várias coisas), mas é mais difícil obter as informações, enquanto definir / limpar um pino de saída é imediatamente visível em um escopo e você pode capturar estatísticas, mas é difícil distinguir vários eventos.
Finalmente, há uma habilidade muito importante que vem com a experiência - geral e com combinações específicas de processador / compilador: saber quando e quando não otimizar . Em geral, a resposta é não otimizar. A citação de Donald Knuth é publicada com freqüência no StackOverflow (geralmente apenas a última parte):
Mas você está em uma situação em que sabe que precisa fazer algum tipo de otimização, então é hora de acertar e otimizar (ou obter um processador mais rápido, ou ambos). Você não escrever todo o seu ISR na montagem. Isso é quase um desastre garantido - se você fizer isso, dentro de meses ou até semanas você esquecerá partes do que fez e por quê, e o código provavelmente será muito quebradiço e difícil de mudar. Não é provável que sejam partes de seu código, no entanto, que são bons candidatos para a montagem.
Sinais de que partes do seu código são adequadas para codificação de montagem:
Aprenda as convenções de chamada de função do seu compilador (por exemplo, onde ele coloca os argumentos nos registros e quais registros ele salva / restaura) para que você possa escrever rotinas de montagem que podem ser chamadas por C.
No meu projeto atual, temos uma base de código bastante grande com código crítico que precisa ser executado em uma interrupção de 10kHz (100usec - soa familiar?) E não existem muitas funções escritas em assembly. Os que são, são coisas como cálculo de CRC, filas de software, compensação de ganho / compensação de ADC.
Boa sorte!
fonte
Outra coisa a ser observada - provavelmente há algumas otimizações que você pode executar para tornar seu código mais eficiente.
Por exemplo - eu tenho uma rotina que é executada a partir de uma interrupção do timer. A rotina precisa ser concluída em 52µS e precisa percorrer uma grande quantidade de memória enquanto o faz.
Consegui um grande aumento de velocidade bloqueando a variável principal do contador em um registro com (no meu µC e compilador - diferente do seu):
Não sei o formato do seu compilador - RTFM, mas haverá algo que você pode fazer para tornar sua rotina mais rápida sem precisar mudar para montagem.
Dito isso, você provavelmente pode fazer um trabalho muito melhor na otimização de sua rotina do que o compilador, portanto, mudar para montagem pode muito bem proporcionar aumentos de velocidade maciços.
fonte