Optimizar suele empezar demasiado pronto. En un sistema embebido es tentador cambiar una función, reemplazar una operación o reducir un retardo porque «suena más eficiente». El problema es que ninguna de esas acciones nos dice qué parte del sistema está fallando.
Medir primero no elimina la intuición; le da un lugar donde apoyarse. En lugar de cambiar el código entero, definimos una señal, una carga de trabajo y una hipótesis.
Una medición que responde una pregunta
Supongamos que una lectura de sensor tarda demasiado. La pregunta no es «¿el sensor es lento?», sino «¿cuánto tiempo tardamos entre el evento y el dato disponible?». La respuesta necesita dos referencias: el instante en que ocurre el evento y el instante en que termina el procesamiento.
volatile uint32_t eventAt = 0;
volatile bool eventPending = false;
void onSensorEvent() {
eventAt = micros();
eventPending = true;
}
void processSensor() {
if (!eventPending) {
return;
}
const uint32_t startedAt = micros();
const int value = readSensor();
publish(value);
const uint32_t finishedAt = micros();
recordLatency(startedAt - eventAt, finishedAt - startedAt);
eventPending = false;
}
Este ejemplo no pretende ser una librería ni un patrón universal. Sirve para mostrar una idea: separar el tiempo de espera del tiempo de procesamiento permite saber dónde actuar después.
Elegir una señal útil
Una buena métrica es pequeña, repetible y está cerca del comportamiento que nos importa. Puede ser el tiempo entre dos marcas, el número de bytes escritos en un puerto o la frecuencia con la que se completa un ciclo. También es importante registrar el contexto: voltaje, temperatura, tamaño de la carga o modo de energía.
No todas las métricas merecen el mismo coste. Un contador global puede ser barato; una traza completa ejecutada en cada iteración puede alterar el sistema que intenta observar. Antes de añadir instrumentación, conviene responder cuánto sobrecosto puede tolerar la aplicación.
Antes y después
Una comparación útil tiene al menos cuatro partes:
- la misma carga de trabajo;
- el mismo entorno o sus límites documentados;
- una señal antes del cambio;
- una señal después del cambio.
Si sólo se conserva el resultado final, no se puede distinguir una mejora de una variación normal. Repetir la medición varias veces y anotar el rango evita convertir un promedio aislado en una certeza.
La optimización es una hipótesis
Después de medir, el cambio debe estar escrito como una hipótesis comprobable. «Cambiar el buffer va a reducir las escrituras» es mejor que «hay que optimizar el buffer»: identifica el mecanismo esperado y deja una forma de comprobarlo.
Si el resultado no mejora, el experimento sigue siendo útil. Puede haber dejado a la vista una dependencia que no estaba visible, un cuello de botella en otra capa o una medición que necesita otro punto de referencia. El conocimiento negativo también evita repetir cambios sin criterio.
Medir no es una ceremonia que se coloque entre pensar y hacer. Es la forma de decidir qué hacer después.
