# Tips de lenguaje C



## Aleinshir (Sep 23, 2015)

Hola chicos se me ha ocurrido la idea de: ¿Que tal un post donde estén trucos y tips para mejorar la eficiencia?. Esto debido a que c genera mucho código innecesario y mi lema es "Es mejor un C eficiente y ordenado, que un ASM deficiente e incompresible".

Así que, ¿que truco conocen para ahorrar memoria y ser mas eficiente?

Comienzo yo :

*CASO 1:*
En vez de:
variable = variable * (2 o 4 o 8 o 16)
usar:
variable = variable << (1 o 2 o 3 o 4 respectivamente)

En vez de:
variable = variable / (2 o 4 o 8 o 16)
usar:
variable = variable >> (1 o 2 o 3 o 4 respectivamente)

*CASO 2:*
Si tienes una estructura con muchas variables ni se te ocurra pasarla como parámetro (yo tenia una con 12 variables) y cada vez que llamaba a la función pasándole la estructura era un 1% menos de memoria ROM, es mejor declarar de manera global (No se mucho de ASM pero creo que esto es debido a que coloca cada variable en la pila antes de llamar la función).

*CASO 3:*
y por ejemplo yo tenia que hacer una comprobación si era TRUE mostrar " "(espacio) y si era FALSE mostrar la "B"
en vez de

```
int1 esModificable = TRUE;
if(esModificable == TRUE){
[INDENT]esModificable = FALSE;
lcd_putc("B");[/INDENT]
}else{
[INDENT]esModificable = TRUE;
lcd_putc(" ");[/INDENT]
}
```
use:


```
int8 esModificable = ' ';
isModificable = isModificable ^ 98;		// Cambia entre el caracter " " y el "B" para evitar if.
lcd_putc(isModificable);
```
Si se dan cuenta uso un circunflejo este es el operador XOR en binario la:
01000010 = 66 (que en ascii es "B")
y
00100000 = 32 (que en ascii es " ")
si le aplico un xor con el numero 98
01100010 = 98
01000010 = 66
desde la izquierda tomando el binario de 66 y 98: 
0 y 0 = 0
1 y 1 = 0
0 y 1 = 1
0 y 0 = 0
0 y 0 = 0
0 y 0 = 0
1 y 1 = 0
0 y 0 = 0

Sip es lo que ustedes piensan da 00100000 que es igual a 32 y si le aplican de nuevo se invierte.
Si ven es mas eficiente genera menos codigo yo ahorre un 0.2% de espacio (si se que no es mucho) pero si les falta un poco pueden aplica mini optimizaciones como esta y escribir lo demas en C y no usar la dificultad de asm.

Espero que les haya gustado (y me hayan entendido) espero sus consejos y trucos .

Como extra les dejo esta pagina que contiene mas sobre formas de optimizar http://www.ual.es/~jjfdez/IC/Practicas/optim.html.

Por cierto no estoy seguro si colocarlo aquí o en interfaz y programación xd


----------



## TRILO-BYTE (Sep 23, 2015)

Fíjate que hay un método para usar sólo registros de corrimiento en lugar de desperdiciar preciosos pines del microcontrolador.
Podemos usar el 74HC595 y el CD4021 para leer o escribir en el puerto.

La idea es simple, sólo enviamos un pulso de reloj por cada dato y completados los 8 bits, el puerto está listo.
Así podemos usar motores paso a paso, LCD, DAC y todo a 3 pines, algo muy similar al SPI.

En cuanto tenga tiempo subiré el algoritmo.


----------



## Aleinshir (Sep 23, 2015)

Ese lo estoy usando casualmente xd con el 74ls164
este es el que uso:

```
#define enable PIN_D0
#define CLOCK PIN_D4
#define DATO PIN_D3

#define PIN_OFF output_low
#define PIN_ON output_high

#define set_tris_lcd(x) set_tris_b(x)

#define LCD_TYPE 2           // 0=5x7, 1=5x10, 2=2 lines
#define LDC_LINE_TWO 0x40    // LCD RAM address for the second line

BYTE const LCD_INIT_STRING[4] = {0x20 | (lcd_type << 2), 0xf, 1, 7};
// These bytes need to be sent to the LCD
// to start it up.

void lcd_send_nibble( BYTE n ,BYTE rs) {
	int8 d,q;  
	d=8;

	for(q=0;q<4;q++){
		pin_off(CLOCK);
		output_bit(DATO,(n&d));   
		d = d >> 1;
		pin_on(CLOCK);
	}
	pin_off(CLOCK);
	output_bit(DATO,rs); 
	pin_on(CLOCK);
	pin_on(enable);
	delay_us(30);
	pin_off(enable);  
}


void lcd_send_byte( BYTE address, BYTE n ) {

	delay_cycles(1);
	pin_off(enable);

	lcd_send_nibble((n >> 4),address);
	lcd_send_nibble((n & 0xf),address);
}


void lcd_init() {
	BYTE i;
	pin_off(enable);
	delay_ms(15);
	for(i=1;i<=3;++i) {
		lcd_send_nibble(3,0);
		delay_ms(5);   
	}      
	lcd_send_nibble(2,0);
	for(i=0;i<=3;++i)
		lcd_send_byte(0,LCD_INIT_STRING[i]);
}

void lcd_gotoxy(BYTE x, BYTE y){
	BYTE address;

	if(y!=1)
		address=LDC_LINE_TWO;
	else
		address=0;

	address+=x-1;
	lcd_send_byte(0,0x80|address);
}

void lcd_putc(char c){
	switch (c){
		case '\f':
			lcd_send_byte(0,1);
			delay_ms(2);
			break;
		case '\n': 
			lcd_gotoxy(1,2);
			break;
		case '\b': 
			lcd_send_byte(0,0x10); 
			break;
		default: 
			lcd_send_byte(1,c);
			break;
	}
}
void lcd_clear(int1 line){
	int8 i;
	for(i = 1; i <= 16; i++){
		lcd_gotoxy(i, line + 1);
		lcd_send_byte(1,' ');
	}
	lcd_gotoxy(1, line + 1);
}
```


----------



## Dr. Zoidberg (Sep 23, 2015)

Yo uso una muuucho mas simple y confiable: ajusto la optimizacion del compilador para lo que necesito (minimizar espacio, maximizar velocidad... lo que sea) y lo dejo hacer su trabajo. Si el resultado no me satisface, recien entonces analizo el problema.

De las "recetas" que mencionan se desconoce la validez y el campo de aplicacion, por que cada compilador puede generar un codigo potencialmente diferente y los "ahorros" pueden terminar siendo nulos...

Ademas, la optimizacion de espacio es un problema completamente colateral si lo que se busca es velocidad de ejecucion... y no me digan que "poco codigo se ejecuta rapido" por eso es falso, y si no me creen googleen por "loop unrolling".


----------



## Aleinshir (Sep 23, 2015)

Dr. Zoidberg dijo:


> Yo uso una muuucho mas simple y confiable: ajusto la optimizacion del compilador para lo que necesito (minimizar espacio, maximizar velocidad... lo que sea) y lo dejo hacer su trabajo. Si el resultado no me satisface, recien entonces analizo el problema.
> 
> De las "recetas" que mencionan se desconoce la validez y el campo de aplicacion, por que cada compilador puede generar un codigo potencialmente diferente y los "ahorros" pueden terminar siendo nulos...
> 
> Ademas, la optimizacion de espacio es un problema completamente colateral si lo que se busca es velocidad de ejecucion... y no me digan que "poco codigo se ejecuta rapido" por eso es falso, y si no me creen googleen por "loop unrolling".



Ahh eso claro no es lo mismo hacer varias multiplicaciones (con una función por ejemplo) que ejecutarlas inline la cual tomara mas espacio pero no hace llamadas o saltos los cuales son mas lentos. A lo que me refiero es de cualquier tipo y la persona lo tome segun a sus necesidades por ejemplo TRILO-BYTE dijo que hacer uso del printf ocupa bastante espacio algo que no sabia. Yo me refiero mas a buenos hábitos o cosas que permitan una minimejora que al acumularse haga una diferencia, también dije para hacerlo mejor ordenado ya que no es lo mismo usar if-else que un switch o un while que un for (claro tampoco cosas tan basicas pero si que pasen a veces desapercibidas) si es por ahorrar espacio se puede usar recursividad pero si se quiere velocidad no se puede usar no ando buscando la solucion "divina" xd si no algo que podamos aplicar en determinado caso.
Por cierto el compilador es "bruto" puede ser muy optimizado pero no puede sustituir a un buen programador (gracias a Dios) por que si no no se necesitarían de estos ultimos (hasta que llegue el momento de que las maquinas piensen xd)


----------



## Dr. Zoidberg (Sep 23, 2015)

Ahí no acaba la historia:
La generación de código del compilador puede ser dependiente del contexto, y es casi imposible saber el código que se va a generar por que el compilador va usando y reusando registros y variables transitorias en memoria. Por eso opino que intentar aplicar "reglas de codificación optimizada a mano" no es algo muy rentable, por que terminan alterando el comportamiento normal del compilador para generar código en un contexto diferente a lo que sería normal.

Por otra parte, ahorrar memoria solo por el gusto de hacerlo y sin tener una necesidad real de ello, no conduce a nada bueno, en particular a la legibilidad y mantenimiento del código.

Digo... si vas a programar un proyecto para vos y si no sirve o no funciona solo perdés unos pocos dólares, está todo bien. Pero en una empresa que diseña productos para un mercado masivo y luego tiene que darle soporte, la forma de trabajar es diferente.... y no es bueno traer "mañas" aprendidas en experimentos caseros.


----------



## Aleinshir (Sep 23, 2015)

No son "mañas" y no es por el gusto.
Como dije en el post, si te falta espacio (un 3% o 5%) puedes aplicar estas mini mejoras y no gastar más en un PIC con más memoria.
Y no trabajo para desarrollar el firmware de un PIC, pero comúnmente en una PC en el caso de un juego o aplicación de diseño, no se valieran de optimización del compilador ni de "mañas" sino que lo escribirían en ASM.
Pero como no me pagan por eso sino que es un hobby, me gustaría conocer "mañas" que en realidad son características para hacer mi código mejor.

Por ejemplo, yo empecé hace poco con los PIC y venía con un fuerte aprendizaje del principio del menor privilegio, algo que en PIC lo veo innecesario y por hacer variables y pasarlas por funciones en vez de globales, me cobraba con la ROM.
Si hubiera visto un post de "Tips de C" y me hubieran dicho que era mejor rotar o usar variables globales, me hubiera evitado eso... digo.

Por cierto... Otro truco*:*

