En la actualidad, la gestión del rendimiento en sistemas de almacenamiento de alto nivel se ha convertido en un aspecto crucial para garantizar la eficiencia y la rapidez que demandan las aplicaciones modernas. Cuando hablamos de servidores capaces de manejar decenas de millones de operaciones de entrada/salida por segundo (IOPS) en arquitecturas masivamente paralelas, como las que cuentan con más de 300 CPUs, el desafío de monitorear y contabilizar la latencia de estos procesos se vuelve aún más relevante y complejo. Uno de los enfoques más prometedores en el monitorizado de latencia en dispositivos de bloque en Linux es el uso de eBPF (Extended Berkeley Packet Filter). eBPF ha revolucionado la forma en que se recopilan datos en sistemas Linux, ofreciendo una solución eficiente, flexible y de bajo impacto para observar internamente el kernel sin necesidad de modificarlo o reiniciarlo. Sin embargo, al operar en entornos extremadamente demandantes, como aquel con 37 millones de IOPS utilizando 384 CPUs, las herramientas y métodos tradicionales de eBPF pueden no estar a la altura en términos de eficiencia y escalabilidad.
El reto principal radica en la manera en la que las herramientas existentes, como biolatency de iovisor/bcc, implementan la captura y el procesamiento de la información sobre latencias de entrada/salida. Esta herramienta tradicional emplea tracepoints del kernel que intervienen en dos fases distintas del ciclo de vida de una solicitud de E/S: la emisión (block_rq_issue) y la finalización (block_rq_complete). Para cada operación, se mantiene un registro del momento en que se inicia la operación guardando un timestamp en un mapa hash, identificado por la dirección del puntero de la solicitud. Al momento de completarse la solicitud, se realiza una consulta a este mapa para obtener el tiempo inicial, calcular la latencia y luego actualizar un histograma que agrega estas latencias. Este método, aunque funcional, presenta varios cuellos de botella cuando se ejecuta bajo cargas de trabajo extremas.
La escritura, lectura y eliminación constante en un mapa hash global a menudo produce una cantidad significativa de tráfico en la memoria caché, intensificando la competencia entre CPUs y provocando una sobrecarga que se traduce en latencias adicionales y consumo elevado de recursos. Además, el hecho de que la inserción y el procesamiento de la información ocurran en dos puntos separados del flujo reduce la eficiencia y aumenta la carga del sistema. El caso descrito con un servidor dual-socket AMD EPYC Genoa con 384 vCPUs y 21 unidades NVMe, capaces de alcanzar hasta 38.5 millones de IOPS, ilustró este problema con claridad. Al ejecutar biolatency bajo estas condiciones, se observó que el rendimiento se redujo a la mitad debido al elevado costo computacional de mantener la contabilidad de latencia.
La monitorización misma impactaba negativamente el sistema, consumiendo el tiempo equivalente a cientos de CPUs solo para rastrear las solicitudes de E/S. Para resolver estos problemas, una aproximación innovadora reutilizó la información nativa que el kernel Linux ya mantiene en la estructura interna de las solicitudes de E/S. Desde versiones anteriores del kernel, la estructura "struct request" guarda timestamps de alta resolución sobre el momento en que se inició la operación, lo que permitió eliminar completamente la necesidad de guardar manualmente estos tiempos en mapas hash globales y, por ende, la dependencia al tracepoint de emisión. Con esta estrategia, el monitoreo puede ocurrir únicamente en el tracepoint de finalización de la solicitud, accediendo directamente a los timestamps proporcionados por el kernel. Esto elimina la mitad de las intervenciones eBPF y, por ende, reduce significativamente los costos asociados.
Además, para la agregación de los histogramas de latencia, en lugar de utilizar mapas hash globales, se adoptó una estructura de mapas eBPF por CPU (per-CPU hash maps). Esta técnica distribuye la carga de agregación evitando la contención de acceso en un único mapa global y mejora notablemente el rendimiento, especialmente en máquinas multisocket y con arquitecturas NUMA, donde la latencia en acceso a memoria entre sockets puede agravar el problema. Esta combinación de tácticas produjo un rendimiento que superó en 59 veces la eficiencia en uso de CPU y latencia de los probes, con una reducción visible de la carga en el sistema de monitoreo. Del uso que antes requería más de 300 CPUs para contabilizar las latencias, ahora el proceso demandaba apenas alrededor de 10 CPUs, manteniendo la precisión y calidad de los datos obtenidos. Un aspecto importante de esta optimización radica también en disminuir el tamaño y la complejidad de las estructuras de datos de los mapas eBPF.
Al eliminar el mapa global de inicio y al reducir el tamaño de los mapas de histogramas, no solo se disminuye la presión en la memoria caché sino que se mejora la eficiencia general de las operaciones atómicas necesarias para actualizar los contadores, lo cual es crítico en sistemas con alta concurrencia. No obstante, esta nueva técnica aún presenta algunos retos para ser considerada completamente estable y lista para producción. Se han observado anomalías ocasionales como outliers de latencia introducidos por capas como el dispositivo de multiplexado (DM) y eventos infrecuentes de bloqueo mientras se limpian los mapas per-CPU bajo carga intensa. Además, a diferencia del método previo, esta aproximación actualmente no permite vincular fácilmente las latencias de E/S con el proceso o PID originador de la solicitud, dado que el kernel no almacena esta información directamente junto con la solicitud. Resolver esta limitación requerirá enfoques más creativos y posiblemente apoyo de futuras versiones del kernel.
El avance en la optimización de la contabilidad de latencia con eBPF tiene un impacto directo en entornos que requieren una observabilidad detallada sin comprometer la performance. En áreas como bases de datos de alto rendimiento, sistemas de archivos distribuidos y cargas de trabajo en la nube, poder analizar la latencia real de cada operación sin añadir una sobrecarga significativa es un ventaja competitiva. Este desarrollo también demuestra que entender en profundidad las estructuras internas y funcionalidades nativas del kernel es clave para diseñar soluciones de monitoreo eficientes. Muchas veces, reinventar la rueda implica costos que la infraestructura del sistema ya solventa y que pueden ser aprovechados si se conocen en detalle. Por último, esta experiencia pone en relieve la capacidad de herramientas abiertas y el valor de la comunidad y desarrollo colaborativo para evolucionar las técnicas utilizadas en la administración y monitoreo de sistemas críticos.
Con proyectos abiertos como iovisor/bcc y la posibilidad de contribuir con propuestas de mejora, se acelera la innovación en herramientas que impactan directamente la estabilidad y eficiencia de infraestructuras modernas. En conclusión, la optimización del seguimiento de latencias en sistemas que manejan enormes cargas de entrada/salida, mediante la reutilización de datos internos del kernel y la distribución inteligente de estructuras eBPF, abre la puerta a una monitorización escalable y de bajo coste. A medida que el hardware evoluciona y los datos que procesamos crecen exponencialmente, este tipo de avances serán fundamentales para mantener sistemas rápidos, confiables y observables.