Ir al contenido

Cómo optimizamos las Colecciones de Delphi y aceleramos la compilación en 60%

Dext Collections Performance

Todo desarrollador Delphi conoce el costo de las Generics.Collections. Ellas cambiaron completamente el juego cuando fueron introducidas, pero también trajeron un “impuesto” la mayor parte de las veces invisible: Code Bloat y tiempos de compilación que ponen a prueba nuestra paciencia diariamente. En el Project Dext, decidimos que necesitábamos algo más rápido, más inteligente y, sobre todo, más productivo.

La Gran Victoria: Productividad y “Code Folding” ⚡

Sección titulada «La Gran Victoria: Productividad y “Code Folding” ⚡»

El mayor villano de la productividad en proyectos grandes de Delphi es el tiempo inactivo esperando a que el compilador termine su trabajo. Al comienzo del proceso con nuestra línea base de grandes proyectos Dext, una compilación completa tomaba alrededor de 8 minutos y 36 segundos.

El problema central es la arquitectura de compilación de Generics: el compilador de Delphi necesita generar el código en binario por separado y en su totalidad para cada especialización de genérico instanciada. En una prueba de estrés moderna de backend de Dext, enfrentamos flujos simultáneos de:

  • 500 especializaciones de IList
  • 1.500 especializaciones de TDictionary<K,V>
  • Total: 2.000 especializaciones completas duplicando el código generado

¿Cómo resolvimos esto? Implementando el patrón de Code Folding Binario, en el cual esos cientos de listas y colecciones comparten un único motor central (TRawList), que fue modelado para operar de forma silenciosa y segura sobre porciones de memoria en bruto.

Esto redujo nuestro tiempo de compilación colosalmente. Incluso después de agregar otras capas agresivamente enfocadas en el entorno de ejecución (lo que costó cerca de 1 minuto extra del compilador), este enorme proyecto estabilizó un build final de apenas 3 minutos y 36 segundos.

El Veredicto: Pasamos de casi 9 minutos a simples 3.5 minutos de espera. Es más del doble de la productividad diaria a largo plazo y un alivio para el equipo de desarrolladores, sin sacrificar un binario veloz.

La Arquitectura: El Francotirador de Rendimiento 🎯

Sección titulada «La Arquitectura: El Francotirador de Rendimiento 🎯»

Diseño de Arquitectura

Alcanzar picos máximos de fluidez requiere rehacer las cosas que ya no escalan. Adoptamos tres grandes pilares de ingeniería en las docenas de implementaciones internas de búsqueda e indexación de las Colecciones:

  1. Recursión Especializada: Eliminamos comandos y bifurcaciones case de los inner loops en los algoritmos de ordenamiento. El motor establece rutas de procesamiento directo para Integer, Float y String. El procesador ahora se enfoca en la línea de meta, sin decisiones costosas en el camino.
  2. Hybrid Sort (QuickSort + Insertion Sort): Cuando la lista entra en un pequeño estrato de porciones y bloques, forzosamente abandonamos la recursión pesada del algoritmo QuickSort y la delegamos en algoritmos amigables con el caché de CPU como Insertion Sort con un enfoque lineal.
  3. Flags Mirroring: Dext reformula brillantemente el comportamiento de flags vitales: el contenedor superior TList<T> carga atributos tipo espejo, previniendo interacciones y loops de solicitar constantemente por el TypeInfo() de objetos administrados, ahorrando ciclos vitales y brutales.

Con los motores afinados, nuestras pruebas acreditan las victorias. Estos benchmarks se ejecutan estrictamente en Single-Thread y procesos completamente aislados (sin interrupciones como antivirus ni tareas en segundo plano agresivas).

OperaciónEscenarioRTL (ms)Dext (ms)Ratio %
List Sort10k Integers0.54ms0.07ms14.6% (¡6.8x Más Rápido!)
IndexOf100 Integers0.008ms0.0002ms2.4% (¡42x Más Rápido!)
Dictionary Lookup100k ítems6.61ms1.00ms15.2% (¡6.6x Más Rápido!)
List Add100k Integers0.61ms0.26ms43.2% (¡2.3x Más Rápido!)

Tipos Administrados y Objetos (Eficiencia con Seguridad)

Sección titulada «Tipos Administrados y Objetos (Eficiencia con Seguridad)»

Trabajar con referencias circulares o cadenas añade una severa carga de Reference Counting. Aún manejando esto, Dext mantuvo un aliento impecable:

OperaciónEscenarioRTL (ms)Dext (ms)Ratio %
List Add100k Objetos5.95ms4.42ms74.3% (¡Más Rápido!)
List Sort100k Strings33.29ms28.30ms85.0% (¡Más Rápido!)
Iteration (for-in)10k Strings0.16ms0.14ms88.1% (¡Más Rápido!)
Add/Populate100k Strings9.81ms9.17ms93.4% (¡Más Rápido!)

[!TIP] En escenarios vitales dentro de Strings y el procesamiento pesado de referencias en memorias administradas (donde prevalece el costo de la arquitectura Windows/SO), RTL disminuye debido al overhead general. Dext demuestra que también prevalece de manera precisa y en corto tiempo en las colecciones administradas.

