¿Por qué los archivos tar.xz son 15 veces más pequeños cuando se usa la biblioteca tar de Python en comparación con el tar de macOS?

¿Por qué los archivos tar.xz son 15 veces más pequeños cuando se usa la biblioteca tar de Python en comparación con el tar de macOS?

Contexto

Estoy comprimiendo carpetas de ~1,3 GB, cada una llena con 1440 archivos JSON y descubro que hay una diferencia de 15 veces entre usar el tarcomando y el integrado de Python.tarfilebiblioteca en macOS oraspbian 10(Destructor)

Ejemplo de trabajo mínimo

Este script compara ambos métodos:

#!/usr/bin/env python3

from pathlib import Path
from subprocess import call
import tarfile

fullpath = Path("/Users/user/Desktop/temp/tar/2021-03-11")
zsh_out = Path(fullpath.parent, "zsh-archive.tar.xz")
py_out = Path(fullpath.parent, "py-archive.tar.xz")

# tar using terminal
# tar cJf zsh-archive.tar.xz folderpath
call(["tar", "cJf", zsh_out, fullpath])

# tar using tarfile library
with tarfile.open(py_out, "w:xz") as tar:
    tar.add(fullpath, arcname=fullpath.stem)

# Print filesizes
print(f"zsh tar filesize: {round(Path(zsh_out).stat().st_size/(1024*1024), 2)} MB")
print(f"py tar filesize: {round(Path(py_out).stat().st_size/(1024*1024), 2)} MB")

La salida es:

zsh tar filesize: 23.7 MB
py tar filesize: 1.49 MB

Las versiones que uso son las siguientes:

  • taren MacOS:bsdtar 3.3.2 - libarchive 3.3.2 zlib/1.2.11 liblzma/5.0.5 bz2lib/1.0.6
  • taren Raspbian 10:xz (XZ Utils) 5.2.4 liblzma 5.2.4
  • tarfileBiblioteca de Python:0.9.0

Cosas que he probado

Después de la compresión, extraje ambos archivos y comparé la carpeta resultante con:

diff -r py-archive-expanded zsh-archive-expanded

No hubo diferencia.

Si comparo los dos archivos tar directamente, parecen diferentes:

➜ diff zsh-archive.tar.xz py-archive.tar.xz
Binary files zsh-archive.tar.xz and py-archive.tar.xz differ

Si inspecciono los archivos con Quicklook (y el complemento Betterzip), veo que los archivos en el archivo están ordenados de una manera diferente:

La izquierda es zsh-archive.tar.xz, la derecha es py-archive.tar.xz:

Ingrese la descripción de la imagen aquíingrese la descripción de la imagen aquí

El archivo zsh utiliza un orden desconocido y el archivo Python ordena el archivo por fecha de modificación. No estoy seguro si eso importa.

Pregunta

¿Qué está pasando? ¿Estoy perdiendo algo al usar la biblioteca Python para comprimir mis datos? ¿Es la diferencia de tamaño de 15 veces un indicador de algún problema? ¿O puedo seguir adelante y utilizar con seguridad la implementación eficiente de Python?

Respuesta1

Respuesta corta: sí, es seguro usar Python tarlibpara comprimir los datos, no se pierde nada en comparación con BSD tar.

Problema de fondo: clasificación

Creo que el problema subyacente es que BSD tary GNU, tarsin ninguna opción de clasificación, colocan los archivos en el archivo en un orden indefinido.

GNU tartiene una --sortopción:

ordenar las entradas del directorio según ORDER, cuál es uno de none, name, o inode.
El valor predeterminado es --sort=none, que almacena los miembros del archivo en el mismo orden en que los devuelve el sistema operativo.

Probando GNUtar

Para probar esto instalé GNU taren mi Mac con:

brew install gnu-tar

Y luego tarreó la misma carpeta, pero con la --sortopción:

gtar --sort='name' -cJf zsh-archive-sorted.tar.xz /Users/user/Desktop/temp/tar/2021-03-11

El zsh-archive-sorted.tar.xzarchivo es de 1,5 MB, igual al tamaño del archivo creado por la biblioteca Python.

Concatenar en orden ordenado

El efecto que tiene la clasificación en el tamaño del archivo final se demuestra concatenando primero todos los archivos JSON ordenados por nombre (que tiene la creación de Unixtime al principio) y luego tar con BSD tar:

cat *.json > all.txt
tar cJf zsh-cat-archive.tar.xz all.txt

El zsh-cat-archive.tar.xzarchivo también pesa 1,5 MB.

tarfileClasificación de Python

Finalmente, eldocumentación de la TarFile.addfunción de Pythonconfirma que Python tarfileordena de forma predeterminada:

Los directorios se agregan de forma recursiva de forma predeterminada. Esto se puede evitar estableciendo recursivo en False. La recursión agrega entradas en orden.

Por qué es importante ordenar

Creo que la razón por la que la clasificación tiene tal impacto en mi caso es la siguiente:

Mis archivos JSON contienen ubicaciones de cientos de vehículos. Cada minuto leo todas las ubicaciones, pero sólo algunas de estas ubicaciones tienen un valor diferente de un minuto a otro.
Al ordenar los archivos por nombre, dos archivos posteriores tienen caracteres ligeramente diferentes entre ellos. Aparentemente esto es muy favorable para la eficiencia de la compresión.

Respuesta2

Intente configurar los niveles de compresión en la línea de comando de macOS.

Sé que estás preguntando sobrexzpero explicado enesta respuesta aquí, en versiones anteriores de GZip puedes establecer el nivel de compresión con una variable de entorno como esta:

GZIP=-9 tar cf zsh-archive.tar.xz folderpath

Dicho esto, parece que sólo funciona con GZip 1.8 y se deprecia en versiones posteriores. Entonces use la opción -I/ --use-compress-program=COMMANDpara tar en su lugar; Tenga en cuenta que es posible que esta opción no funcione en macOS, pero colóquela aquí de todos modos por si acaso. Entonces el comando cambiaría a:

tar -I 'gzip -9' -cf zsh-archive.tar.xz folderpath

Y sí, estos ejemplos comprimirían el archivo Gzip en lugar de xz, pero puedes cambiar fácilmente el comando a este para usarlo xzasí:

tar -I 'xz -9' -cf zsh-archive.tar.xz folderpath

El xznivel de compresión varía de -0a -9y el valor predeterminado es -6; también lo -9es el nivel de compresión más alto.

Solo tenga en cuenta que xzno está instalado en macOS de forma predeterminada. Para instalarlo en macOS primero debes instalarcerveza caseray luego instalarxzvía Homebrew así:

brew install xz

Respuesta3

Me pregunto qué usa Python para la compresión.

http://tukaani.org/xz/

Probablemente esté usando las llamadas a funciones en liblzma.Alquitránprobablemente esté canalizando el comando xz shell.

Un comentario rápido sobre --sort=name:

La opción de clasificación es una mejora relativamente reciente de GNU tar y se introdujo en la versión tar 1.28.

Es posible que nunca se implemente en BSD tar.

información relacionada