Generalmente, los recursos se comparten entre diferentes procesos. Suponga que su programa toma más recursos, entonces definitivamente afectará el rendimiento de otros procesos que necesitan los mismos recursos. Entonces tenemos la necesidad de escribir y optimizar nuestro programa teniendo en cuenta los recursos, por ejemplo, el tiempo del procesador y la memoria principal.
Las siguientes son algunas técnicas de optimización.
- Optimizaciones en bucle
- Desenrolle pequeños bucles : la mayoría de las veces Compiler hace esto automáticamente, pero es un buen hábito escribir códigos optimizados. Las actualizaciones de array que usan esto son muy ventajosas.
Programa 1:
#include <stdio.h>
int
main(
void
)
{
int
fact[5];
fact[0] = 1;
// Overhead of managing a counter
// just for 4 iterations
// is not a good idea
for
(
int
i = 1; i < 5; ++i) {
fact[i] = fact[i - 1] * i;
}
return
0;
}
Programa 2:
#include <stdio.h>
int
main(
void
)
{
int
fact[5] = { 1, 1, 2, 6, 24 };
// Here the same work is done
// without counter overhead
return
0;
}
- Evitar cálculos en bucle : debemos evitar cualquier cálculo que sea más o menos constante en valor. Los bucles internos deben tener el mínimo de cálculos posibles.
Programa 1:#include <stdio.h>
int
main(
void
)
{
int
arr[1000];
int
a = 1, b = 5, c = 25, d = 7;
// Calculating a constant expression
// for each iteration is not good.
for
(
int
i = 0; i < 1000; ++i) {
arr[i] = (((c % d) * a / b) % d) * i;
}
return
0;
}
Programa 2:
#include <stdio.h>
int
main(
void
)
{
int
arr[1000];
int
a = 1, b = 5, c = 25, d = 7;
// pre calculating the constant expression
int
temp = (((c % d) * a / b) % d);
for
(
int
i = 0; i < 1000; ++i) {
arr[i] = temp * i;
}
return
0;
}
- Evite la desreferenciación del puntero en bucle : la desreferenciación del puntero crea muchos problemas en la memoria. Así que mejor asígnalo a alguna variable temporal y luego usa esa variable temporal en el bucle.
Programa 1:#include <stdio.h>
int
main(
void
)
{
int
a = 0;
int
* iptr = &a;
// Dereferencing pointer inside loop
// is costly
for
(
int
i = 1; i < 11; ++i) {
*iptr = *iptr + i;
}
printf
(
"Value of a : %d"
, a);
return
0;
}
Producción:Value of a : 55
Programa 2:
#include <stdio.h>
int
main(
void
)
{
int
a = 0;
int
* iptr = &a;
// Dereferencing pointer outside loop
// and saving its value in a temp variable
int
temp = *iptr;
for
(
int
i = 1; i < 11; ++i) {
// performing calculations on temp variable
temp = temp + i;
}
// Updating pointer using final value of temp
*iptr = temp;
printf
(
"Value of a : %d"
, a);
return
0;
}
Producción:Value of a : 55
- Utilice variables de registro como contadores de bucles internos : se puede acceder a las variables almacenadas en registros mucho más rápido que a las variables almacenadas en la memoria.
Programa:#include <stdio.h>
int
main(
void
)
{
register
int
i = 0;
register
int
j = 0;
int
n = 5;
// using register variables
// as counters make the loop faster
for
(i = 0; i < n; ++i) {
for
(j = 0; j <= i; ++j) {
printf
(
"* "
);
}
printf
(
"\n"
);
}
return
0;
}
Producción:* * * * * * * * * * * * * * *
- Desenrolle pequeños bucles : la mayoría de las veces Compiler hace esto automáticamente, pero es un buen hábito escribir códigos optimizados. Las actualizaciones de array que usan esto son muy ventajosas.
- Matemáticas Rápidas
- Evite la división de enteros innecesaria : las operaciones de división son lentas, por lo que debemos minimizar las operaciones de división.
Programa:#include <stdio.h>
int
main(
void
)
{
int
a = 100, b = 2, c = 5;
// int d=a/b/c; two division operators
int
d = a / (b * c);
// single division operator
return
0;
}
- Multiplicación y división por potencia de 2 : use el desplazamiento a la izquierda (<<) para la multiplicación y el desplazamiento a la derecha (>>) para la división. Las operaciones de bits serán mucho más rápidas que las operaciones de multiplicación y división. Para operaciones simples, el compilador puede optimizar automáticamente el código, pero en el caso de expresiones complejas, siempre se recomienda usar operaciones de bits.
Ejemplo :Multiply by 6 : a= a<<1 + a<<2; Multiply by 7 : a= a<<3 - a; Divide by 8 : a= a>>3; // division by power of 2
- Simplificación de expresiones: A veces podemos reducir algunas operaciones simplificando expresiones.
Ejemplo :a*b + a*b*c + a*b*c*d ---> (a*b)*(1 + c*(1 + d)) L.H.S can be Simplified to R.H.S L.H.S : 6 multiplications and 2 additions R.H.S : 3 multiplications and 2 additions
- Evite la división de enteros innecesaria : las operaciones de división son lentas, por lo que debemos minimizar las operaciones de división.
- Optimización con los
compiladores de sentencias Switch traducen las sentencias Switch de diferentes maneras. Si las etiquetas de casos son pequeños valores enteros contiguos, se crea una tabla de salto. Esto es muy rápido y no depende del número de etiquetas de cajas. Si las etiquetas de los casos son más largas y no contiguas, se crea un árbol de comparación, es decir, declaraciones if…else. Entonces, en este caso, debemos mantener la etiqueta más frecuente primero y la etiqueta menos frecuente debe estar al final.A veces vemos mucho código repetido escrito en todos los casos excepto en una o dos sentencias
Ejemplo :
switch(expression) { case a: ........ ........ break; case b: ........ ........ break; case c: common statements; different statements; common statements; break; case d: common statements; different statements; common statements; break; ' case e: common statements; different statements; common statements; break; case f: common statements; different statements; common statements; break; default: break; }
Podemos tomar todos los casos juntos y podemos escribir declaraciones comunes solo una vez y declaraciones diferentes en casos relacionados usando otro interruptor. Aquí tomaremos los casos c, d, e, f juntos y escribiremos declaraciones comunes, luego podemos usar otro interruptor y escribir declaraciones diferentes en el caso c, d, e, f. Luego, después de este cambio, podemos volver a escribir declaraciones comunes.
switch(expression) { case a: ........ ........ break; case b: ........ ........ break; case c: case d: case e: case f: common statements; switch(expression); { case c: different statements; break; case d: different statements; break; case e: different statements; break; case f: different statements; break; } /*End of switch*/ common statements; break; default: break; }/*End of switch*/
- Preferir int a char o short
Siempre debemos preferir int a char porque C realiza todas las operaciones de char con un número entero. En todas las operaciones, como pasar un carácter a una función o una operación aritmética, el primer carácter se convertirá en un número entero y, después de la compilación de la operación, se convertirá nuevamente en carácter . Para un solo carácter, esto puede no afectar la eficiencia, pero supongamos que la misma operación se realiza 100 veces en un ciclo, entonces puede disminuir la eficiencia del programa. - Prefiere incremento/decremento previo a incremento/decremento posterior
En pre-incremento, primero incrementa el valor y simplemente copia el valor en la ubicación de la variable pero en post-incremento, primero copia el valor en una variable temporal, lo incrementa y luego copia el valor valor a la ubicación de la variable. Si el incremento posterior es 1000 veces en un ciclo, disminuirá la eficiencia. - Orden de Evaluación de la Expresión
-
A || B
Aquí primero se evaluará A, si es verdadera, entonces no hay necesidad de evaluar la expresión B. Por lo tanto, deberíamos preferir tener una expresión que se evalúe como verdadera la mayoría de las veces, en el lugar de A.
-
A && B
Aquí primero se evaluará A, si es falso, entonces no hay necesidad de evaluar la expresión B. Por lo tanto, deberíamos preferir tener una expresión que se evalúe como falsa la mayoría de las veces, en el lugar de A.
-
Publicación traducida automáticamente
Artículo escrito por RishavPandey1 y traducido por Barcelona Geeks. The original can be accessed here. Licence: CCBY-SA