Isso é bastante padrão na engenharia de software como um todo - quando você otimiza o código, o compilador pode reorganizar as coisas da maneira que desejar, desde que você não consiga diferenciar a operação. Portanto, por exemplo, se você inicializar uma variável dentro de cada iteração de um loop e nunca alterar a variável dentro do loop, o otimizador poderá mover essa inicialização para fora do loop, para que você não perca tempo com ela.
Também pode perceber que você calcula um número com o qual não faz nada antes de sobrescrever. Nesse caso, pode eliminar a computação inútil.
O problema com a otimização é que você deseja colocar um ponto de interrupção em algum trecho de código, que o otimizador moveu ou eliminou. Nesse caso, o depurador não pode fazer o que você deseja (geralmente, ele colocará o ponto de interrupção em algum lugar próximo). Portanto, para tornar o código gerado mais parecido com o que você escreveu, desative as otimizações durante a depuração - isso garante que o código que você deseja quebrar esteja realmente lá.
Você precisa ter cuidado com isso, no entanto, como dependendo do seu código, a otimização pode quebrar as coisas! Em geral, o código que é quebrado por um otimizador funcionando corretamente é na verdade apenas um código de buggy que está fugindo de algo, então você geralmente quer descobrir por que o otimizador o quebra.
Enviei esta pergunta a Jack Ganssle e foi isso que ele me respondeu:
fonte
Depende, e isso geralmente é verdade para todas as ferramentas, não apenas para o C30.
As otimizações geralmente removem e / ou reestruturam o código de várias maneiras. A instrução switch pode ser reimplementada com uma construção if / else ou, em alguns casos, pode ser removida por completo. y = x * 16 pode ser substituído por uma série de mudanças à esquerda, etc., embora esse último tipo de otimização ainda possa ser feito, geralmente é a reestruturação da instrução de controle que o recebe.
Isso pode impossibilitar a execução de um depurador no código C porque as estruturas que você definiu em C não existem mais, elas foram substituídas ou reordenadas pelo compilador em algo que o compilador acredita que será mais rápido ou ocupará menos espaço. Também pode tornar impossível definir pontos de interrupção a partir da listagem C, pois as instruções que você interrompeu podem não existir mais. Por exemplo, você pode tentar definir um ponto de interrupção dentro de uma instrução if, mas o compilador pode ter removido esse if. Você pode tentar definir um ponto de interrupção dentro de um loop while ou for, mas o compilador decidiu desenrolar esse loop para que ele não exista mais.
Por esse motivo, se você pode depurar com otimizações desativadas, geralmente é mais fácil. Você deve sempre testar novamente com as otimizações ativadas. Essa é a única maneira de descobrir que você perdeu um importante
volatile
e está causando falhas intermitentes (ou alguma outra estranheza).No caso de desenvolvimento incorporado, você deve ter cuidado com as otimizações de qualquer maneira. Especificamente em seções de código com tempo crítico, algumas interrupções, por exemplo. Nesses casos, você deve codificar os bits críticos no assembly ou usar diretivas do compilador para garantir que essas seções não sejam otimizadas, para que você saiba que eles têm um tempo de execução fixo ou um tempo de execução fixo no pior caso.
A outra pegadinha pode ser encaixar o código no uC. Você pode precisar de otimizações de densidade de código para simplesmente encaixar seu código no chip. Essa é uma das razões pelas quais geralmente é uma boa idéia começar com a maior capacidade de ROM uC de uma família e escolher apenas uma menor para fabricação, depois que seu código estiver bloqueado.
fonte
Geralmente, eu depurava com qualquer configuração que planejasse lançar. Se eu fosse liberar código otimizado, eu depuraria com código otimizado. Se eu fosse liberar código não otimizado, eu depuraria com código não otimizado. Eu Faço Isso Por Duas Razões. Primeiro, os otimizadores podem fazer diferenças de tempo significativas o suficiente para fazer com que o produto final se comporte de maneira diferente do código não otimizado. Segundo, embora a maioria seja muito boa, os fornecedores de compiladores cometem erros e o código otimizado pode produzir resultados diferentes do código não otimizado. Como resultado, eu gosto de obter o máximo de tempo possível com qualquer configuração que pretendo lançar.
Dito isto, os otimizadores podem dificultar a depuração, conforme observado nas respostas anteriores. Se eu encontrar uma seção específica do código que é difícil de depurar, desligarei temporariamente o otimizador, faça a depuração para obter o código funcionando, depois ligue novamente o otimizador e teste novamente.
fonte
Minha estratégia normal é desenvolver com a otimização final (máximo para tamanho ou velocidade, conforme apropriado), mas desative temporariamente a otimização se precisar depurar para rastrear algo. Isso reduz o risco de erros surgirem como resultado da alteração dos níveis de otimização.
Um modo típico de falha é quando o aumento da otimização faz com que bugs anteriormente invisíveis apareçam devido ao fato de você não ter declarado variáveis como voláteis quando necessário - isso é essencial para informar ao compilador que coisas não devem ser 'otimizadas'.
fonte
Use qualquer forma com a qual você libere, os depuradores e a compilação para depuração ocultam (MUITOS) erros que você não vê até compilar para a liberação. Até lá, é muito mais difícil encontrar esses bugs, vs depuração conforme você avança. 20 anos e meio agora e nunca tive uso de um gdb ou outro como depurador, sem necessidade de observar variáveis ou passo único. Centenas a milhares de linhas por dia. Portanto, é possível, não ser levado a pensar o contrário.
A compilação para depuração e, posteriormente, a compilação para liberação podem e levarão de duas a mais do que o dobro do esforço. Se você entrar em uma ligação e precisar usar uma ferramenta como um depurador, compile para que o depurador funcione com o problema específico, em seguida, volte à operação normal.
Outros problemas também são verdadeiros, como o otimizador torna o código mais rápido, para que, em particular, o seu tempo mude com as opções do compilador e que possam afetar a funcionalidade do seu programa, use novamente a opção de compilação que pode ser entregue durante toda a fase. Compiladores também são programas e têm bugs e otimizadores cometem erros e alguns não têm fé nisso. Se for esse o caso, não há nada errado com a compilação sem otimização, faça-o dessa maneira o tempo todo. O caminho que eu prefiro é compilar para otimização; se eu suspeitar de um problema do compilador, desabilite a otimização, se isso for corrigido, geralmente vai e volta às vezes, examinando a saída do assembler para descobrir o motivo.
fonte
Eu sempre desenvolvo código com -O0 (opção gcc para desativar a otimização). Quando sentir que estou no ponto em que quero começar a deixar as coisas avançarem mais em direção a um lançamento, começarei com -Os (otimizar o tamanho), pois geralmente quanto mais código você puder manter no cache, melhor será, mesmo que não seja otimizado para super-duper.
Acho que o gdb funciona muito melhor com o código -O0, e é muito mais fácil seguir se você precisar entrar no assembly. Alternar entre -O0 e -Os também permite ver o que o compilador está fazendo com seu código. Às vezes, é uma educação bastante interessante e também pode descobrir erros do compilador ... aquelas coisas desagradáveis que fazem você arrancar o cabelo tentando descobrir o que há de errado com o seu código!
Se eu realmente precisar, começarei a adicionar -fdata-section e -fcode-section com --gc-seções, que permitem ao vinculador remover funções inteiras e segmentos de dados que não são realmente usados. Há muitas pequenas coisas com as quais você pode mexer para tentar diminuir ainda mais as coisas ou torná-las mais rápidas, mas, em geral, esses são os únicos truques que acabo usando, e qualquer coisa que tenha que ser menor ou mais rápida, eu entregarei -montar.
fonte
Sim, desabilitar otimizações durante a depuração é uma prática recomendada há algum tempo, por três razões:
Muitas pessoas vão ainda mais nessa direção e enviam afirmações ativadas .
fonte
Simples: as otimizações demoram muito e podem ser inúteis se você precisar alterar esse trecho de código posteriormente no desenvolvimento. Portanto, eles podem muito bem ser uma perda de tempo e dinheiro.
Eles são úteis para módulos acabados; partes do código que provavelmente não precisarão mais de alterações.
fonte
certamente faz sentido no caso de pontos de interrupção ... pois o compilador pode remover muitas instruções que não afetam a memória.
considere algo como:
pode ser totalmente otimizado (porque
i
nunca é lido). do ponto de vista do seu ponto de interrupção, parece que ele pulou todo esse código, quando ele basicamente nem estava lá ... Presumo que seja por isso que nas funções do tipo sleep você sempre verá algo como:fonte
Se você estiver usando o depurador, eu desabilitaria as otimizações e habilitava a depuração.
Pessoalmente, acho que o depurador PIC causa mais problemas do que me ajuda a corrigir.
Eu apenas uso printf () para USART para depurar meus programas escritos em C18.
fonte
A maioria dos argumentos contra a ativação da otimização em sua compilação se resume a:
IMHO os dois primeiros são legítimos, o terceiro nem tanto. Isso geralmente significa que você tem algum código incorreto ou está confiando na exploração insegura da linguagem / implementação ou talvez o autor seja apenas um fã do bom e velho comportamento não definido do tio.
O blog Embedded in Academia tem uma coisa ou duas a dizer sobre comportamento indefinido, e este post é sobre como os compiladores o exploram: http://blog.regehr.org/archives/761
fonte