por msamsoniuk » 11 Mai 2010 14:54
o ARM nao tem INC e DEC primeiro pq eh um processador RISC, entao a ideia eh justamente minimizar o numero de instrucoes para conseguir compactar a arquitetura e reduzir custos.
segundo pq eh um processador orientado a registros, ou seja, vc tem que tentar se doutrinar a fazer load no comeco da rotina, processar o maximo possivel em registros e soh no final descarregar com store. e nesse caso nem tem a ver com ser RISC, mas por ser uma arquitetura orientada a registros, em oposicao as tipicas arquiteturas orientadas a acumulador e memoria dos processadores de 8 bits. as vantagens sao obvias, por exemplo, compare a sequencia de incrementar uma variavel e multiplicar por dois fazendo um shift. na memoria seria algo como:
inc memoria
rshift memoria
soh que incrementar na memoria implica em 1 clock para leitura, 1 clock para incrementar e mais 1 clock para escrever, perfazendo assim 3 clocks. o rshift vai consumir recursos identicos, o que resulta em 6 clocks.
fazendo a mesma coisa em uma arquitetura orientada a registros:
load r0, memoria
inc r0
rshift r0
store r0, memoria
nessa caso vc vai gastar 1 clock para a leitura, 1 clock para incrementar, 1 clock para shiftar e 1 clock para escrever, totalizando 4 clocks. de fato, se fosse apenas incrementar a variavel diretamente, o resultado seria identico:
inc memoria
consome 3 clocks pq vc precisa de 1 para ler, 1 para incrementar e 1 para gravar... exatamente o mesmo do codigo para o processador orientado a registros:
load r0, memoria
inc r0
store r0, memoria
entao no minimo vc empata e, se precisar de mais operacoes, vc jah tem vantagens de performance, as custas do numero de instrucoes.
porem, tem algo importante no set de instrucoes... quando falamos inc memoria, que tipo de instrucao realmente eh codificado?
no caso de um hc908 usando enderecamento curto (primeiros 256 bytes de memoria), um inc implica em dois bytes. se for usar enderecamento longo (toda faixa de 64KB de memoria), um inc implica em 3 bytes. como o bus de instrucoes eh de um byte, vc teria apenas para a busca da instrucao 2 ou 3 clocks, somando entao com uma sequencia de leitura-modificacao-escrita de provavelmente mais 3 clocks, totalizando assim 5 a 6 clocks para um mero inc!
comparativamente, um inc no acumulador consome apenas 1 clock, como varias outras instrucoes... entao teria que pesar na balanca: vale a pena mexer na memoria ou no acumulador?
como ele nao tem muitos registros, nao tem como pensar muito... mas em um arm ou 68000, que possuem 16 registros de 32 bits, eh muito mais economico carregar tudo para os registros e processar diretamente. compare por exemplo um strcpy() em um processador orientado a acumulador (HC908) e um processador orientado a registros (68040):
loop:
ldhx @src // 3 bytes de instrucao, 2 bytes de leitura = 5 clocks
lda @hx) // 1 byte de instrucao, 1 byte de leitura = 2 clocks
inc hx // 1 byte de instrucao = 1 clock
sthx @src // 3 bytes de instrucao, 2 bytes de escrita = 5 clocks
ldhx @dst // 3 bytes de instrucao, 2 bytes de leitura = 5 clocks
sta @hx) // 1 byte de instrucao, 1 byte de escrita = 2 clocks
inc hx // 1 byte de instrucao = 1 clock
sthx @dst // 3 bytes de instrucao, 2 bytes de escrita = 5 clocks
tsta // 1 byte de instrucao = 1 clock
jnz @loop // 2 bytes de instrucao = 2 clocks
total ae de 29 clocks para mover cada byte entre dois ponteiros de 16 bits... na outra versao, com um bus de 16 bits e orientacao a registros:
move.l @src,a0 // 3 words de instrucao, 2 word read = 5 clocks
move.l @dst,a1 // mesma coisa = 5 clocks
loop:
move.b @a0+,@a1+ // 1 word de instrucao, 1 word read com a0+, 1 word write com a0+ = 5 clocks
bnz loop // branch curto, com 1 word = 1 clock
pequeno massacre com 6 clocks por byte transferido e 10 clocks para setup inicial dos registros que operam como ponteiros src e dst. como o incremento esta embutido no modo de enderecamento ao custo de 1 clock extra por move, nao existe inc/dec no 680x0. e como o move seta o flag Z de acordo com o operando, eh uma instrucao de teste a menos.
opa, tem um pequeno truque aqui... eu consigo fazer move de memoria para memoria no 680x0 e ainda por cima setar os flags de acordo com o valor movido pq 1/4 do set de instrucoes dele eh composto apenas de move... provavelmente eh o processador que dedica mais espaco de microcodigo para move, pq tem todo tipo possivel!
provavelmente em um RISC mais puramente load/store, o move seria quebrado em dois, com um load seguido de um store... de qq forma, a diferenca de performance seria minima e a maioria dos processadores RISC possui auto-incremento de ponteiros.
nas contas eu ainda nao inclui coisas tipo cache, overlap de instrucoes, execucao fora de ordem e outras coisas que o 680x0 faz. por outro lado, o acesso a memoria externa em apenas 1 clock eh algo bastante otimista (embora tenha compensado usando bus de apenas 16 bits).