Nuestro team especializado luego de muchos días de investigación y de recopilar de datos, obtuvimos los datos necesarios, queremos que todo este artículo sea de utilidad en tu plan.
Solución:
Resumen:
Los archivos de paquete de Git se construyen cuidadosamente para usar de manera efectiva las cachés de disco y proporcionar patrones de acceso “agradables” para comandos comunes y para leer objetos a los que se hace referencia recientemente.
El formato de archivo del paquete de Git es bastante flexible (consulte Documentation / technical / pack-format.txt, o The Packfile en The Git Community Book). Los archivos de paquete almacenan objetos de dos formas principales: “sin definir” (tomar los datos del objeto sin procesar y desinflarlos-comprimirlos) o “deltificados” (formar un delta contra algún otro objeto y luego desinflar-comprimir los datos delta resultantes). Los objetos almacenados en un paquete pueden estar en cualquier orden (no tienen (necesariamente) que ser ordenados por tipo de objeto, nombre de objeto o cualquier otro attribute) y se pueden fabricar objetos deltificados contra cualquier otro objeto adecuado del mismo tipo.
Git’s paquete-objetos comando utiliza varias heurísticas para proporcionar una excelente localidad de referencia para comandos comunes. Estas heurísticas controlan tanto la selección de objetos base para los objetos deltificados como el orden de los objetos. Cada mecanismo es mayoritariamente independiente, pero comparten algunos objetivos.
Git forma largas cadenas de objetos comprimidos delta, pero la heurística intenta asegurarse de que sólo los objetos “antiguos” estén en los extremos de las cadenas largas. La caché base delta (cuyo tamaño está controlado por el
core.deltaBaseCacheLimit
variable de configuración) se utiliza automáticamente y puede reducir en gran medida el número de “reconstrucciones” necesarias para los comandos que necesitan leer una gran cantidad de objetos (p. ej. git log
).
-p
Heurística de compresión delta
Un repositorio de Git típico almacena una gran cantidad de objetos, por lo que no puede compararlos todos razonablemente para encontrar los pares (y cadenas) que producirán las representaciones delta más pequeñas.
La heurística de selección de base delta se basa en la idea de que las buenas bases delta se encontrarán entre objetos con nombres de archivo y tamaños similares. Cada tipo de objeto se procesa por separado (es decir, un objeto de un tipo nunca se utilizará como base delta para un objeto de otro tipo).
A los efectos de la selección de base delta, los objetos se ordenan (principalmente) por nombre de archivo y luego por tamaño. Se utiliza una ventana en esta lista ordenada para limitar el número de objetos que se consideran como bases delta potenciales. Si es “suficientemente bueno”1 La representación delta no se encuentra para un objeto entre los objetos en su ventana, entonces el objeto no se comprimirá delta.
El tamaño de la ventana está controlado por el --window=
opción de
git pack-objects
, o la pack.window
variable de configuración. La profundidad máxima de una cadena delta está controlada por el --depth=
opción de git pack-objects
, o la pack.depth
variable de configuración. los --aggressive
opción de git gc
amplía en gran medida tanto el tamaño de la ventana como la profundidad máxima para intentar crear un archivo de paquete más pequeño.
El orden de nombre de archivo agrupa los objetos para entradas con nombres idénticos (o al menos terminaciones similares (p. Ej. .c
)). La clasificación por tamaño es de mayor a menor, de modo que los deltas que eliminan datos se prefieren a los deltas que agregan datos (ya que los deltas de eliminación tienen representaciones más cortas) y de modo que los objetos más antiguos y más grandes (generalmente los más nuevos) tienden a representarse con compresión simple.
1
Lo que califica como “suficientemente bueno” depende del tamaño del objeto en cuestión y su base delta potencial, así como de la profundidad de la cadena delta resultante.
Heurística de ordenación de objetos
Los objetos se almacenan en los archivos del paquete en el orden de “referencia más reciente”. Los objetos necesarios para reconstruir la historia más reciente se colocan antes en el paquete y estarán muy juntos. Por lo general, esto funciona bien para los cachés de disco del sistema operativo.
Todos los objetos de confirmación se ordenan por fecha de confirmación (la más reciente primero) y se almacenan juntos. Esta colocación y ordenación optimiza los accesos al disco necesarios para recorrer el gráfico del historial y extraer información básica de confirmación (p. Ej. git log
).
Los objetos de árbol y blob se almacenan comenzando con el árbol de la primera confirmación almacenada (más reciente). Cada árbol se procesa primero en profundidad, almacenando los objetos que aún no se han almacenado. Esto pone todos los árboles y blobs necesarios para reconstruir la confirmación más reciente en un solo lugar. Los árboles y blobs que aún no se han guardado pero que son necesarios para confirmaciones posteriores se almacenan a continuación, en el orden de confirmación ordenado.
El orden final del objeto se ve ligeramente afectado por la selección base delta en el sentido de que si se selecciona un objeto para la representación delta y su objeto base aún no se ha almacenado, entonces su objeto base se almacena inmediatamente antes que el objeto deltificado en sí. Esto evita posibles pérdidas de caché de disco debido al acceso no lineal requerido para leer un objeto base que se habría almacenado “naturalmente” más adelante en el archivo del paquete.
El uso del almacenamiento delta en el archivo del paquete es solo un detalle de implementación. En ese nivel, Git no sabe por qué o cómo cambió algo de una revisión a la siguiente, sino que solo sabe que el blob B es bastante similar al blob A excepto por estos cambios C. Por lo tanto, solo almacenará el blob A y los cambios C (si elige hacerlo, también podría optar por almacenar blob A y blob B).
Al recuperar objetos del archivo del paquete, el almacenamiento delta no está expuesto a la persona que llama. La persona que llama todavía ve manchas completas. Entonces, Git funciona de la misma manera que siempre lo ha hecho sin la optimización del almacenamiento delta.
Como mencioné en “¿Qué son los paquetes delgados de git?”
Git hace la deltificación solo en archivos de paquete
Detallé la codificación delta utilizada para los archivos de paquete en “¿Está estandarizado el algoritmo git binary diff (delta storage)?”.
Consulte también “¿Cuándo y cómo usa git deltas para el almacenamiento?”.
Tenga en cuenta que el core.deltaBaseCacheLimit
config, que controla el tamaño predeterminado del archivo del paquete, pronto pasará de 16 MB a 96 MB, para Git 2.0.x / 2.1 (tercer trimestre de 2014).
Véase la confirmación 4874f54 de David Kastrup (mayo de 2014):
Bump core.deltaBaseCacheLimit a 96m
El valor predeterminado de 16 m provoca graves problemas para las cadenas delta grandes combinadas con archivos grandes.
Aquí hay algunos puntos de referencia (variante pu de
git blame
):
time git blame -C src/xdisp.c >/dev/null
para un repositorio de Emacs reempaquetado con
git gc --aggressive
(v1.9, lo que da como resultado un tamaño de ventana de 250) ubicado en una unidad SSD.
El archivo en cuestión tiene aproximadamente 30000 líneas, 1 Mb de tamaño y un historial con aproximadamente 2500 confirmaciones.
16m (previous default):
real 3m33.936s
user 2m15.396s
sys 1m17.352s
96m:
real 2m5.668s
user 1m50.784s
sys 0m14.288s
Esto se optimiza aún más con Git 2.29 (Q4 2020), donde “git index-pack
“(hombre) aprendió a resolver objetos deltificados con mayor paralelismo.
Consulte commit f08cbf6 (08 de septiembre de 2020), y confirme ee6f058, confirme b4718ca, confirme a7f7e84, confirme 46e6fb1, confirme fc968e2, confirme 009be0d (24 de agosto de 2020) por Jonathan Tan (jhowtan
).
(Fusionada por Junio C Hamano – gitster
– en commit b7e65b5, 22 de septiembre de 2020)
index-pack
: hacer que la cantidad de trabajo sea más pequeñaFirmado por: Jonathan Tan
Actualmente, cuando el paquete de índice resuelve los deltas, no divide los árboles delta en subprocesos: cada raíz base delta (un objeto que no es un
REF_DELTA
oOFS_DELTA)
puede ir a su propio hilo, pero todos los deltas en esa raíz (directos o indirectos) se procesan en el mismo hilo.Este es un problema cuando un repositorio contiene un archivo de texto grande (por lo tanto, apto para delta) que se modifica muchas veces; el tiempo de resolución delta durante la recuperación está dominado por el procesamiento de los deltas correspondientes a ese archivo de texto.
Este parche contiene una solución para eso.
Al clonar usandogit -c core.deltabasecachelimit=1g clone https://fuchsia.googlesource.com/third_party/vulkan-cts
en mi computadora portátil, el tiempo de clonación mejoró de 3m2 a 2m5s (usando 3 subprocesos, que es el valor predeterminado).
La solución es tener una pila de trabajo global. Esta pila contiene bases delta (objetos, ya sea que aparezcan directamente en el archivo de paquete o generados por resolución delta, que a su vez tienen hijos delta) que necesitan ser procesados; siempre que un hilo necesita trabajo, se asoma en la parte superior de la pila y procesa su siguiente hijo sin procesar. Si un hilo encuentra la pila vacía, buscará más raíces base delta para empujar en la pila.
La principal debilidad de tener una pila de trabajo global es que se pasa más tiempo en el mutex, pero la elaboración de perfiles ha demostrado que la mayor parte del tiempo se dedica a la resolución de los deltas mismos, por lo que esto no debería ser un problema en la práctica. En cualquier caso, la experimentación (como se describe en el comando de clonación anterior) muestra que este parche es una mejora neta.
Con Git 2.31 (Q1 2021), tiene más detalles sobre el formato.
Consulte la confirmación 7b77f5a (29 de diciembre de 2020) de Martin Ågren (none
).
(Fusionada por Junio C Hamano – gitster
– en el compromiso 16a8055, 15 de enero de 2021)
pack-format.txt
: tamaños de documentos al comienzo de los datos deltaReportado por: Ross Light
Firmado por: Martin Ågren
Documentamos los datos delta como un conjunto de instrucciones, pero nos olvidamos de documentar los dos tamaños que preceden a esas instrucciones: el tamaño del objeto base y el tamaño del objeto a reconstruir.
Corrija esta omisión.En lugar de incluir todos los detalles sobre la codificación en el texto en ejecución, introduzca una sección separada que detalle nuestra “codificación de tamaño” y consúltela.
technical/pack-format
ahora incluye en su página de manual:
Codificación de tamaño
Este documento utiliza la siguiente “codificación de tamaño” de enteros no negativos: De cada byte, los siete bits menos significativos se utilizan para formar el entero resultante.
Siempre que el bit más significativo sea 1, este proceso continúa; el byte con MSB 0 proporciona los últimos siete bits.Los fragmentos de siete bits están concatenados.
Los valores posteriores son más significativos.Esta codificación de tamaño no debe confundirse con la “codificación de desplazamiento”, que también se utiliza en este documento.
technical/pack-format
ahora incluye en su página de manual:
Los datos delta comienzan con el tamaño del objeto base y el tamaño del objeto a reconstruir. Estos tamaños se codifican utilizando la codificación de tamaño de arriba.
El resto de los datos delta es una secuencia de instrucciones para reconstruir el objeto.