Olá Pes­soal!

No primeiro artigo, falei um pouco sobre como fun­ciona o processo de otimiza­ção de código real­izado pelo com­pi­lador, e dei algu­mas dicas sobre como otimizar a uti­liza­ção de var­iáveis e funções. Para quem ainda não leu, a primeira parte do artigo pode ser aces­sada aqui.

Vamos começar este artigo falando das dire­ti­vas de otimiza­ção do compilador.

Otimiza­ção do compilador

O com­pi­lador pode ser instruído para com­pi­lar o pro­grama com difer­entes obje­tivos, nor­mal­mente pri­or­izando tempo de proces­sa­mento (speed opti­miza­tion) ou tamanho de código (size optimization).

Como exem­plo, vamos acom­pan­har a tabela abaixo, que lista infor­mações sobre a com­pi­lação de dois pro­gra­mas distintos:

otimizacao compilador1 Otimização de código em Linguagem C   Parte 2

Os dois pro­gra­mas foram com­pi­la­dos com o mesmo com­pi­lador, uti­lizando os mes­mos mod­e­los de memória, mas com critérios de otimiza­ção difer­entes. Perceba que o pro­grama 1 ficou menor quando sele­cionada a otimiza­ção por tamanho de código, mas o pro­grama 2 ficou menor quando sele­cionada a otimiza­ção por tempo de proces­sa­mento! Isso sig­nifica que é sem­pre bom tes­tar as duas opções no seu código e ver­i­ficar os resul­ta­dos, antes de definir a otimiza­ção ótima para seu projeto.

Dicas de programação

1. Use o tamanho certo para as var­iáveis do seu pro­grama.

Basi­ca­mente, o com­pi­lador é preparado para com­pi­lar o código otimizado para a arquitetura-alvo do seu sis­tema. Isso sig­nifica que se você está tra­bal­hando com um micro­proces­sador de 8 bits, os cál­cu­los terão maior per­for­mance se real­iza­dos com 8 bits de  dados, em var­iáveis do tipo char. Da mesma forma, se você estiver tra­bal­hando com proces­sadores de 32 bits, os cál­cu­los terão maior per­for­mance se real­iza­dos com var­iáveis do tipo int. Isso acon­tece porque o com­pi­lador real­iza os cál­cu­los com base no tamanho de seus reg­istradores, que por sua vez são basea­dos na arquite­tura da CPU. Se a sua CPU é de 32 bits, seus reg­istradores serão de 32 bits, e se você está real­izando cál­cu­los com var­iáveis char, de 8 bits, nesta arquite­tura, o com­pi­lador terá o esforço extra de con­verter o resul­tado final, de 32 bits, em uma var­iável de 8 bits.

2. Incre­mento de var­iáveis

É muito mais efi­ciente incre­men­tar var­iáveis com o uso de oper­adores de incre­mento (ex: ind++) do que o comando de atribuição (ex: ind = ind + 1). Isso porque a maio­ria dos com­pi­ladores usam , se disponível, a instrução de incre­mento do proces­sador, o que torna o código-objeto ger­ado muito mais otimizado, con­forme exem­plo abaixo:

1
2
3
4
5
6
7
8
9
10
11
12
13
/* incremento com atribuicao */
x = x + 1;
/* codigo gerado em assembly
*  mov A, x     ; carrega o valor de x no registrador A
*  add A, 1     ; soma 1 ao registrador A
*  mov x, A     ; carrega de volta o valor do registrador A em x
*/
 
/* operador de incremento */
x++;
/* codigo gerado em assembly
*  inc x; incrementa x em 1
*/

3. Use pro­tóti­pos para funções

Pro­tótipo de função foi intro­duzido na definição do padrão ANSI para lin­guagem C como uma forma de mel­ho­rar a ver­i­fi­cação de tipos. Se o pro­tótipo da função não é dev­i­da­mente definido, o com­pi­lador pre­cis­ará “adi­v­in­har” quais os tipos de dados com que sua função tra­balha, o que diminui bas­tante a per­for­mance do sistema.

4. Não escreva códi­gos muito “inteligentes”!

