Todos los artículos
Arquitectura · Rendimiento

Del caché de rutas al índice del juego: buscar menos para ejecutar más rápido

Un juego con miles de recursos no debería redescubrir una y otra vez dónde están. Kirin toma una idea nacida para compatibilidad y la convierte en una capa de acceso pensada para saber primero y buscar después.

Evolución de miles de archivos y carpetas de RPG Maker XP hacia una tabla indexada de recursos en Kirin
La idea central es simple: dejar de recorrer el proyecto para responder preguntas que el motor puede haber resuelto de antemano.

RPG Maker XP entiende gran parte de un juego como una colección de rutas. Gráficos, sonidos, mapas y datos viven detrás de nombres como Graphics/Characters/Hero.png o Audio/BGM/Town.ogg. En proyectos pequeños esto parece trivial. En proyectos enormes, localizar recursos termina convirtiéndose en una responsabilidad del motor.

El problema no es que exista una carpeta llamada Graphics. El problema aparece cuando, durante miles de operaciones, el runtime tiene que volver a responder preguntas que ya podría conocer: si un archivo existe, cómo se escribe realmente su nombre, qué extensión tiene, qué contiene un directorio o qué variante de una ruta corresponde al recurso físico.

La optimización más rápida no siempre consiste en buscar más rápido. Muchas veces consiste en evitar que la búsqueda tenga que repetirse.

¿Qué es un caché de rutas?

Un caché de rutas es una memoria de correspondencias entre el nombre que utiliza el juego y la ubicación real del recurso. En vez de consultar continuamente el sistema de archivos, el motor puede descubrir una ruta y conservar la respuesta.

CLAVEgraphics/characters/pikachu.png
VALORGraphics/Characters/Pikachu.png

Esta idea es especialmente útil para software heredado de Windows. Muchos proyectos de RPG Maker XP fueron desarrollados asumiendo que Hero.png, hero.png y HERO.PNG podían resolverse de la misma manera. Al moverlos a Linux o Android, esa suposición deja de ser segura.

mkxp-z ya implementa un pathCache para resolver precisamente este tipo de compatibilidad: almacena una ruta completa normalizada en minúsculas y la relaciona con la ruta que conserva la capitalización real.

En su contexto original, el caché es sobre todo una respuesta al comportamiento histórico de Windows. Kirin conserva esa función, pero cambia la ambición del sistema.

De compatibilidad a infraestructura

Una vez que el motor ya ha recorrido el proyecto y conoce sus archivos, surge una pregunta inevitable: ¿por qué utilizar ese conocimiento solamente para arreglar mayúsculas y minúsculas?

Kirin empieza a tratar el conjunto de rutas como un catálogo del juego. La ruta deja de ser solamente una cadena que debe interpretarse; pasa a ser una clave que puede consultar estructuras preparadas específicamente para responder.

01Qué archivos existen y cuál es su ruta real
02Qué archivos pertenecen a cada directorio
03Qué aliases pueden resolver una misma ruta
04Tamaño y fecha de modificación asociados al recurso

La consecuencia conceptual es importante. El motor deja de pensar únicamente como un explorador de carpetas y empieza a comportarse como un sistema de consulta.

Una tabla indexada en lugar de una búsqueda repetida

Podemos imaginar un proyecto tradicional como un árbol que el runtime debe atravesar:

Graphics → Characters → Pokémon → Pikachu.png

Un índice cambia la pregunta. En lugar de “recorre esta estructura hasta encontrar Pikachu”, la operación pasa a ser “dame el recurso asociado a esta clave”.

Kirin utiliza tablas hash en memoria para acercar la resolución de rutas al comportamiento de una tabla indexada. En el caso promedio, una búsqueda hash permite localizar una entrada sin recorrer secuencialmente todos los recursos del proyecto. Tener veinte mil archivos no debería convertir cada consulta en una exploración de veinte mil posibilidades.