Si quieres ahorrar espacio (a veces no siempre) en PIC C de CCS, puedes usar la directiva #SEPARATE antes de la función, lo cual ahorrará memoria ROM pero pierdes velocidad (si el compilador va hacia inline) y usa más espacio de la pila, o puedes usar #inline si no te importa la ROM sino más la velocidad.
(A veces el compilador las coloca #inline, otras no, por eso lo de hacerlo explicito).


----------



## cosmefulanito04 (Sep 23, 2015)

Con el caso 2 mucho que digamos no estoy de acuerdo. Si se puede evitar la variable global, mejor.

Para trabajar con estructuras que van de una función a otra, para no tener una "copia" de esa estructura al pasarla, lo que se debe hacer es trabajarla por referencia, es decir pasarle a la función la posición de memoria donde está alojada la variable de la estructura, de esa forma siempre trabajás con una sola estructura.

La ventaja de trabajar por referencia, es que no todo el código puede ver esa variable de estructura y solo pueden verla aquellas funciones donde explícitamente querías que la estructura se vea.


----------



## Aleinshir (Sep 23, 2015)

cosmefulanito04 dijo:


> Con el caso 2 mucho que digamos no estoy de acuerdo. Si se puede evitar la variable global, mejor.
> 
> Para trabajar con estructuras que van de una función a otra, para no tener una "copia" de esa estructura al pasarla, lo que se debe hacer es trabajarla por referencia, es decir pasarle a la función la posición de memoria donde está alojada la variable de la estructura, de esa forma siempre trabajás con una sola estructura.
> 
> La ventaja de trabajar por referencia, es que no todo el código puede ver esa variable de estructura y solo pueden verla aquellas funciones donde explícitamente querías que la estructura se vea.



A eso me refería con el principio del menor privilegio (Las funciones deben ser capaz de acceder a la mínima cantidad de información necesaria para su correcta ejecución) sin embargo es para mejorar la seguridad y en programación defensiva no entiendo su uso en los pic. respecto a lo de pasarlo por referencia eso ahorra RAM no la memoria ROM claro que si no se hace global también se ahorra RAM al haber solo las variables necesarias, por eso solo hago global estructuras que usan muchas variables. Aunque supongo que si se le pasa la dirección de memoria no necesita cargar variables en la pila, así que también ahorraría ROM no se tendría que probar y ver si se obtienen los mismos resultados, no se me paso por la mente que quizás con la referencia no tenga que cargar cada variable si no que apunta en el inicio de la estructura... déjame pruebo a ver.. (sinceramente odio usar variables globales no dejan ver de donde salieron y a mi parecer hacen menos entendible el código).


----------



## Dr. Zoidberg (Sep 23, 2015)

El paso de estructura como parametros de una funcion implica la copia de todos los bytes del parametro actual en los del formal ya que en C el paso siempre es por valor.
En cambio si pasas un puntero tambien se copia pero solo 2 o 4 bytes de tamaño del puntero.
Lo mismo sucede si la funcion devuelve una estructura o una referencia a la misma.
Lo que propone Cosme es la solucion correcta.


----------



## Aleinshir (Sep 23, 2015)

Nop error el caso 2 si tiene valides prueba de uno que estoy programando orita diferencia entre global y por referencia.. Es de un 2,9% y solo cambie entre tenerla global o por referencia. Hice 8 veces la llamada a la misma función y hubo un incremento del 5% que suma un total de 7,9%... hice 21!! llamadas en global y no incremento ni 1% (solo 0,5%).


----------



## Dr. Zoidberg (Sep 23, 2015)

Aha...
Y a que se debe eso????


----------



## cosmefulanito04 (Sep 23, 2015)

Subí el código y lo vemos.

Por otro lado, ahí ¿hacés referencia a memoria de programa (o código) o a RAM?


----------



## Aleinshir (Sep 23, 2015)

tengo una estructura
con 
	
	



```
int16 resolution;				//	Resolucion del ciclo de trabajo.
	int16 duty;
	int16 pr2, pr2Ant;				//	PR2 y la variable que almacena el anterior.
	int8 posHZ;						//	Posicion en el arreglo hz.
	int8 cicloTrabajo;				//	Almacena ciclo de trabajo.
	int8 pScale;					//	Preescaler
	int8 limite;					//	Maximo que se puede editar ciclo de trabajo.
```
cree una variable global con la estructura

```
Pwm pwm;  //(uso typedef y #case)
```
llame a una función llamada setFrecuencia modificando pwm, primero global y después usando setFrecuencia(pwm) para modificarla. (obviamente en la declaración de la función coloque Pwm &pwm).

Cuando llamo por referencia "setFrecuencia(pwm)" ocupa alrededor de un 0,3% cada vez que la llamo.

Si lo hago global "setFrecuencia()" ocupa cada vez que la llamo un 0,023%.
cuando lo hago por referencia es desde una función llamada "generadorPwm()". ahi esta el .c (cualquier sugerencia para mejorarlo estoy abierto a opiniones xd ademas no esta completo xd).


----------



## TRILO-BYTE (Sep 24, 2015)

Una vez diseñé un taxímetro para hacer mi propia marca, cumplía todas las normas y estaba basado en un taxímetro comercial marca BLUE-H, un taxímetro mexicano.

En realidad superaba a éste taxímetro que estaba hecho con 6 displays de 7 segmentos, 4 botones, entrada de velocímetro, un buzzer, un calendario DS1320 y un PIC16F886.

Nunca supe en que estaba programado tal PIC, pero hice mi propio firmware clónico.
Lo malo que para pasar las pruebas de laboratorio, las pruebas son muy caras y piden mínimo 1000 piezas fabricadas, así que decliné a la idea del taxímetro.

Pero con lo que si me topé, fue que mi horrenda manera de programar se había consumido la ROM del microcontrolador.

Ahí fue donde me vi obligado a economizar instrucciones y me dí cuenta que varios If ocupan mas RAM y ciclos de trabajo del CPU, y ahí vi que era útil usar Switch.

Luego me dí cuenta que a veces las funciones ocupan mas memoria, tanto RAM como ROM, ahí estaba comprobando y comprobando lo que generaba el .LST del compilador.

Aunque mi adorado taxímetro no pudo ser comercializado, me ayudó a aprender a exprimir cada byte de la ROM y de la RAM.


----------



## george.manson.69 (Sep 24, 2015)

Es interesante el tema. Los tips que se mencionarán podrían ayudarnos a hacer mejor los códigos.
Vamos a publicar las optimizaciones que sabemos, para aplicarlas en proyectos que más adelante tengamos.

Por ahora pongo mi granito de arena:

```
typestruct Info{

    unsigned char* Name;
    int age;
    unsigned char* address;

};

Info Datos;


void main(void){

        Datos.Name = "Forosdeelectronica";
        Datos.age = 17;
        Datos.Address = "Internet";


}
```
Este código es simple, es para poder crear una estructura de datos y pasarlos en varias funciones que tengamos.

```
void mifuncion(Info Datos2){

       Datos2.Name = "MyName";
       Datos2.age = 256;
       Datos2.Address = "google.com";

}
```


----------



## cosmefulanito04 (Sep 24, 2015)

Desde linux usando GCC, tengo este código:


```
#include <stdio.h>

//#define REFERENCIA

struct Prueba
{
    unsigned int valor1;
    unsigned int valor2;
};

#if defined(REFERENCIA)
    void ModificarValor(struct Prueba *variable_estructura)
    {
        int aux;

        scanf("%d", &aux);
        variable_estructura->valor1=aux;

        scanf("%d", &aux);
        variable_estructura->valor2=aux;
    }
#else
    struct Prueba variable_estructura;

    void ModificarValor()
    {
        int aux;

        scanf("%d", &aux);
        variable_estructura.valor1=aux;

        scanf("%d", &aux);
        variable_estructura.valor2=aux;
    }
#endif


int main()
{
    #if defined(REFERENCIA)
        struct Prueba variable_estructura;
        ModificarValor(&variable_estructura);
    #else
        ModificarValor();
    #endif

    printf("Valor del entero 1: %d\nValor del entero 2: %d\n", variable_estructura.valor1, variable_estructura.valor2);

    return 0;
}
```

Cuyo peso final en el Release dá 6344 bytes usando el flag "-O2".

Definiendo "REFERENCIA", ahora se trabaja las estructuras por referencia, el resultado del código es exactamente igual a 6344 bytes usando el flag "-O2".

Cabe aclarar que los #if solo sirven para habilitar o deshabilitar parte del código, es decir que aquellas rutinas que no están dentro de la condición, para el compilador directamente no existen.


----------



## TRILO-BYTE (Sep 24, 2015)

Eso es muy interesante. Hay quienes usan muchas directivas del preprocesador para habilitar o deshabilitar código.

En muchos ejemplos se puede ver cuando se trabaja con microcontroladores de diferentes familias, pero el código es genérico.
Donde todos los parámetros de configuración de un microcontrolador, digamos, *FULANO, *están dentro de un #if


```
#if defined(FULANO)
//oscilador
//pines
//etc
#endif   

//si no estamos trabajando con FULANO
#elif defined(PERENGANO) 
//oscilador
//pines
//etc
#endif [/B]
```
También para preguntar en que compilador estamos trabajando.

Ejemplo:

```
#if defined( C_fulano1)
// configuraciones propias del compilador   1
    
#endif

#if defined( C_fulano2)
// configuraciones propias del compilador   2
    
#endif
```


----------



## Aleinshir (Sep 25, 2015)

cosmefulanito04 dijo:


> Desde linux usando GCC, tengo este código:
> 
> 
> ```
> ...



Hola cosmefulanito04 te equivocas quizás estas viendo el tamaño en disco y no el tamaño Cómo tal recuerda que el disco duro se divide en clusters y si el cluster es de 40kb y el archivo ocupa 1kb en disco ocupa 40 derrochando los otros 39  por eso no ves cambio debido a que en disco ocupan igual  dame un chance cuando tenga tiempo subo uno que hice igual que tu un pic 18f4550 con ccs en global ocupó 0.5%  y el lst era poco mas de 200 lineas en cambio por referencia son más de 2000 y ocupan casi un 10%  luego realice la prueba en visual studio con visual c++ y ocupa el archivo obj casi 300 bytes mas (claro para una pc no es nada si se toma la seguridad que representa el ocultamiento de información) pero un pic no necesita esa seguridad, necesita esa memoria luego mando un zip con el archivo c y una imagen comparativa.

Ahi deje el archivo abrelo y compilalo te daras cuenta de  la diferencia (Aunque exagere llamando 30 veces a la funcion pero la idea era que se notara la diferencia)

PD: Me gusta esa frase trilo-byte "De que sirve saber cosas interesantes si a nadien le importan"


----------



## cosmefulanito04 (Sep 26, 2015)

A ver, si no pudiera ver algo menor a 40kB en disco, no podría decirte con precisión que el resultado sea de 6344 bytes. Te puedo asegurar que si se puede ver diferencias en el tamaño del ejecutable. 

Sin embargo probé el mismo código con el Keil 4 en un ARM LPC1768 (obviamente sin el scanf y el printf) y efectivamente el resultado fue el mismo en los dos casos (1384 bytes).

De todas formas supongo que puede depender del compilador, la configuración y como está encarado el código. Sin embargo, desde mi punto de vista, a medida que el código se hace más complejo, mientras menos recursos globales tengas, mucho mejor.


----------



## Miembro eliminado 356005 (Sep 27, 2015)

Mi opción de ahorro de memoria favorito es la de evitar que el compilador agregue las rutinas de punto flotante al código final. Ocupan muchísimo, y buena parte de las veces podemos simularlo con operaciones en variables enteras.

Por ejemplo, si tenemos un sensor de temperatura, y como mucho nos va a dar 120 °C, y queremos usar dos dígitos decimales en la presentación, resulta que podemos hacer todas las operaciones en un entero (dos bytes) ya que el valor más alto que tenemos que guardar es 12 000. Solo hay que ajustar un poco la rutina de presentación, para la colocación del «.» entre la parte entera y la decimal.

De hecho, esto es tan frecuente que algunos compiladores lo "traen de serie", como el caso del CSS y su *declarador de tipos _fixed()*.


----------



## Aleinshir (Sep 27, 2015)

JoaquinFerrero dijo:


> Mi opción de ahorro de memoria favorito es la de evitar que el compilador agregue las rutinas de punto flotante al código final. Ocupan muchísimo, y buena parte de las veces podemos simularlo con operaciones en variables enteras.
> 
> Por ejemplo, si tenemos un sensor de temperatura, y como mucho nos va a dar 120 °C, y queremos usar dos dígitos decimales en la presentación, resulta que podemos hacer todas las operaciones en un entero (dos bytes) ya que el valor más alto que tenemos que guardar es 12 000. Solo hay que ajustar un poco la rutina de presentación, para la colocación del «.» entre la parte entera y la decimal.
> 
> De hecho, esto es tan frecuente que algunos compiladores lo "traen de serie", como el caso del CSS y su *declarador de tipos _fixed()*.


Eso lo he visto en muchos código pero no entendía el porque, buen aporte . 

Una pregunta  , ¿eso se aplica para uno de 32bit? Retomando tu ejemplo, si quiero representar 1200 grados y 2 decimales de precision, necesitaría un valor máximo de 120000, uno de 2 bytes solo almacena 65535, osea no cabe.

Entonces: ¿Solo me queda usar el de 32bit?. Si es así ¿derrochare memoria? ¿Aún si no uso operaciónes en coma flotantes?. Ejm:
Int32 variable = 1250065;
Ahí no use coma pero cuando lo muestre por pantalla el se verá 12500.65 usando lo que tu dices.

Mi gran pregunta es ¿derrochará la memoria en 32bit, aún si no uso coma?.

Otra pregunta: ahorras RAM o ROM.


----------



## Miembro eliminado 356005 (Sep 27, 2015)

Ahorras de los dos: ROM porque el programa es más corto, y RAM, porque almacenar enteros ocupa menos que los 4 u 8 bytes de los flotantes.

No entiendo lo que quieres decir. ¿Podrías reeditar tu mensaje para explicarlo algo mejor? Me faltan puntos y comas, para poder entender lo que dices. 

Si, lo que creo que dices, es que con 4 bytes sobra resolución, se pueden usar trucos, como por ejemplo que un uint16t almacene la parte entera, y un uint8 la parte decimal. Pero... el código podría complicarse un poco.


----------



## TRILO-BYTE (Sep 27, 2015)

s controvercial el char y el int

pero hay varios int

int de  8 bits
int 16 bits
int de 32 bits

depende del compilador es el tamaño estandar del int

CCS lo usa como de 8 bits y otros aveces es 32 bits o 16 bits

pero yo por lo regular rara vez ocupo int , uso en su lugar char

asi solo ocupo 8 bits en cualquier compilador siempre me marcara 8 bits

hay que recordar que cuando hacemos un contador de 0 a 10 no importa se sea un simple *char*
pero si nuestra cuenta es de 0 a 200 debemos usar *unsigned char*

rara vez ocupo cuentas mayores a 255 normalmente las calculo para que no pasen mas aya de un byte

asi ahorro de 2 a 3 bytes tanto en ROM como en RAM

que no es mucho pero en micros pequeños es evidente, aparte que en rutinas de PWM emulado y SPI emulado es mas rapido trabajar con 1 byte que con 2 bytes 

pues es muchisimo mas lento trabajar con 2 bytes juntos, si uno trabaja con 1 byte y quiere uno trabajar con 2 bytes repetimos el algoritmo que sea 2 veces y aun asi sera mucho mas rapido 

*¿por que?*
cuando trabajamos con 1 byte y hacemos el recorrido bit a bit , nuestro algoritmo debe recorrer de 0 a 255

pero si queremos trabajar 2 bytes  entonces nuestro recorrido de bit a bit sera de 0 a 65535 

¿donde nos es util estas tonterias?

1.- matrices de led
2.- control de PWM emulado "cuando se te acaban los PWM por hardware"
3.-registros de corrimiento "cuando no tienes SPI por hardware"
4.-control de luces led RGB "lo mismo que el punto No 2"
5.-DAC R2R 
6.-etc.


----------



## cosmefulanito04 (Sep 27, 2015)

Me pasó que tuve que armar una matriz de factores que dependían de senos/cosenos, el resultado la mayoría de las veces daba un valor decimal "0,xxxx", si bien no me importaba demasiado el tamaño en código, si tenía que hacer hincapié en la velocidad. 

Por tal motivo, decidí usar decimales fijo y en vez de multiplicar por múltiplos de 10, decidí usar desplazamientos a la izquierda, debido a que esos factores que tenía la matriz después se iban a multiplicar por algo, al final de la multiplicación se volvía a desplazar a la derecha (anulando el desplazamiento inicial, pero permitiendo no tener pérdidas durante la multiplicación).


----------



## Dr. Zoidberg (Sep 27, 2015)

Hace muchos años, cuando la tierra aún estaba tibia (1993), tuve que escribir un controlador de posición PID para un motor DC brushless con mando directo que *requería un tiempo de muestreo inferior a 1 ms*. El micro era un *80C196KB* de Intel corriendo a 12Mhz y había que programarlo en PLM (lenguaje de Intel medio parecido al Pascal). La posición la daba un codificador óptico con resolución de 1000 líneas por vuelta que multiplicábamos por 4 con una suerte de filtro digital por hardware para lograr la resolución necesaria en la posición final (así lográbamos cerca de 0.1º).
La unica solución para lograr el tiempo de muestreo necesario y el cálculo de la posición con esa resolución fué usar cálculo en punto fijo, así que tuve que hacer rutinas de multiplicación y división, y unos macros para sumas y restas donde la parte decimal eran 8 bits (1/256) y la parte entera eran 24 bits lo que completaban los 32 bits de un tipo escalar propio del lenguaje que no me acuerdo como se llamaba  . Por fortuna, el PLM usaba el mismo protocolo del C para las invocaciones a las funciones, así que solo era cosa de ir al stack a buscar los valores y ya.... pero me dió algo de trabajo...no era cuestion de multiplicar por 100 o por 1000...


----------



## Aleinshir (Oct 5, 2015)

Una pregunta alguien conoce como ahorrar if es que tengo que hacer Muchas comprobaciones y un solo if me ocupa como 4% de la memoria y ando escaso (dentro sólo hay 2 líneas una variable y un return) son como 8 comprobaciones del tipo
If (! (1 && 2 && 3 && 4)) siendo cada número una comprobación


----------



## Miembro eliminado 356005 (Oct 5, 2015)

Puedes usar una *tabla de despacho* en función del valor de las expresiones.

O hacer if() en cascada para discriminar los casos más frecuentes.

Necesitaría ver un poco más de código, para aconsejarte mejor.


----------



## Aleinshir (Oct 5, 2015)

Lo colocare cuando llegue a mi casa ahora no se si colocarte el código completo (son como 800 lineas) o solo el pedazo en cuestión son más que todo variables almacenadas del adc del pic


----------



## Miembro eliminado 356005 (Oct 5, 2015)

Con unas pocas líneas, donde se vea el valor que adquieren las variables, y luego unas pocas líneas donde se vean algunos if(), sería suficiente.


----------



## Aleinshir (Oct 5, 2015)

Hola te adjunto el archivo con 3 casos donde me gustaria pasarle una optimizadita (ese archivo solo ocupa el 45% del pic no el que te adjunte el completo de 800) y con los demas archivos llega al 90% y todavía me falta por agregar.

Otra cosa dices que es mejor no usar el punto flotante tengo la siguiente formula:
((1 / (2*PI*F))**2) / 0,000000000235

pense en hacerla asi
((1000000000000 / (2*PI*F))**2) / 235

de esa manera no uso flotante lo que pasa es que ese "1" es un numero muy grande y seguro necesita 32 bit asi que no se si vale la pena.

PD: F=frecuencia y va desde 10 hasta 100 mas o menos.
PDD: el "**2" es elevado a la 2.
PDDD: Es un pic 18F4550.


----------



## Miembro eliminado 356005 (Oct 5, 2015)

De la fórmula que has dado, queda reducida a
[LATEX]\frac{5*10^{22}}{47*pi^2*f^2}[/LATEX]​
Si f = 10, el resultado es 1.07787989125528e+18, mientras que
si f = 100, el resultado es 1.07787989125528e+16, con lo que es difícil que se pueda simplificar, ya que no sabemos cómo o con qué escala debe salir al exterior.

Hay que replantearse el problema desde el principio, porque estás manejando escalas muy grandes. No sé si eso es justamente lo que quieres hacer, o si el resultado va a salir en una pantalla tipo LCD o la enviarás por puerto serie a otro equipo... en fin... que faltan detalles para saber el contexto en que estamos trabajando. Por ejemplo, saber de dónde sale el 235, ya que solo podemos deducir que es igual a 47*5 

En cuanto al código, se trataría de pasar de

```
if(!((adcv[0] + 100) * 6 >= (sc.res[sc.nRes].volt[0] + 100) * 5 &&
	(sc.res[sc.nRes].volt[0] + 100) * 6 >= (adcv[0] + 100) * 5 &&
	(adcv[1] + 100) * 6 >= (sc.res[sc.nRes].volt[1] + 100) * 5 &&
	(sc.res[sc.nRes].volt[1] + 100) * 6 >= (adcv[1] + 100) * 5)) {
	sc.tmpPartFound = PART_NONE;
	allLow();
	return;
}
```
a

```
res0  = sc.res[sc.nRes].volt[0] + 100;
res05 = res0 * 5;

res1  = sc.res[sc.nRes].volt[1] + 100;
res15 = res1 * 5;

adc0  = adcv[0] + 100;
adc05 = adc0 * 5;

adc1  = adcv[1] + 100;
adc15 = adc1 * 5;

if (!(adc05 + adc0 >= res05
&&    res05 + res0 >= adc05
&&    adc15 + adc1 >= res15
&&    res15 + res1 >= adc15
)) {
```
y... si no me he equivocado en la lógica, se podría reducir a:

```
if (adc05 + adc0 < res05
||  res05 + res0 < adc05
||  adc15 + adc1 < res15
||  res15 + res1 < adc15
) {
```


----------



## Aleinshir (Oct 5, 2015)

Hola la formula es para un inductometro, el cual usa una compuerta lógica nand con capacitores, de acuerdo a la inductancia de la bobina tardan mas o menos en cargarse si los condensadores estan a full el nand se bloquea (corriente de condensador mas 5v aplicados en la otra patilla), empezando a descargarse cuando se descarga hay corriente en una sola patilla (la de 5 voltios) y empiezan a cargarse produciendo una frecuencia (mientras mayor la inductancia menor la frecuencia).

Con esto se puede medir teóricamente hasta 1000H (probado en proteus) y un mínimo de 0.8H sin perder mucha precisión ese 235 viene dado de la formula:

(C1xC2)/(C1+C2) use dos condensadores de 470nF que me dio ese valor.
(0,00000047 * 0,00000047) / (0,00000047 + 0,00000047) = 0,000000235

Yo le agregue 2 ceros mas porque con F en 240 (disculpa he recordado que una bobina de 1,8H produce una frec de 240) me daría 1,87143 en cambio con esos ceros extra da 187,143 (quería aplicar lo que dijiste) pero luego pensé que si el 235 tiene "," no sirve de mucho.

PD: La resolución basta con 2 decimales con los 2 ceros que explique arriba. (eliminando el 143).
PDD: Ese código que usas quizás no reduzca la ROM me he percatado que si se almacenan así (para aumentar legibilidad) aumenta la ROM.


----------



## Miembro eliminado 356005 (Oct 5, 2015)

Pues entonces, si no me he equivocado, se puede reducir la fórmula anterior a

[LATEX]\frac{107788.493237}{f^2}[/LATEX]​
y da un resultado con "dos decimales extra", que puedes almacenar ya en un uInt16.

El caso es que no habrá mucha ganancia: la simple presencia de la operación elevar al cuadrado y la posterior división hará que se importe la biblioteca de flotantes.

Una opción a eso, que podría resultar, es lo que en informática solemos hacer cuando hay problemas de rendimiento en la parte de la CPU, y es la cambiar potencia de CPU por memoria RAM. En tu caso, como F varía entre 10 y 240, consistiría en hacer una tabla de 230 valores con el resultado de la operación anterior, y guardarla en el programa. Entonces, ya no necesitarás realizar la operación. Simplemente, en función de F, se obtiene el resultado directo.

Con un sencillo script se puede hacer en un momento:

```
$ perl -E 'say int(107_788.493237 / $_**2) for 10..240'
1077
890
748
637
549
479
421
372
332
298
269
...
```

En cuanto a la parte de ahorro de ROM en el if(), es cierto: no hay ahorro, pero es porque esas operaciones siempre hay que hacerlas. No puedes obviarlas o deducirlas a partir de otras. Bueno, sí que puedes, pero al final, el número de adiciones y multiplicaciones es el mismo.

¿En cuánto más aumenta la ROM?


----------



## Aleinshir (Oct 5, 2015)

Bueno supongo  tendré que buscar ahorrar memoria en otros lados respecto a la tabla creo que es viable podría hacerlo hasta 255 que me daría un valor mínimo de 1,6H (así podría usar de 8 bits) aunque a mayores inductancia se pierde bastante precisión (aunque no creo que exista algo con mas de 200H si es así que? . Aunque y que tal si intento esto:

```
resultado = (int16) 10778849 / f * f;
```
Igual se importaría la librería? mi intención (con f=249) es que de 173 (con una funcion que tengo el mostrara en el lcd 1,73 aunque sea un entero).

Una pregunta como hiciste esa reconversión de la formula es muy interesante la simplificaste bastante ya que tengo otras formulas que me gustaría aplicar los mismo y me encantaría ser capaz de hacerlo por mi cuenta .

Todavía tengo que hacer una función de medición de voltaje (medirá de dos sitios distinto y corriente es un vatímetro) un medidor de capacitancia que no esta listo el código (son como 250 lineas) y hacer mi pwm (por software) con ciclo ajustable. Lo mas triste es que solo tengo 13% de la memoria del pic (4156 bytes) para implementarlo.

PD: Gracias por compartir tus conocimientos veo que tienes un foro aparte y que hay fechas incluso menores al 2005 supongo tienes una amplia experiencia en programación, lamentablemente aunque me gusta mucho la pc hace alrededor de 1 año que comencé a programar (también entre a la uni como ing. de información), sin embargo últimamente me ha llamado la atención del mundo de la electrónica.
PDD: Acabo de intentar lo que te dije arriba y ahorre 0,5% de memoria  (sin el int16 porque sino me da 0). 0,5% es algo  gracias. a retornar a la guerra de optimizado xd.
PDDD: No se le olvide decirme su secreto de simplificación xd.


----------



## Miembro eliminado 356005 (Oct 5, 2015)

Debería ser así:

```
resultado = (uint16_t) (10778849L / (f * f));
```
(los paréntesis son importantes).

Suponiendo que f no llega a 255 se puede declarar como uint8_t; f*f da un resultado que cabe en un uint16_t (entero de dos octetos sin signo); lo que queda es una división entera entre un uint32_t (un _long_, largo, atención a la 'L' final) y el uInt16. El resultado será otro uint16_t. Y ya tenemos toda una operación con enteros.

En cuanto a la simplificación, solo hay que operar con los números que ya tenemos:
[LATEX]\frac{\frac{1}{(2 \cdot \pi \cdot f)^2}}{235 \cdot 10^{-9}}=\frac{\frac{10^9}{235 \cdot 4 \cdot \pi^2}}{f^2}=\frac{ \frac {10^9}{9277.42813702}}{f^2}=\frac{10778849.3237}{f^2}[/LATEX]​


----------



## J2C (Oct 6, 2015)

.





JoaquinFerrero dijo:


> .... En cuanto a la simplificación, solo hay que operar con los números que ya tenemos:
> 
> [LATEX]\frac{\frac{1}{(2 \cdot \pi \cdot f)^2}}{235 \cdot 10^{-9}}=\frac{\frac{10^9}{235 \cdot 4 \cdot \pi^2}}{f^2}=\frac{ \frac {10^9}{9277.42813702}}{f^2}=\frac{10778849.3237}{f^2}[/LATEX]​


 
 Matemáticas pura y cruda .


 A no tenerle miedo jamás !!!!.



 Saludos, JuanKa.-


----------



## TRILO-BYTE (Oct 6, 2015)

jeje ya no son metodos de ahorro de memoria y optimizacion de codigo
se convirtio en un debate de matematicas y algebra


----------



## Miembro eliminado 356005 (Oct 9, 2015)

Otro truco que me han contado hace un par de horas. Depende del compilador, desde luego, pero el gcc-avr a veces tiene problemas para distinguir estos casos.

Supongamos que queremos hacer un bucle de 25 vueltas. Lo escribiríamos así:

```
for (i = 0; i < 25; i++) {
```
pero, según el compilador -repito-, si lo escribimos de otra manera, podemos ahorrar alguna instrucción:

```
i = 25;
do {
    # ...

} while(--i);
```
La clave está en la última línea: el compilador la traduce a una instrucción de decremento de la variable, y si el resultado es distinto de cero, repite el bucle, algo que en muchos códigos en ensamblador lo hace en una sola instrucción.


----------



## TRILO-BYTE (Oct 10, 2015)

es cierto

el do 

hace toda las instrucciones 1 vez y las veces que vienen ya son condicionadas

yo lo hacia sin el do

i=0;
while(contador != numero)
{
//codigo
i++;
}

es aveces mas rapido que usar un for , eso lo aprendi cuando hise un jueguito en borland C++ corriendo en una 486

lo habia olvidado y como dices es ddependiendo el compilador el pic al tener pocas instrucciones hace que el mismo codigo en otro CPU sea mas eficiente mientras que en el pic debe estar mas elaborado


----------



## Dr. Zoidberg (Oct 10, 2015)

El "problema" siempre es el mismo: no pueden pretender predecir el codigo que va a generar el compilador, y menos aun en cross-compilers multi-target, ya que depende de un par de miles de cosas que son contexto-dependientes.
Hay algunas tecnicas basicas de optimizacion que se pueden hacer manualmente, pero no pueden evitar el karma: "no podes tener algo sin dar algo a cambio", y si queres explotar el ultimo recurso del set de instrucciones no queda otra que usar assembler... y pagar por el excesivo tiempo de desarrollo. Determinar si vale la pena o no es una decision tambien de contexto...


----------



## Miembro eliminado 356005 (Oct 10, 2015)

No veo muy claro que un bucle while() como el que muestras sea más rápido que un bucle for(), ya que, cuando se crea el código intermedio, tanto uno como otro genera el mismo código (una inicialización, salto el final del bucle donde se coloca la condición, incremento de la variable, salto al comienzo del bucle, y contenido del bucle).

Como dice Dr. Zoidberg, depende del contexto. Un ejercicio como *Finalizar rutina* cuesta lo mismo escribirlo en C que en ensamblador.


----------



## Aleinshir (Oct 10, 2015)

Y si es muy complejo el programa y necesitas optimizar un poco. Disculpa (Dr. Zoidberg) si sueno hiriente pero ya habías dado una opinión al respecto y no contribuye con el tema. Es muy interesante Lo de los for ya que en lo personal los Uso mucho aún si me basta con el while.
Otra forma de optimizar es en los if es algo básico pero quizás algunos No lo sepan esto mejora más que todo velocidad. 

Si tienes 

```
if (día == 31|| día % 2 == 0)
```
Es más eficiente hacerlo así

```
if (día % 2 == 0 || día == 31)
```
Esto debido a que los días transcurren todos los días valga la redundancia pero es 31 una vez al mes por eso es mejor colocar la que es mas frecuente de primera (al cumplirse la primera no evalúa la segunda) a hacerlo al revés. 
Eso con el OR pero si es un AND "&&" es mejor al revés (colocando la que tiene más probabilidades de fallar de primera)

PD: habría que comprobar si hay ahorro o no luego lo compruebo


----------



## TRILO-BYTE (Oct 10, 2015)

sera?

si soy criticado por el for y el while

pero ami me consto cuando cargaba puntos en la 486 , no sabia como cargar un bitmap asi que dibujaba una figura en texto donde una letra indicaba el color.

y con for se veia que tardaba un poco mas que en el while en cargar la imagen.

esto mismo lo lleve al pic pero el resultado *NO* era similar.

nada que ver con la 486.

a lo que voy es cierto que depende del compilador, no es lo mismo compilar un codigo C para una PC, a un micro sea avr o pic.

el problema personal que veo en el CCS "no es que sea un compilador malo del todo".

pero si uno usa demaciados *if* por alguna extraña razon empiezan a haber bugs, si uno usa demaciada RAM empiezan problemas de que las variables les entra basura.

y si uno hace uso de un int16 y quiere imprimir con printf 
si se imprimira con usando %ld "long int" pero si uno usa varias veces la impresion con %ld

empieza a imprimir basura.

ahi es cuando empieza a ser criticado el compidador que hace sus patrañas.

como digo es muy propio del compilador.

otra cosa yo vi que en C18 el codigo que sale es mas largo en bytes que en el CCS 
¿por que?

no tengo idea pero ahi es cuando uno ve que es cuestion de quien hiso el compilador


----------



## Miembro eliminado 356005 (Oct 10, 2015)

Aleinshir dijo:


> Otra forma de optimizar es en los if es algo básico pero quizás algunos No lo sepan esto mejora más que todo velocidad.
> 
> Si tienes
> 
> ...


No, no hay ahorro de memoria porque el número de operaciones es siempre el mismo. Pero esta optimización es de tiempo de ejecución, y está indicada desde los primeros tiempos del C.

En el libro "The C Programming Language", de Brian W. Kernighan y Dennis M. Ritchie, Prentice Hall, 2ª ed., ISBN 0-13-110362-8, 1988, en el apartado 1.5.4 _Word Counting_, dice:


> Existe el correspondiente operador && para AND; su precedencia es justo un poco más alta que ||. Expresiones conectadas con && o || se evalúan de izquierda a derecha, y se garantiza que la evaluación parará tan pronto como se sepa la verdad o falsedad (de toda la expresión).


¡Gracias por recordarlo!



			
				TRILO-BYTE dijo:
			
		

> y si uno hace uso de un int16 y quiere imprimir con printf
> si se imprimira con usando %ld "long int" pero si uno usa varias veces la impresion con %ld
> 
> empieza a imprimir basura.
> ...


Es completamente normal que te saque basura: le estás pidiendo que saque la información de un largo (Int32_t) cuando solo tienes un Int16_t. Esto también me pasó a mi de las primeras veces que programé en C, pero la solución es entender que a C hay que decírselo todo muy claro. Y leer el manual, en la parte que explica el printf 

Entonces, o le dices que quieres imprimir un entero sin más ("%d"), o le haces un _casting_ al int16 para convertirlo en int32 antes de pasárselo al printf (algo así como *printf "%ld", (long) variable*). Es obvio que la primera solución suele ser la mejor.

Aparte de todo esto, los padres del C no recomiendan programar pensando en el orden de evaluación: podemos llevarnos alguna sorpresa.

En el mismo libro de C comentado antes, al final del segundo capítulo, dice (hablando del orden de ejecución de las sentencias):


> Los compiladores pueden interpretar esto (el último ejemplo) de formas diferentes, y generar diferentes respuestas dependiendo de su interpretación. El estándar deja muchos de esos asuntos sin especificar. Cuando ocurren efectos colaterales (asignación a variables) dentro de una expresión, se deja a la discreción del compilador. ya que el mejor orden depende estrechamente de la arquitectura de la máquina. (El estándar especifica todos los efectos colaterales en los argumentos antes de llamar a una función, pero eso no ayudaría en la llamada al printf anterior). La moraleja es que escribir código que dependa del orden de evaluación es una mala práctica de programación en cualquier lenguaje. Naturalmente, es necesario saber qué cosas evitar, pero si no sabes cómo se realizan en diferentes máquinas, no tendrás la tentación de aprovecharte de una ventaja de una determinada implementación.


----------



## Dr. Zoidberg (Oct 10, 2015)

Aleinshir dijo:


> Y si es muy complejo el programa y necesitas optimizar un poco. Disculpa (Dr. Zoidberg) si sueno hiriente pero ya habías dado una opinión al respecto y no contribuye con el tema.


Te pido disculpas, pero lo que no contribuye con el tema, y menos aún con el conocimiento es lo que vos has escrito:



> Otra forma de optimizar es en los if es algo básico pero quizás algunos No lo sepan esto mejora más que todo velocidad.
> 
> Si tienes
> 
> ...



Y lo ha puesto muy claramente el quote de Joaquin:



> Los compiladores pueden interpretar esto (el último ejemplo) de formas  diferentes, y generar diferentes respuestas dependiendo de su  interpretación. El estándar deja muchos de esos asuntos sin especificar.  Cuando ocurren efectos colaterales (asignación a variables) dentro de  una expresión, se deja a la discreción del compilador. ya que el mejor  orden depende estrechamente de la arquitectura de la máquina. (El  estándar especifica todos los efectos colaterales en los argumentos  antes de llamar a una función, pero eso no ayudaría en la llamada al  printf anterior). *La moraleja es que escribir código que dependa del  orden de evaluación es una mala práctica de programación en cualquier  lenguaje. Naturalmente, es necesario saber qué cosas evitar, pero si  no sabes cómo se realizan en diferentes máquinas, no tendrás la  tentación de aprovecharte de una ventaja de una determinada  implementación.*



que, por supuesto, fué lo que dije en el mismo comentario que calificaste como que "no aporta".


----------



## cosmefulanito04 (Oct 10, 2015)

JoaquinFerrero dijo:


> Otro truco que me han contado hace un par de horas. Depende del compilador, desde luego, pero el gcc-avr a veces tiene problemas para distinguir estos casos.
> 
> Supongamos que queremos hacer un bucle de 25 vueltas. Lo escribiríamos así:
> 
> ...



Si hacés esto, deberías obetener un resultado similar:


```
for (i = 25; i ; i--)
```

Tal como dijiste:



> La clave está en la última línea: el compilador la traduce a una instrucción de decremento de la variable, y si el resultado es distinto de cero, repite el bucle, algo que en muchos códigos en ensamblador lo hace en una sola instrucción.



Al decrementar, en la preguna del salto, solo se hace por el flag "zero" del registro de los bits de estado, en cambio si la operatoria fuera incremental, si o si se debe agregar una instrucción más de comparación (o resta) previamente a la pregunta.


----------



## TRILO-BYTE (Oct 10, 2015)

hoo O_O eso si estubo muy bien pensado nunca lo vi asi.


----------



## Aleinshir (Oct 10, 2015)

JoaquinFerrero dijo:


> No, no hay ahorro de memoria porque el número de operaciones es siempre el mismo. Pero esta optimización es de tiempo de ejecución, y está indicada desde los primeros tiempos del C.



Sorry joaquin, no me especifique, me refería a que comprobaría el ahorro de memoria del for, no del orden de los operadores, por eso especifique antes que era una mejora de velocidad. Ese tip lo leí en el libro con el que aprendí a programar y hombre debes tener memoria fotografica porque recuerdas de que libro proviene (o lo buscas por internet xd).



Dr. Zoidberg dijo:


> Te pido disculpas, pero lo que no contribuye con el tema, y menos aún con el conocimiento es lo que vos has escrito:
> 
> 
> 
> ...


Afff... Mira no quiero ponerme intenso pero no estoy diciendo que no tengas razón respecto a los compiladores. Lo que me refiero es que antes ya habías especificado que puede afectar como lo interprete el compilador. Ahora, si quieres podemos llenar el tema de mensajes repetidos sobre que el compilador puede interpretarlo de otra manera, no sé quizás al lector se le olvide que el compilador tiene que ver luego de darle clic para ver la próxima pagina y tengamos que recordarlo, no, por si se le olvida, uno no sabe quizás tenga problema como tener memoria de pez. Sarcasmo aparte no es necesario que repitas una, otra y otra vez lo mismo, ya que desde el inicio del tema has expresado tu desacuerdo con el tema.

Dime algo, es igual:

```
void add(float x, float y, float *z) 
{ 
*z = x + y; 
}
...

main() 
{ 
int i, j; 
static float x[N], y[N], z[N];

for(j=0; j<ITER; j++) 
    for(i=0; i<N; i++){ 
        add(x[i], y[i], &z[i]); 
        } 
}
```

Que hacer las iteraciones dentro de la función (dejando aparte la reusabilidad del código)

```
void add2(float *x, float *y, float *z) 
{ 
register int i, j;
for(j=0; j<ITER; j++) 
   for(i=0; i<N; i++) 
       z[i] = x[i] + y[i]; 
}

...

main() 
{ 
static float x[N], y[N], z[N];

add2(x, y, z); 
}
```
O usar operaciones de desplazamiento las cuales son instrucciones que maneja de manera natural el procesador.

Si el compilador es tan inteligente para discriminar esos casos o casos mas complejos y optimizarlos, si es así entonces me retracto tu ganas...

Ahora cuando existan computadoras mas inteligentes que el ser humano, quizás si me dedique a escribir basura de código ya que igual ella lo optimizara, ahh cierto porque para ese momento ellas serán capaces de escribir el código solas sin ayuda, ¿si los compiladores ya son tan inteligentes por que no lo hacen ya ellos solo?. Hoy en día no existe ese lujo, así que según mi muy humilde opinión deberíamos escribir código eficiente.

Respecto a la ultima cita de juaquin.

Se refiere a esto:

```
printf("%d %d", n++, funcion(n));
```

Ahí no se sabe que ocurrirá si se llama a función primero antes de sumar n o sumar n y luego llamar, esto dependería del compilador la manera de corregirlo seria:
Si quieres que primero se sume


```
n++
printf("%d %d", n, funcion(n));
```

o si quieres llamar primero a la función y luego sumar:


```
printf("%d %d, n, funcion(n));
n++;
```

o llamar a la funcion sin sumar n y mostrar n sumado:

```
tmp = n;
printf("%d", n++, funcion(tmp));
```
y podría seguir indefinidamente según el caso.

Así que no entiendo sinceramente a que te refieres, la cita trata sobre la forma en que se ejecuta el printf (si se ejecuta de derecha a izquierda o izquierda a derecha en una llamada a funcion) osea que se realiza primero si la función, o el uso de n ya que el estándar no especifica esto.

Ahora dime en que no aporta mi comentario si la cita final no tiene nada que ver con mi orden de colocación de los operando, me podrías explicar tengo rato dando vueltas pero no entiendo 

Otra cosa ¿tu crees que existiría el orden de precedencia para que los compiladores no lo sigan? sumar antes que multiplicar "a + b * c" o multiplicar fuera del paréntesis ignorándolo? "a + (b + c) * c" me parece muy descabellado re analiza el enunciado enfría tu cabeza y piensa tu próxima respuesta por favor.

PD: Cualquier error en mi respuesta por favor corrijan que muy gratamente tomare la observación.
PDD: Si alguien pudiera esclarecer este tema lo agradecería quizás soy yo el que me equivoco.


----------



## Miembro eliminado 356005 (Oct 10, 2015)

Aquí no se trata de discutir, sino de aclarar cosas. Y lo único claro es que cada compilador es de su padre y de su madre.

Ejemplo: el gcc y el gcc-avr tienen la misma raíz por lo que cuentan con el mismo conjunto de optimizaciones en el árbol sintáctico del programa una vez interpretado, *pero* la traducción de los _opcodes_ a lenguaje ensamblador es muy mala en el caso del gcc-avr (o al menos, no tan buena como en las arquitecturas 80x86).

Entonces, lo que estamos diciendo es que tú puedes trabajar mucho el código para intentar optimizarle, pero dependes también del comportamiento del compilador para obtener un resultado bueno o menos bueno.

No se trata de ver quién tiene razón porque, es que en realidad no sabemos de en qué escenario estamos hablando 

En cuanto al ejemplo de código que pones, en el primero estás llamando ITER*N llamadas a la función add(), más que el segundo código, así que el segundo es más eficiente.

Hay una parte de una asignatura en Informática (ahora no recuerdo el nombre), donde se explica cómo calcular la eficiencia, pero básicamente consiste en sumar todas las operaciones matemáticas, de salto, y de condición.

Sobre la cita del libro (que me he leído varias veces), lo que quería decir es que los padres del C recomiendan escribir código limpio frente a código "supuestamente" optimizado (los ingleses lo llaman "no pasarse de listo") porque a) puede llegar el momento en que escribas un código que pueda ser complejo de mantener en el futuro, y b) -lo dicho antes- no podemos fiarnos de cómo va a ser traducido nuestro programa cuando cambiemos de arquitectura o compilador. Así que recomiendan escribir código "limpio y claro", sin más.

Y solo, *solo*, en el caso de que tengamos problemas de memoria o consumo de CPU, sí que le dedicaremos tiempo a investigar trucos de optimización (que es justo el tema de este hilo que abriste).

Del ejemplo del printf(), el estándar dice que se tienen que resolver los argumentos, pero el compilador puede decidir cambiar el orden de evaluación. Entonces K&R recomiendan escribir esos casos de otra manera (por ejemplo, evaluar los argumentos antes de pasarlos al printf), y así queda claro el orden de ejecución.

Como regla general, hoy en día, se puede decir que los compiladores son muy buenos aunque el código sea muy malo (detectan muchos casos extremos), así que al final es mucho más importante escribir código que sea más fácil de leer por el mantenedor del código (otro ser humano), ya que es la parte más cara de todo el proceso.


----------



## Miembro eliminado 356005 (Oct 10, 2015)

cosmefulanito04 dijo:


> Si hacés esto, deberías obtener un resultado similar:
> 
> ```
> for (i = 25; i ; i--)
> ...



A ver... cuidado con la palabra "similar". Estamos hablando de ahorrar unos pocos bytes o de hacer que el código sea más eficiente.

Estamos hilando fino y eso implica que un breve cambio implica unos bytes más o menos.

Pero voy a poner un ejemplo que va a clarificar lo que quería decir.

Supongamos que queremos hacer un programa para un PIC16F877A a 4 Mhz que consiste en leer el puerto A 25 veces, con un retraso de 1 ms entre lecturas.

Tenemos este programa:

```
#include <xc.h>
#include <pic16f877a.h>
#include <stdint.h>

#define _XTAL_FREQ 4000000                  // Fosc  frequency for _delay()  library

void main(void) {
    uint8_t i;
    uint8_t a;

    for (i = 0; i < 25; i++) {
        a = PORTA;
        __delay_ms(1);
    }

    return;
}
```
El código resultado es este:

```
19:                for (i = 0; i < 25; i++) {
07DF  01F2     CLRF i                       ; i = 0

07E0  3019     MOVLW 0x19
07E1  0272     SUBWF i, W
07E2  1803     BTFSC STATUS, 0x0            ; ¿ 0 < 25 - i ? Sí, seguir
07E3  2FF9     GOTO 0x7F9                   ; no, acabar

20:                    a = PORTA;
07E4  1283     BCF STATUS, 0x5              ; <-
07E5  1303     BCF STATUS, 0x6
07E6  0805     MOVF PORTA, W
07E7  00F0     MOVWF __pcstackCOMMON
07E8  0870     MOVF __pcstackCOMMON, W
07E9  00F1     MOVWF a
21:                    __delay_ms(1);
07EA  30F9     MOVLW 0xF9
07EB  00F0     MOVWF __pcstackCOMMON
07EC  0000     NOP
07ED  0BF0     DECFSZ __pcstackCOMMON, F
07EE  2FEC     GOTO 0x7EC
07EF  2FF0     GOTO 0x7F0
07F0  0000     NOP
22:                }

07F1  3001     MOVLW 0x1                    ; i++
07F2  00F0     MOVWF __pcstackCOMMON
07F3  0870     MOVF __pcstackCOMMON, W
07F4  07F2     ADDWF i, F

07F5  3019     MOVLW 0x19
07F6  0272     SUBWF i, W
07F7  1C03     BTFSS STATUS, 0x0            ; ¿ 0 < 25 - i ? no, terminar
07F8  2FE4     GOTO 0x7E4                   ; sí, repetir  ->
23:
24:                return;
25:            }

07F9  120A     BCF PCLATH, 0x4
```
Las partes

a = PORTA;
__delay_ms(1);

siempre sin las mismas, pero lo que cambia es la forma de hacer el bucle.

Lo que nos interesa está aquí:

```
19:                for (i = 0; i < 25; i++) {
07DF  01F2     CLRF i                   ; i = 0

07E0  3019     MOVLW 0x19
07E1  0272     SUBWF i, W
07E2  1803     BTFSC STATUS, 0x0        ; ¿ 0 < 25 - i ? Sí, seguir
07E3  2FF9     GOTO 0x7F9               ; no, acabar

; contenido del bucle

07F1  3001     MOVLW 0x1                ; i++
07F2  00F0     MOVWF __pcstackCOMMON
07F3  0870     MOVF __pcstackCOMMON, W
07F4  07F2     ADDWF i, F

07F5  3019     MOVLW 0x19
07F6  0272     SUBWF i, W
07F7  1C03     BTFSS STATUS, 0x0        ; ¿ 0 < 25 - i ? no, terminar
07F8  2FE4     GOTO 0x7E4               ; sí, repetir
```
Esto es lo que ha generado:

inicializa i
comprobar la condición: continuar o salir
bucle: ejecutar el contenido del bucle
incrementar i
comprobar la condición: salir o
saltar a repetir bucle
Total: 26 bytes.

Veamos ahora la propuesta de cosmefulanito04:

```
19:                for (i = 25; i; i--) {
07E2  3019     MOVLW 0x19               ; i = 25
07E3  00F0     MOVWF __pcstackCOMMON
07E4  0870     MOVF __pcstackCOMMON, W
07E5  00F2     MOVWF i

07E6  0872     MOVF i, W                ; <-
07E7  1903     BTFSC STATUS, 0x2        ; ¿ i == 0 ? No, continuar
07E8  2FF9     GOTO 0x7F9               ; Sí, salir

; contenido del bucle

07F6  3001     MOVLW 0x1                ; i--
07F7  02F2     SUBWF i, F
07F8  2FE6     GOTO 0x7E6               ; ->
```
Es decir:

inicializa i
comprobar la condición: continuar o salir
bucle: ejecutar el contenido del bucle
decrementar i
saltar a comprobar condición
Total: 20 bytes.

y ahora, el código que yo comentaba, con el do{ } while():

```
19:                i = 25;
07E4  3019     MOVLW 0x19               ; i = 25
07E5  00F0     MOVWF __pcstackCOMMON
07E6  0870     MOVF __pcstackCOMMON, W
07E7  00F2     MOVWF i
20:                do {

; contenido del bucle

24:                } while (--i);
07F5  3001     MOVLW 0x1                ; --i
07F6  02F2     SUBWF i, F
07F7  1D03     BTFSS STATUS, 0x2        ; ¿ i == 0 ? Sí, terminar
07F8  2FE8     GOTO 0x7E8               ; No, repetir
```
O sea:

inicializa i
bucle: ejecutar el contenido del bucle
decrementar i
comprobar la condición: continuar o salir
saltar a repetir bucle
Total: 16 bytes.

¡Vaya! un 38 % menos que el for() original, y un 20 % menos que lo que proponía cosmefulanito04.

Bueno, sí... hay trampa...  Ese 20 % menos es debido a que no hemos hecho la comprobación al inicio del bucle. Pero es que este es un bucle en el que sabemos que siempre será cierta la condición en la primera vuelta. Por eso podemos entrar en el do{} sin miedo.


----------



## cosmefulanito04 (Oct 11, 2015)

A ver, discutir por 4 bytes desvirtua todo, ya es demasiado. Si querés ahorrarte código hasta en ese nivel, directamente programá en assembler. Si vas a programar en C, 4 bytes de código no existen.

Además, yo en realidad apunto al loop (que en eso es donde realmente hay que poner la lupa) y no tanto al peso en código y de su inicialización previa. Compará el loop del for decremental que mencioné y del do while, vas a ver que es el mismo, no así con el otro for incremental.

Si hacemos un for de 500 repeticiones, el for incremental va a tener que laburar bastante más que los otros dos, ahora bien, esa optimización está buena cuando realmente vas muy justo de tiempo, sino... bien gracias, prefiero el for/while incremental que se entiende mucho mejor, además la exploración de un por ej. un vector es muchísimo más fácil de ver empezando desde su inicio hasta su final.


----------



## cosmefulanito04 (Oct 11, 2015)

En realidad hoy las cosas ya no van por ese camino. Hace 20 años o más puede ser, hoy ya los sistemas embebidos no tienen un gran limitante en cuanto a memoria de código.

Si te falta memoria de código, no vale la pena matarse en reducir (salvo que sean cosas muy sencillas de corregir), por tres motivos, el tiempo de desarrollo es plata, a futuro muy posiblemente la capacidad que ya era un limitante, te va a limitar las posibles mejoras y por último, si estás corto en memoria de código es porque estas usando una familia de uC equivocada para el proyecto que querés desarrollar.

Por ej. en la línea AVR pasar de un Atmega8 a un 16 en términos de precios es casi lo mismo y mágicamente duplicaste esa capacidad de programa. Ni te cuento los ARM, tenés memoria de código para reírte. Lamentablemente este tipo de discusión es prehistórica.

En lo que se debería hacer mucho énfasis (en los casos más críticos), no es tanto en el tamaño del código, sino en la velocidad de ejecución que tendrá una cierta rutina. Ese es mi punto de vista y probablemente con el avance de la tecnología, los uC serán más y más rápido hasta tal punto que esos tiempos críticos sean cosas muuuy raras.

Supongo que el *Dr. Zoidberg* puede dar muchísimo más detalles sobre como se vino desarrollando todo lo que son sistemas embebidos.


----------



## Ardogan (Oct 12, 2015)

cosmefulanito04 dijo:


> En realidad hoy las cosas ya no van por ese camino. Hace 20 años o más puede ser, hoy ya los sistemas embebidos no tienen un gran limitante en cuanto a memoria de código.
> 
> Si te falta memoria de código, no vale la pena matarse en reducir (salvo que sean cosas muy sencillas de corregir), por tres motivos, el tiempo de desarrollo es plata, a futuro muy posiblemente la capacidad que ya era un limitante, te va a limitar las posibles mejoras y por último, si estás corto en memoria de código es porque estas usando una familia de uC equivocada para el proyecto que querés desarrollar.
> 
> ...



Coincido Cosme. Hoy por hoy me parece que un aspecto mucho más importante es la posibilidad de expandir el sistema, agregar nueva funcionalidad, conectarle nuevo hardware...
Un proyecto arranca siendo un sensor de temperatura para un compartimento, después te dicen que estaría bueno que controle un ventilador, después que haga un log en memoria flash, y por supuesto -no puede faltar- poder controlarlo desde un celular. Ah sí, y dejalo abierto para un módulo GSM que pueda mandar los datos al servidor XXX que administra 100 aparatitos a la vez.
Y no te olvides del bootloader para solucionar los bugs a futuro (aún si tu código es 100% correcto, que no lo es; las librerías que estás usando seguramente van a tener bugs y vas a precisar actualizarlo remotamente).

Para eso es necesario tener una arquitectura de software clara, no importa tanto cual: algunos prefieren un RTOS, otros máquinas de estado jerárquicas, otros un esquema productor/consumidor, sistema orientado a eventos...
El tema es que en el código quede clara la funcionalidad del sistema y como agregarle funcionalidad nueva, para que el día de mañana cuando se quiera agregar algo nuevo después de 6 meses o 1 año de tener el proyecto en el freezer se pueda hacer sin tener que estudiar todo el código de vuelta, y sin miedo de que si toco esto acá se rompe el sistema o esta otra parte deja de funcionar. Y si hay que meterse en la funcionalidad básica del sistema entonces que sea un código legible, con nombres de variables significativos, comentarios que expliquen lo que se pretende hacer,etc


----------



## Miembro eliminado 356005 (Oct 12, 2015)

Por favor, no desvirtuemos el espíritu del hilo (ver #1).

Todos estamos de acuerdo en la opinión de *cosmefulanito04*. Incluso yo mismo se lo he aconsejado a otro miembro, en otro hilo, cuando le he dicho que por unos céntimos más tiene el doble de memoria, así que no perdiera más el tiempo.

En cambio, este hilo es justo lo contrario: partimos del interés en intentar hacer el código más eficiente, aunque sea ahorrando medio byte (un _nibble_).


----------



## Dr. Zoidberg (Oct 12, 2015)

Aunque mis comentarios "parezcan" desalentadores, voy a insistir en que optimizar ejemplos simples de programas no representa absolutamente nada. Las optimizaciones siempre son contextuales: no solo dependen de los lazos y contadores, sino que tambien dependen del mapa de memoria utilizado, del uso de los registros y disponibilidad de los mismos, etc, etc, etc.
Si alguien cree que puede predecir que diablos va a hacer el compilador en todas las oportunidades y arquitecturas, entonces recien podemos hablar de tips generales de optimizacion, pero como dudo que eso suceda deberemos atarnos a optimizaciones especificas. Por eso opino que si vamos a tratar de encontrar patrones de optimizacion (que por lo visto son todas de tamaño y ninguna de velocidad) entonces hay que analizar una aplicacion completa y el codigo assembler generado para ver cuales son las decisiones que tomó el compilador EN ESE CASO y asi poder hacer alguna estimacion mas o menos consistente.


----------



## juanma2468 (Oct 12, 2015)

Dejo un pequeño tip para cuando se usa muchos retardos por soft, yo lo utilizo en CCS. Para evitar la repeticion de mucho codigo en demoras, se hace uso de una funcion que contenga una demora fija y que a partir de un contador, repita ese delay.

```
void Demora_ms(long tiempo)
{
    while (tiempo--)
       delay_ms(1); Demora de un mseg
}
...........
Demora_ms (100); // Retardo 100 mseg
...........
...........
Demora_ms (350); // Retardo 350 mseg
```
En tiempo se pone el numero que representaria en mseg. Para tiempos cortos (<500 mseg) y un cristal de 4MHz o mas la diferencia en el tiempo es casi imperseptible, 1 o 2 mseg de mas. Para useg se podria aplicar pero ya el error es mucho mayor.


----------



## D@rkbytes (Oct 12, 2015)

Eso se me hace redundante y menos preciso.
Si ya tienes instrucciones para retardos, ¿para qué crear otra/s?
Da lo mismo que ejecutes un delay_ms(100); a que llames a otra rutina que también usa la instrucción delay_ms(_*time*_);

Aparte, las instrucciones involucradas en la rutina harán que el retardo sea menos preciso que la misma instrucción.
Y si requieres mayores tiempo de retardo, para eso están los Timers.


----------



## TRILO-BYTE (Oct 12, 2015)

sobre todo que es una aberracion usar un delay

pero si se puede hacer esto con un delay

int16 contador;
while(1)
{
if(contador>= 1000) //un segundo
{
prende_led;
contador=0;
}
else
{
apaga_led;
}

contador++;
delay_ms(1);
}

sigue siendo una solucion marrana pero solo atoro el micro 1 milisegundo


----------



## Ardogan (Oct 13, 2015)

JoaquinFerrero dijo:


> Por favor, no desvirtuemos el espíritu del hilo (ver #1).
> 
> Todos estamos de acuerdo en la opinión de *cosmefulanito04*. Incluso yo mismo se lo he aconsejado a otro miembro, en otro hilo, cuando le he dicho que por unos céntimos más tiene el doble de memoria, así que no perdiera más el tiempo.
> 
> En cambio, este hilo es justo lo contrario: partimos del interés en intentar hacer el código más eficiente, aunque sea ahorrando medio byte (un _nibble_).



Entre los tips de C los tips de optimización son solo una parte. No todos los tips tienen por qué ser de optimización.
Me pareció que era una crítica válida no invertir un esfuerzo excesivo en optimizar sacrificando por ello cosas como capacidad de expansión, claridad de código, etc.

Pero bueno, ya se dijo de sobra, siendo que tiramos una de cal, va otra de arena: dividir por una constante cuando en el micro hay un multiplicador por hardware.

Los micros de medio pelo para arriba incorporan multiplicadores hardware, mínimo de 8 bits (con resultado de 16 bits), y se suele dar el caso donde es necesario dividir por una constante conocida de antemano.
Que pasa si queremos hacer algo como:

uint8_t variable = otraVariable/17; //otraVariable también es de 8 bits

Si tengo un multiplicador de 8 bits que me da un resultado en 16 bits (digamos que da el resultado en 2 registros tamaño byte llamados RESL y RESH, siendo parte baja y alta respectivamente), puedo hacer la división con una multiplicación:

otraVariable / 17 = otraVariable /17 * (256 / 256)  = otraVariable * (256/17) / 256

La división por 256 está implícita al quedarnos con la parte alta del resultado de la multiplicación (con el registro que llamamos RESH).
256/17 = 15.0588... no es un número redondo, voy a tener que convivir con el error de redondearlo a 15.

Entonces, en vez de escribir la línea


```
uint8_t variable = otraVariable/17;
```
Puedo hacer

```
uint8_t variable;
uint16_t variable16 = otraVariable * 15;
variable = ParteAlta(variable16);
```
Donde parteAlta puede ser una macro que devuelva el byte alto de una variable de 16 bits.

Ejemplo con números:
Si variable = 210
Queremos obtener 210/17 = 12.35 -> 12
Para hacer la división multiplicando el factor será 256/17 = 15.05.. -> 15
La cuenta que vamos a hacer es 210*15 = 3150 = 0x0C4E
Parte alta = 0x0C = 12 (por lo menos dió sin error en este caso particular, aunque no es lo normal, suele haber error).

¿Y sirve de algo meterse en este menester?. Puede que sí, puede que no. A veces pasa que el compilador ignora el multiplicador hardware, o al menos no lo usa para hacer divisiones por constantes, aunque sí para multiplicaciones. 
Para un micro ARM de 32 bits no creo que sirva porque ya tienen unidades aritmético lógicos muchos más potentes (división incluída) y código bastante maduro. Puede andar para micros de 8 bits (si tienen multiplicador y una ALU sin división).

¿Y tanto le cuesta a un micro de 8 bits hacer una división?, y... gratis no le sale.
Según recuerdo para compilador gcc (o derivados, cada vez es más común que los compiladores para micros sean derivados de gcc) eso viene en la librería stdlib/libgcc donde hay funciones con nombres *divmod*.
A ver si lo encuentro... uff, costó... acá está la versión para AVR de la página oficial de gcc (tarda un rato largo en cargar)
https://gcc.gnu.org/viewcvs/gcc/trunk/libgcc/config/avr/lib1funcs.S
La función de división 8/8 está hecha en assembler:
https://gcc.gnu.org/viewcvs/gcc/trunk/libgcc/config/avr/lib1funcs.S?view=markup#l1335
Código (no borré los números de línea):

```
1335     .section .text.libgcc.div, "ax", @progbits
1336     
1337     /*******************************************************
1338            Division 8 / 8 => (result + remainder)
1339     *******************************************************/
1340     #define r_rem   r25     /* remainder */
1341     #define r_arg1  r24     /* dividend, quotient */
1342     #define r_arg2  r22     /* divisor */
1343     #define r_cnt   r23     /* loop count */
1344     
1345     #if defined (L_udivmodqi4)
1346     DEFUN __udivmodqi4
1347             sub     r_rem,r_rem     ; clear remainder and carry
1348             ldi     r_cnt,9         ; init loop counter
1349             rjmp    __udivmodqi4_ep ; jump to entry point
1350     __udivmodqi4_loop:
1351             rol     r_rem           ; shift dividend into remainder
1352             cp      r_rem,r_arg2    ; compare remainder & divisor
1353             brcs    __udivmodqi4_ep ; remainder <= divisor
1354             sub     r_rem,r_arg2    ; restore remainder
1355     __udivmodqi4_ep:
1356             rol     r_arg1          ; shift dividend (with CARRY)
1357             dec     r_cnt           ; decrement loop counter
1358             brne    __udivmodqi4_loop
1359             com     r_arg1          ; complement result
1360                                     ; because C flag was complemented in loop
1361             ret
1362     ENDF __udivmodqi4
1363     #endif /* defined (L_udivmodqi4) */
```
Como se ve usa un algoritmo con un loop que se repite 8 veces donde se hacen comparaciones, saltos, decremento, y resta. Pongamos un número redondo (a mi favor  ) de 50 instrucciones.
Si eso se puede reemplazar por 2 instrucciones entonces no está tan mal (ok ok, no son solo 2, digamos que pueden rondar las 5?).

No tengo un compilador instalado para AVR en este momento, ¿alguien lo puede compilar a ver que pasa?.
Código 1 (normal):


```
#include blabla... lo que requiere el avr
#include <stdint.h>

uint8_t dividendo;
uint8_t divisor;

void main(void)
{
    //TODO Lo que requiera el micro para inicializar
    
    divisor = dividendo/17;

    while(1)
    {
        nop();
    }
}
```
Código 2 (optimizado?):


```
#include blabla... lo que requiere el avr
#include <stdint.h>

uint8_t dividendo;
uint8_t divisor;
uint16_t mul;

void main(void)
{
    //TODO Lo que requiera el micro para inicializar
    
    mul = dividendo * 15;
    divisor = high(mul);
    while(1)
    {
        nop();
    }
}
```
Ojalá no haya escrito de gusto 


*Actualización
* 
Ojalá no haya escrito de gusto   -> SI, escribí de gusto... ahí va el descargo.
Me instalé el avr gcc (para Ubuntu) siguiendo:
http://maxembedded.com/2015/06/setting-up-avr-gcc-toolchain-on-linux-and-mac-os-x/

Afortunadamente, parece que siguiendo las instrucciones al pie de la letra no hay ningún error raro, pude compilar el código de ejemplo sin ningún problema (y también lo hice fallar borrando un punto y coma y tiro el error como debe ser).

Listo, vamos a compilar para atmega32u4 que es popular y tiene multiplicador por hardware.

Archivo divNormal.c:

```
//Compilar con: 
//avr-gcc -g -O2 -mmcu=atmega32u4 -c divNormal.c
//avr-gcc -g -mmcu=atmega32u4 -o divNormal.elf divNormal.o

//Desensamblar con:
//avr-objdump -S divNormal.elf > divNormalDisAsm.txt

//Compilar sin optimizar 
//avr-gcc -g -O0 -mmcu=atmega32u4 -c divNormal.c
//avr-gcc -g -mmcu=atmega32u4 -o divNormalO0.elf divNormal.o
//avr-objdump -S divNormalO0.elf > divNormalDisAsmO0.txt

#ifndef F_CPU
#define F_CPU 16000000UL // or whatever may be your frequency
#endif
 
#include <avr/io.h>
#include <stdint.h>

uint8_t dividendo = 148;
uint8_t divisor;

int main(void)
{
    //TODO Lo que requiera el micro para inicializar
    
    divisor = dividendo/17;

    while(1)
    {
        ;
    }
    
    return 0;
}
```
Archivo divOpt.c:


```
//Compilar con: 
//avr-gcc -g -O2 -mmcu=atmega32u4 -c divOpt.c
//avr-gcc -g -mmcu=atmega32u4 -o divOpt.elf divOpt.o

//Desensamblar con:
//avr-objdump -S divOpt.elf > divOptDisAsm.txt

#ifndef F_CPU
#define F_CPU 16000000UL // or whatever may be your frequency
#endif
 
#include <stdint.h>

uint8_t dividendo = 148;
uint8_t divisor;
uint16_t mul;

void main(void)
{
    //TODO Lo que requiera el micro para inicializar
    
    mul = dividendo * 15;
    divisor = high(mul);
    while(1)
    {
        nop();
    }
}
```
Compilar y desensamblar como dicen los comentarios, queda
Archivo divNormalDisAsm.txt (solo pongo la parte relevante)

```
int main(void)
{
    //TODO Lo que requiera el micro para inicializar
    
    divisor = dividendo/17;
  ea:    90 91 00 01     lds    r25, 0x0100
  ee:    81 ef           ldi    r24, 0xF1    ; 241
  f0:    98 9f           mul    r25, r24
  f2:    81 2d           mov    r24, r1
  f4:    11 24           eor    r1, r1
  f6:    82 95           swap    r24
  f8:    8f 70           andi    r24, 0x0F    ; 15
  fa:    80 93 02 01     sts    0x0102, r24
  fe:    ff cf           rjmp    .-2          ; 0xfe <main+0x14>

00000100 <_exit>:
 100:    f8 94           cli

00000102 <__stop_program>:
 102:    ff cf           rjmp    .-2          ; 0x102 <__stop_program>
```
Archivo divOptDisAsm.txt:


```
000000d4 <main>:
uint8_t dividendo = 148;
uint8_t divisor;
uint16_t mul;

void main(void)
{
  d4:    90 91 01 01     lds    r25, 0x0101
  d8:    81 ef           ldi    r24, 0xF1    ; 241
  da:    98 9f           mul    r25, r24
  dc:    81 2d           mov    r24, r1
  de:    11 24           eor    r1, r1
  e0:    82 95           swap    r24
  e2:    8f 70           andi    r24, 0x0F    ; 15
  e4:    80 93 00 01     sts    0x0100, r24
  e8:    ff cf           rjmp    .-2          ; 0xe8 <main+0x14>

000000ea <_exit>:
  ea:    f8 94           cli

000000ec <__stop_program>:
  ec:    ff cf           rjmp    .-2          ; 0xec <__stop_program>
```
Como se ve el compilador generó código (casi) idéntico.. así que nada, por lo menos me queda el compilador para avr instalado .
Ni siquiera sirve cuando se compila sin optimización (con O0), así que a llorar a la iglesia


----------



## Miembro eliminado 356005 (Oct 15, 2015)

Cuidado, cuidado... No te fíes de lo que estás viendo...

En el código divisor = dividendo/17, ¿has hecho la prueba de cambiar ese 17 por otros valores?

Te vas a sorprender...


----------



## TRILO-BYTE (Nov 5, 2015)

una preguntota a los super programadores

digamos que cometo la siguiente porqueria:


```
switch(dato)
{
case 0xF1:
//codigo
break;

case 0xAA:
//codigo
break;

case 0x01:
//codigo
break;

case 0x11:
//codigo
break;
}
```

todo bien cochinamente rebolvido

*¿afecta la velocidad de ejecucion?*

*¿y si no le pongo default al switch case?*

¿necesito un jalon de orejas?


----------



## Dr. Zoidberg (Nov 5, 2015)

TRILO-BYTE dijo:


> una preguntota a los super programadores
> 
> digamos que cometo la siguiente porqueria:
> 
> ...


No debería, pero depende del código que genere el compilador. La estructura switch se supone que no hace comparación secuencial, y el compilador puede precalcular los saltos por que los case llevan constantes...



TRILO-BYTE dijo:


> *¿y si no le pongo default al switch case?*


No pasa nada...



TRILO-BYTE dijo:


> ¿necesito un jalon de orejas?


Mejor un golpe con un palo en la frente...


----------



## TRILO-BYTE (Nov 5, 2015)

un golpe!! 

otra duda para ahorrar espacio en la placa y costo se me ocurrio meter el audio directo al ADC claro con su respectivo capacitor.

era simple preamplificar la señal segun la norma de un conector RCA debe dar 1vpp pero 
como todo mundo sabra aveces el audio esta muy bajo.

se me ocurrio que el valor leido del ADC lo puedo multiplicar por 100 por decir asi

de porsi hise otra porqueria hise PWM a software 15 para ser exactos, el PWM no lo reflejo en los puertos si no en registror de corrimiento y dentro del main tengo varios switch case.

hise puras porquerias lo se.

pero mi duda es la siguiente

ojo necesito regaños de los buenos.

*¿es buena idea andar multiplicando el valor leido del ADC?

¿le comeria tiempo al CPU, hay otra manera mas eficiente de hacerlo sin multiplicar?*

lo que quiero es hacer que el valor leido del ADC tenga una buena resolucion para meterlo en 1 byte para poder asi mover mi PWM marrano a software


----------



## juanma2468 (Nov 5, 2015)

Depende de la referencia que uses en el adc va a variar tu resolucion con respecto a la señal que midas. Si la señal es baja como decis 1Vpp pues montala en una continua de 1,25v y la referencia ponela en 2,5v, asi mejoras tu resolucion y no tenes que multiplicar por 100


----------



## TRILO-BYTE (Nov 5, 2015)

es verdad como lo olvide lo peor es que ya hise las pcb.

ahora si necesito una paliza.
pensaba arreglarlo con software, nimodo


----------



## juanma2468 (Nov 5, 2015)

Pasa que si tu señal es chica, y tu resolucion ed pobre, al multiplicar por 100 tu resolucion no mejora, sigue siendo la misma


----------



## TRILO-BYTE (Nov 5, 2015)

ese es mi problema
debo unirme a la PPA "pesimos programadores anonimos".

paso 1 admitir que tienes un problema.
paso 2 estar convencido de que un Poder Superior podrá ayudarme.
paso 3 decidir poner mi voluntad y mi vida al cuidado de *este tema*, como nosotros lo concebimos.

y etc y etc.

¿pero no habra manera de corregir mi brutalidad?
digo no habra un modo de hacer una amplificacion a software sin multiplicar
una algoritmo

o terminare metiendo una cochina multiplicacion que no se ve muy afectado pero no me gusta 
lo hago para unos LEDS.

se ve bien para un ojo palurdo, pero para un ojo experimentado hay blinking con el ADC.

no todo el tiempo trabaja el ADC en el proyecto, pero ami no me gusta mi cochinada


----------



## Miembro eliminado 356005 (Nov 6, 2015)

Dr. Zoidberg dijo:


> No debería, pero depende del código que genere el compilador. La estructura switch se supone que no hace comparación secuencial, y el compilador puede precalcular los saltos por que los case llevan constantes.


Como regla general, un switch-case se traduce como un conjunto de if-elseif-else en cascada, salvo por la -no- presencia de break, que en ese caso sabemos que la ejecución debe continuar en el código de la siguiente condición.

En el caso propuesto, el código generado será el mismo que cuatro if() en cascada. Si se sabe que 'datos' va a tener solo uno de cuatro valores, pues entonces podemos dejar el último caso con un default, y ahorramos una comparación (si dejamos el cuarto case, el compilador no sabría que solo hay cuatro casos, por lo que siempre haría la comparación).

A veces hay optimizaciones, como cuando las comparaciones se componen de valores consecutivos o repetidos, pero son casos muy raros. Por ejemplo, si tenemos un

switch (datos) {
case 1:
// código
break;
case 2:
// código
break;
case 3:
// código
break;
case 4:
// código
break;
...

el compilador podría generar un código en el que fuera decrementando una copia de datos, y saltar al código correspondiente cuando esa operación sea cero. O incluso generar una tabla de saltos, con una simple multiplicación del valor de datos (esta solución es la que algunos programadores implementan para acelerar el proceso).





TRILO-BYTE dijo:


> lo que quiero es hacer que el valor leido del ADC tenga una buena resolucion para meterlo en 1 byte para poder asi mover mi PWM marrano a software



¿Y cuál es la resolución del ADC?

Si, suponemos, que el ADC tiene 10 bit de resolución, y que el voltaje ocupa todo ese rango (suponemos que alguna vez la entrada al ADC llega al voltaje máximo), entonces el pasarlo a un byte consiste en quedarnos con el byte alto de los dos donde queda guardado el resultado (y además, ajustado a la izquierda).

Me explico: en los ADC de 10, 12, 14 bit, el resultado se guarda en dos registros de 8 bit. Y en un bit de configuración podemos decidir si queremos que lo guarde ajustado hacia la derecha o hacia la izquierda (los bits que faltan hasta completar los 16 bit se rellenan con '0' en el lado contrario).

Ajustando ese valor de configuración para que guarde los 8 bit superiores en el byte alto del resultado, solo tenemos que leerlo, sin tener que hacer ninguna operación más.


----------



## Dr. Zoidberg (Nov 6, 2015)

Fijate *acá*. Es una explicación bastante detallada de como funciona la tabla de saltos del switch.


----------



## chclau (Nov 6, 2015)

Con respecto a los ADC, hay que entender que si no usamos todo su rango dinamico estamos desperdiciando bits del conversor. Y los bits que se desprecian ya no son recuperables en terminos de SNR.

Si el mal ya esta hecho, lo que es multiplicar, se puede multiplicar en forma burda y rapida por multiplos de dos haciendo simples operaciones de shift que son sumamente rapidas.


----------



## TRILO-BYTE (Nov 6, 2015)

me gusto la ultima entonces me sugieres que:

tengo esto leido de mi ADC de 10 bits

1.-0000000111
2.-0000000010
3.-0000000101
4.-0000001001

un ejemplo de una secuencia donde apenas lleno un nible 

¿lo que puedo hacer es recortar mis 10 bits en 8 bits y hacer trampa rotando y rellenando los huecos con 1?

1.-00011111
2.-00001011
3.-00010111
4.-00100111

me parece una buena idea.

seria asi?
unsigned char valor;

while(1)
{
valor= ADC;
valor= valor*<<2* *|* 00000011;
}

creo que hay que sugerir asi como yo que toque fondo haciendo porquerias

un tema que trate 

*si yo tambien cometo mis errores y me cuesta trabajo admitirlo*


----------



## chclau (Nov 6, 2015)

Si, pero no entiendo por que rellenar con unos y no con ceros, que es mas "barato" desde el punto de vista de la implementacion.


----------



## TRILO-BYTE (Nov 6, 2015)

¿entonces no lo lleno con 1?


----------



## Scooter (Nov 6, 2015)

No veo ventaja en rellenar de una u otra cosa


----------



## TRILO-BYTE (Nov 6, 2015)

pero quedaria asi:

1.-00000010 2
2.-00000001 1
3.-00000011 3
4.-00000100 4
5.-00001011 11

si yo lo recorro 2 casillas se veria asi:

1.-00001000 8
2.-00000100 4
3.-00001100 12
4.-00010000 16
5.-00101100 44

es como si lo multiplicara 4

entonces me imagino que debo rotar y rellenar con 1 
en cierto modo para lograr una multiplicacion dejen pienso mi cochino algoritmo


----------



## cosmefulanito04 (Nov 6, 2015)

Cada rotación multiplica o divide por dos según el sentido.

Rotar n veces a la izquierda implica multiplicar por 2^n, ej. rotar 8 veces sería multiplicar por 2^8=256.


----------



## chclau (Nov 6, 2015)

Es mejor con ceros porque es mas barato.

Si hubieras diseñado bien el circuito desde el comienzo, para la señal que ahora recibes 2 recibirias 8, o 16, o mas. El hecho de rotar a la izquierda multiplica por dos y llena los bits mas significativos.

Si tuvieras un amplificador como corresponde, en vez de recibir 2 recibirias 8 o 9 o 10 o cualquier otro numero en que incluso los bits menos significativos varian.

Como estas amplificando por SW, no puedes tener resolucion en los bits menos significativos. Eso es lo que decia antes, perdiste SNR y esa perdida es irrecuperable.


----------



## TRILO-BYTE (Nov 6, 2015)

bueno gracias por sus consejos

creo que mi proyecto no se vera tan afectado total para el ojo palurdo es imperceptible
pero yo si lo noto.

es para unas luces RGB queria que bailaran con el sonido y si lo hace pero hay mucho blinking cuando multiplico, mas bien hago una relacion y pierde mucha velocidad el CPU.

pero apenas me di cuenta que al hacer los PCB no puse referencias y digo al demonio debe haber correccion por software, ya que recurro mas al software que al hardware y sobre todo no sabia como reparar mi error


----------



## locodelafonola (Nov 6, 2015)

Hola





TRILO-BYTE dijo:


> bueno gracias por sus consejos
> 
> creo que mi proyecto no se vera tan afectado total para el ojo palurdo es imperceptible
> pero yo si lo noto.
> ...


 La pucha siempre te entiendo tarde​ Yo te hubiera dicho ., que no usaras el ADC​ Lo mejor que me resulto .,fue la interupcion externa​ Es mas simple ., y para mover cambios ., es mas rapido​ Pero bueeeee ., ya esta !!!​


----------



## TRILO-BYTE (Nov 7, 2015)

nop no se puede

por que tengo 2 entradas por interrupcion para otra cosa, y la interrupcion RB0 para control remoto
por eso no se puede


----------



## Miembro eliminado 356005 (Dic 26, 2017)

Hace unos días *salió un artículo* sobre el tema de mejorar la eficiencia de la rutina que transforma un número entero (almacenado de forma binaria, claro) en una cadena de caracteres (los dígitos que forman la cantidad).

Parece algo muy sencillo... pero la solución tiene *más de 400 líneas*. Todo por conseguir ahorrar unos pocos microsegundos más. Algo muy importante cuando gestionas millones de peticiones.


----------