Alguns pro­gra­madores acred­i­tam que, ao escr­ever em pou­cas lin­has de código fazendo uso “inteligente” de con­struções com­plexas em lin­guagem C, deixarão o código menor e mais rápido. Com­pare a funcao1() com a funcao2() abaixo. O obje­tivo das duas funções é setar o bit menos sig­ni­fica­tivo de uma var­iável se os 21 bits menos sig­ni­fica­tivos de outra var­iável forem difer­entes de zero. Ape­sar de ambos estarem real­izando cor­re­ta­mente a mesma tarefa, a funcao1()  irá gerar um código maior, porque o com­pi­lador irá mon­tar duas estru­tura condi­cionais por causa dos dois oper­adores “!!”, além de deixar o código quase inde­cifrável! A funcao2()  é mais clara, fácil de dar manutenção, e pos­sui mel­hor performance.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
void funcao1()
{
    unsigned long int a;
    unsigned char b;
 
    /* se bits 0..20 de a != 0, seta bit 1 de b */
    b |= !!(a << 11);
 
    ...
}
void funcao2()
{
    unsigned long int a;
    unsigned char b;
 
    /* se bits 0..20 de a != 0, seta bit 1 de b */
    if ( (a & 0x1FFFFF) != 0)
        b |= 0x01;
    ...
}

5. Cuidado com o uso de bibliotecas

Algu­mas vezes, o uso de ape­nas uma função de uma bib­lioteca, como printf() pode trazer junto com ela, implici­ta­mente, out­ras funções necessárias para sua uti­liza­ção, e isso pode aumen­tar bas­tante o tamanho do seu código. Quando trata­mos de sis­temas embar­ca­dos com poucos recur­sos disponíveis, às vezes é mel­hor imple­men­tar uma função sim­ples do tipo str­cat(), do que uti­lizar a de uma bib­lioteca que pode fazer o tamanho do código crescer.

Estes dois arti­gos procu­raram trazer os con­ceitos bási­cos de um com­pi­lador C para que pos­samos enten­der sua estru­tura interna e utilizá-lo da mel­hor forma pos­sível de modo a gerar um código mais otimizado e efi­ciente. Exis­tem muitas téc­ni­cas de otimiza­ção de soft­ware, a maio­ria delas aplicáveis no desen­volvi­mento de sis­temas embar­ca­dos.  Exem­pli­fiquei ape­nas algu­mas destas téc­ni­cas de pro­gra­mação, que podem ser apli­cadas no nosso dia-a-dia, para mel­ho­rar a  per­for­mance e a efi­ciên­cia de nos­sos pro­je­tos de software.

E você, e que cos­tuma fazer para otimizar seu código em lin­guagem C?

Um abraço e até a próxima!

Ser­gio Prado

VN:F [1.9.13_1145]
Rat­ing: 10.0/10 (1 vote cast)
Otimiza­ção de código em Lin­guagem C — Parte 2, 10.0 out of 10 based on 1 rating

Posts rela­ciona­dos:

  1. Otimiza­ção de código em Lin­guagem C — Parte 1
  • http://www.lfamorim.com Lucas Fer­nando Amorim

    Parabéns Sér­gio,

    Ficou muito bom o artigo — bem escrito, fácil com­preen­são, cheio de dicas valiosas. :)

    Abrçs.

    VA:F [1.9.13_1145]
    Rating: 0.0/5 (0 votes cast)
  • http://www.tiinteligente.com.br/blog Bruno

    Muito bem cara!
    Seu blog e o post estão bons!

    Con­tinue adi­cio­nando conteúdo!

    VA:F [1.9.13_1145]
    Rating: 0.0/5 (0 votes cast)
  • http://blog.marcelotoledo.org Marcelo Toledo

    Ser­gio,

    Não pare de escr­ever, os arti­gos estão muito bons!

    abraço!

    VA:F [1.9.13_1145]
    Rating: 0.0/5 (0 votes cast)
  • Andre

    Como é que con­sigo saber o tempo de proces­sa­mento do meu pro­jecto?
    obrigado

    VA:F [1.9.13_1145]
    Rating: 0.0/5 (0 votes cast)
  • http://www.fdavid.com.br Fran­cis David

    Sem­pre achei que incre­men­tar atribuindo era mais efi­ciente do que i++, acred­ito que foi culpa de ten­tar gerar codigo mais “inteligentes”.

    VA:F [1.9.13_1145]
    Rating: 0.0/5 (0 votes cast)
  • Car­los

    Muito bom :)
    Sim­ples e efi­ciente !
     
    Cumps

    VA:F [1.9.13_1145]
    Rating: 0.0/5 (0 votes cast)