Buscar

  1. Recibir una ruta
  2. Consultar el filesystem
  3. Enumerar o probar candidatos
  4. Comparar nombres
  5. Obtener el recurso

Consultar

  1. Recibir una ruta
  2. Normalizar la clave
  3. Consultar el índice
  4. Obtener la entrada
  5. Abrir el recurso

Esto no elimina el coste de abrir y leer el archivo cuando realmente se necesitan sus datos. Lo que elimina es una parte del trabajo de descubrir cuál archivo corresponde a la petición.

Construir una vez, responder miles de veces

Todo índice tiene un coste de construcción. Alguien debe recorrer el proyecto, normalizar las rutas y preparar las tablas. La ventaja aparece cuando esa operación relativamente cara sustituye miles de operaciones posteriores.

La implementación actual de Kirin mantiene un mapa principal de rutas, estadísticas asociadas a cada recurso, aliases para resolución directa y una representación de las entradas de los directorios. Una vez organizadas esas estructuras, consultas como existencia, resolución de nombre o listado de carpeta pueden permanecer en memoria en el camino habitual.

  • Menos enumeraciones repetitivas de directorios
  • Menos pruebas de nombres y extensiones
  • Menos consultas de existencia al almacenamiento
  • Menos trabajo para resolver diferencias de capitalización
  • Respuestas reutilizables entre sistemas Ruby, gráficos y audio

La filosofía es muy sencilla: si Kirin ya sabe que un recurso existe, no necesita volver a preguntárselo al sistema operativo cada vez que Ruby lo consulta.

El índice también puede resolver nombres incompletos

RGSS no siempre solicita los recursos con la extensión escrita explícitamente. Un script puede pedir Graphics/Pictures/Menu aunque físicamente exista Graphics/Pictures/Menu.png.

En Kirin, durante la construcción del índice pueden generarse aliases que relacionan distintas formas lógicas con el mismo recurso. Así, claves como:

Agraphics/pictures/menu
Bgraphics/pictures/menu.png

pueden terminar apuntando a la misma ruta resuelta. El descubrimiento de esa relación se desplaza desde cada acceso hacia una etapa de preparación.

La implementación también contempla aliases normalizados para determinadas diferencias de acentuación, otra fuente de incompatibilidades cuando proyectos creados durante años en Windows terminan ejecutándose sobre sistemas con reglas diferentes.

Persistir el conocimiento: el índice binario

Hasta aquí todavía podríamos construir el catálogo cada vez que se inicia el juego. Eso ya acelera las consultas durante la ejecución, pero sigue obligando a redescubrir miles de archivos en cada arranque.

Kirin da otro paso: persiste el catálogo en un binario.

1ª VEZenumerar → organizar → indexar → guardar binario
DESPUÉSleer binario → reconstruir índices en RAM → ejecutar

El archivo utiliza una firma derivada del conjunto de rutas montadas para identificar el contexto al que pertenece. En Android, la implementación evita además depender directamente de rutas de instalación del APK que pueden cambiar entre reinstalaciones. El objetivo es que el índice represente al juego y a su espacio de recursos, no a una ubicación temporal del sistema.

La versión actual del formato se identifica internamente con la cabecera MKXPPC04 y serializa las rutas normalizadas, las rutas reales, datos básicos de archivo y las listas por directorio. El guardado se realiza mediante un archivo temporal y reemplazo final para evitar dejar un caché parcialmente escrito como resultado válido.

El binario no se busca: se carga

Persistir el índice tendría poco sentido si cada solicitud tuviera que abrir el binario y recorrerlo. Kirin no utiliza ese modelo.

El archivo binario sirve para conservar el conocimiento entre ejecuciones. Al arrancar, Kirin lo lee y reconstruye las estructuras de consulta en RAM. Después, el camino caliente del juego utiliza esas tablas en memoria.

Disco → índice binario → tablas hash en RAM → consultas del juego

El almacenamiento conserva. La memoria responde.

