c++ - televisores - medidas de un tv de 43 pulgadas
C/C++ optimiza las comprobaciones de distancia para ver si una función ya se ha ejecutado antes (11)
Digamos que tiene una función en C / C ++, que se comporta de cierta manera la primera vez que se ejecuta. Y luego, todas las otras veces se comporta de otra manera (ver más abajo, por ejemplo). Después de ejecutarse la primera vez, la instrucción if se vuelve redundante y puede optimizarse si la velocidad es importante. ¿Hay alguna manera de hacer esta optimización?
bool val = true;
void function1() {
if (val == true) {
// do something
val = false;
}
else {
// do other stuff, val is never set to true again
}
}
Los compiladores como g ++ (y estoy seguro de que msvc) admiten la generación de datos de perfil en una primera ejecución, luego usan esos datos para adivinar qué es lo más probable que se sigan las ramas y optimizan en consecuencia. Si está utilizando gcc, mire la opción -fprofile-generate
.
El comportamiento esperado es que el compilador optimizará esa afirmación de tal manera que la otra cosa se ordenará primero, evitando así la operación jmp en todas sus llamadas subsiguientes, haciéndolo casi tan rápido como si no estuviera allí, especialmente si regresa en algún lugar de esa otra cosa (evitando así tener que saltar más allá de las declaraciones ''if'')
Para mantener el compilador INDEPENDIENTE, puede codificar las partes de if () en una función y de else{}
en otra. casi todos los compiladores optimizan el if() else{}
, por lo tanto, una vez el más PROBABLE es el else{}
- por lo tanto, codifique el código ejecutable ocasional en if()
y el resto en una función separada que se llame en else
Podría usar un puntero de función, pero en cualquier caso requerirá una llamada indirecta:
void (*yourFunction)(void) = &firstCall;
void firstCall() {
..
yourFunction = &otherCalls;
}
void otherCalls() {
..
}
void main()
{
yourFunction();
}
Podría usar una variable miembro static
lugar de una variable global.
O, si el código que está ejecutando la primera vez cambia algo para todos los usos futuros (por ejemplo, ¿abrir un archivo?), Podría usar ese cambio como una verificación para determinar si se debe ejecutar el código (es decir, verificar si el archivo está abierto). Esto te ahorraría la variable extra. Además, podría ayudar con la comprobación de errores: si, por algún motivo, el cambio inicial no se modifica con otra operación (por ejemplo, el archivo está en un medio extraíble que se eliminó de forma incorrecta), su verificación podría intentar volver a realizar el cambio.
Si desea que el código sea un poco más limpio, puede hacer que la variable sea local a la función usando static
:
void function() {
static bool firstRun = true;
if (firstRun) {
firstRun = false;
...
}
else {
...
}
}
Al ingresar a la función por primera vez, firstRun
sería verdadero, y persistiría, así que cada vez que se llame a la función, la variable firstRun
será la misma instancia que las anteriores (y será falsa cada vez subsiguiente).
Esto podría usarse bien con la solución de @ouah.
Solo debe hacer el cambio si está seguro de que realmente es un cuello de botella. Con la predicción de rama, la instrucción if
es probablemente instantánea, ya que es un patrón muy predecible.
Dicho esto, puede utilizar devoluciones de llamada:
#include <iostream>
using namespace std;
typedef void (*FunPtr) (void);
FunPtr method;
void subsequentRun()
{
std::cout << "subsequent call" << std::endl;
}
void firstRun()
{
std::cout << "first run" << std::endl;
method = subsequentRun;
}
int main()
{
method = firstRun;
method();
method();
method();
}
produce la salida:
primer intento
llamada subsiguiente
llamada subsiguiente
Un compilador solo puede optimizar lo que se conoce en tiempo de compilación.
En su caso, el valor de val
solo se conoce en tiempo de ejecución, por lo que no se puede optimizar.
La prueba if
es muy rápida, no debería preocuparse por optimizarla.
Un método posible es compilar dos versiones diferentes de la función (esto puede hacerse desde una sola función en la fuente con plantillas), y usar un puntero u objeto de función para decidir en tiempo de ejecución. Sin embargo, la sobrecarga del puntero probablemente superará cualquier ganancia potencial a menos que su función sea realmente costosa.
Una cosa que puede hacer es poner la lógica en el constructor de un objeto, que luego se define static
. Si tal objeto static
produce en un ámbito de bloque, el constructor se ejecuta la primera vez que tiene lugar una ejecución de ese ámbito. La verificación de una sola vez es emitida por el compilador.
También puede colocar objetos static
en el alcance del archivo, y luego se inicializan antes de llamar a main
.
Estoy dando esta respuesta porque quizás no estás haciendo un uso efectivo de las clases de C ++.
(Respecto a C/C++
, no hay tal lenguaje. Hay C y C ++. ¿Está trabajando en C que también tiene que compilar como C ++ (a veces llamado, no oficial, "Clean C"), o realmente está trabajando en C ++?)
Una forma de hacer esta optimización es dividir la función en dos. En lugar de:
void function1()
{
if (val == true) {
// do something
val = false;
} else {
// do other stuff
}
}
Hacer esto:
void function1()
{
// do something
}
void function2()
{
// do other stuff
}
gcc
tiene una función incorporada que le permite informar a la implementación sobre la predicción de la rama:
__builtin_expect
http://gcc.gnu.org/onlinedocs/gcc/Other-Builtins.html
Por ejemplo en tu caso:
bool val = true;
void function1()
{
if (__builtin_expect(val, 0)) {
// do something
val = false;
}
else {
// do other stuff, val is never set to true again
}
}