Concurrencia Moderna: Canales y Operaciones Lock-Free 🚀

Sección titulada «Concurrencia Moderna: Canales y Operaciones Lock-Free 🚀»

Bases robustas necesitan distribuir cargas a través de múltiples procesos sin dolor o fricciones masivas. La gran mayoría de bibliotecas en el ecosistema de colecciones dependen enormemente de la ejecución de bloqueos mediante TCriticalSection o envolturas arcaicas a través de TMonitor. ¿El mayor problema? En poderosos servidores backend multinúcleo este escenario se convierte en fricción que restringe agresivamente el verdadero flujo (throughput) escalado.

En Dext, llevamos la base concurrente a un estadio totalmente nuevo. Nos inspiramos nativamente en el lenguaje Go (Golang) adoptando Canales de Transmisión (Channels) y estrictas, pero potentes implementaciones de colecciones orientadas sin interrupciones ni bloqueos manuales (Lock-Free).

Canales de Concurrencia

Se puede acelerar el rendimiento aislado, pero congestionar colas congela globalmente a la aplicación. IChannel<T> proporciona una estructura superior sin bloqueos:

  • Zero Lock Contention: Fluidez transparente entre productores de comandos/acciones y el pool que consume los datos.
  • Backpressure Nativo de Fábrica: Uso de permisos en búferes de base limitada (Bounded Channels) retienen la fuerza de tráfico descoordinado, previniendo que hilos productores rápidos congestionen el servidor e inunden de forma incontrolable la infraestructura.
  • Diseño Orgánico y Lineal: Escribes rutinas de código asíncrono casi tan familiares como secuencias de bloques sincrónicos.
var
Chan: IChannel<TOrder>;
begin
Chan := TChannel<TOrder>.CreateBounded(100);
// Hilo de Producción
TTask.Run(procedure
begin
while Processing do
Chan.Write(ProduceOrder);
end);
// Hilo de Consumo (¡Sin locks manuales ni dolor de cabeza!)
TTask.Run(procedure
begin
while Chan.IsOpen do
ProcessOrder(Chan.Read);
end);
end;

El enfoque basado en Locks no escala orgánicamente (a partir de 4 peticiones simultáneas entran con mucha facilidad al Windows Memory Manager congestionando colas). En cambio, los Channels del ecosistema Dext mantienen sus ritmos creciendo linealmente, resguardando de forma rentable los recursos del servidor en hardware productivo.

Estabilidad Absoluta: 140 Unit Tests 🛡️

Sección titulada «Estabilidad Absoluta: 140 Unit Tests 🛡️»

Comprobar las promesas con documentación requiere demostración estricta con código real. El nuevo núcleo de código de las Colecciones ha iniciado en un formato inquebrantable de entrega:

  • 140 pruebas automatizadas ejecutándose y aprobando al 100% de rutinas en flujos diarios.
  • Rígido y agresivo procesamiento y rastreo ante fugas de memorias sobre el ciclo de vida de objetos manejables de forma estricta.
  • Integración global y certificaciones de Channels garantizando interoperabilidad libre sin caídas.

Expresividad Fluida: Rendimiento Transparente 🧩

Sección titulada «Expresividad Fluida: Rendimiento Transparente 🧩»

El máximo desempeño de la RTL siempre solía exigir rutinas en sintaxis feas de manipular o crudamente operativas con bucles engorrosos de manejar. Pero en Dext nos importa una semántica visual elegante y expresiva, por estar en un runtime fuertemente avanzado en el nivel de compilación como Prototype.Entity. Resulta en fluidez sin un precio abrumador de instrucciones o de sobrepeso térmico para la máquina:

var
Clients: IList<TClient>;
begin
var c := Prototype.Entity<TClient>;
// Dext Sintaxis Fluida: Lectura rápida para ti, compilación certera para la máquina
Clients := Customers
.Where(c.Balance > 1000000)
.Sort(c.Balance.Asc)
.ToList;
end;

La reconstrucción completa de Dext.Collections ha demostrado con rotunda solvencia la capacidad de viabilidad técnica mediante componentes maduros en su arquitectura, superando internamente al lento legado de ecosistemas tradicionales de compilación repetitiva hacia opciones disruptivas reales para ser parte y cimentar con resultados una modernidad robusta. Sacrificamos apenas un escaso minuto extra en procesamiento base para regalar la recompensa general y alivio a largo plazo a los builds de proyectos enromes en las arquitecturas modernas.

Al final, entregamos una ingeniería para backend de Delphi diseñada para tragar operaciones severamente simultáneas que no temen bloqueos ni paradas técnicas imprevistas.

Esta implementación de colección nativa mostrada aquí y su expresividad limpia expuesta son solo un comienzo. Empezaremos a adoptar de manera absoluta e íntegra cada mejora de esta base hacia todo el ecosistema y plataforma Dext con su potencial extensivo interno, además de destinar escalabilidad o optimizaciones quirúrgicas allí donde sea factible maximizar el rendimiento general al infinito.

¿Estás listo para salir de las contenciones heredadas de la RTL y programar código fluido? Acelera con Dext. 🏁🚀