Ésta es una diferencia fundamental para entender por qué el sistema busca eficiencia extrema. No se cambia una exploración de carpetas por una exploración de un archivo más grande. Se utiliza una representación persistente para volver rápidamente a un estado donde las respuestas ya están organizadas.

Los directorios también pueden dejar de ser una pregunta

Muchas optimizaciones se concentran en abrir archivos, pero un juego también puede preguntar qué contiene una carpeta. Sin un índice, listar un directorio significa volver a consultar el filesystem.

Kirin construye una representación completa de las entradas de directorio en memoria. Así, operaciones de listado pueden resolverse desde el catálogo ya preparado en lugar de enumerar otra vez el almacenamiento.

Esto importa especialmente en Android, donde el árbol lógico del juego puede combinar almacenamiento externo, recursos montados mediante PhysFS y contenido perteneciente a la aplicación. Cada llamada evitada elimina capas de trabajo que no aportan nada nuevo si el motor ya conoce la respuesta.

El índice no elimina la compatibilidad

Optimizar un runtime para juegos antiguos exige una precaución: algunos proyectos hacen cosas extrañas. Pueden crear archivos dinámicamente, depender de rutas inesperadas o utilizar APIs de maneras que no aparecen en el caso común.

Por eso un índice agresivo no significa que el sistema de archivos desaparezca. Kirin puede utilizar el camino indexado para el caso normal y conservar un fallback físico cuando una búsqueda no puede resolverse desde las estructuras preparadas.

Optimizar el caso común sin cerrar la puerta a lo excepcional. Ésa es una regla importante cuando la compatibilidad abarca proyectos creados durante más de veinte años.

El sistema de archivos deja de ser la base de datos del juego

Éste es el cambio conceptual que más nos interesa.

En RPG Maker XP, las carpetas son prácticamente el catálogo. Para saber qué existe, el runtime mira el árbol de archivos. Kirin intenta separar ambas responsabilidades.

Modelo heredado

  1. Las carpetas contienen la verdad
  2. El runtime pregunta al filesystem
  3. Las rutas se resuelven al necesitarlas
  4. El trabajo se repite

Modelo indexado

  1. El catálogo conoce la verdad lógica
  2. Las claves resuelven recursos
  3. Las respuestas viven en RAM
  4. El almacenamiento entrega los datos

Ese cambio abre además la puerta a una evolución mayor. Si el juego ya accede a recursos mediante un catálogo lógico, la representación física puede cambiar sin obligar a Ruby a conocerlo. Un día el recurso puede ser un PNG suelto; otro, una entrada dentro de un paquete gráfico indexado. Para RGSS puede continuar llamándose exactamente igual.

La búsqueda más rápida es la que ya fue resuelta

Cuando se habla de rendimiento solemos imaginar instrucciones más rápidas, compiladores JIT o una GPU capaz de dibujar más. Pero muchos cuellos de botella nacen simplemente de hacer trabajo innecesario demasiadas veces.

No recorrer un directorio si ya conocemos su contenido. No probar extensiones si ya sabemos cuál existe. No preguntar al almacenamiento si la respuesta está en RAM. No reconstruir un catálogo completo si puede recuperarse desde un binario preparado.

DESCUBRIR MENOS → INDEXAR MEJOR → CONSULTAR MÁS RÁPIDO

Ésa es la evolución que Kirin busca para el caché de rutas. Una idea que en mkxp-z existe principalmente para reproducir la tolerancia de Windows empieza a convertirse en una infraestructura general de acceso a recursos.

El proyecto continúa viendo rutas familiares. Ruby continúa pidiendo Graphics/Characters/Pikachu. La compatibilidad permanece arriba.

Debajo, sin embargo, el motor puede dejar de buscar como lo hacía un programa de 2004 y empezar a consultar como un sistema que ya conoce su propio juego.

Prueba Kirin en Android

Consulta la guía de instalación y añade un proyecto compatible a tu biblioteca.

Ver guía de instalación