Contexto
Estou compactando pastas de ~ 1,3 GB, cada uma preenchida com 1440 arquivos JSON e descobri que há uma diferença de 15 vezes entre usar o tar
comando e a tarfile
biblioteca interna do Python no macOS ou Raspbian 10 (Buster)
Exemplo de trabalho mínimo
Este script compara os dois 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")
A saída é:
zsh tar filesize: 23.7 MB
py tar filesize: 1.49 MB
As versões que uso são as seguintes:
tar
no macOS:bsdtar 3.3.2 - libarchive 3.3.2 zlib/1.2.11 liblzma/5.0.5 bz2lib/1.0.6
tar
no Raspbian 10:xz (XZ Utils) 5.2.4 liblzma 5.2.4
tarfile
Biblioteca Python:0.9.0
Coisas que eu tentei
Após a compactação, extraí os dois arquivos e comparei a pasta resultante com:
diff -r py-archive-expanded zsh-archive-expanded
Não houve diferença.
Se eu comparar os dois arquivos tar diretamente, eles parecem diferentes:
➜ diff zsh-archive.tar.xz py-archive.tar.xz
Binary files zsh-archive.tar.xz and py-archive.tar.xz differ
Se eu inspecionar os arquivos com Quicklook (e o plugin Betterzip), vejo que os arquivos no arquivo estão ordenados de maneira diferente:
A esquerda é zsh-archive.tar.xz
, a direita é py-archive.tar.xz
:
O arquivo zsh usa uma ordem desconhecida e o arquivo Python ordena o arquivo por data de modificação. Não tenho certeza se isso importa.
Pergunta
O que está acontecendo? Estou perdendo algo usando a biblioteca Python para compactar meus dados? A diferença de 15 vezes no tamanho é um indicador de algum problema? Ou posso prosseguir com segurança e usar a implementação eficiente do Python?
Resposta curta: sim, é seguro usar o Python
tarlib
para compactar os dados, nada é perdido em comparação com o BSDtar
.Problema subjacente: classificação
Eu acho que o problema subjacente é que o BSD
tar
e o GNUtar
sem nenhuma opção de classificação colocam os arquivos no arquivo em uma ordem indefinida.O GNU
tar
tem uma--sort
opção:Testando GNU
tar
Para testar isso, instalei o GNU
tar
no meu Mac com:E depois tareou a mesma pasta, mas com a
--sort
opção:O
zsh-archive-sorted.tar.xz
arquivo tem 1,5 MB, igual ao tamanho do arquivo criado pela biblioteca Python.Concatenar em ordem ordenada
O efeito que a classificação tem no tamanho final do arquivo é ainda demonstrado concatenando primeiro todos os arquivos JSON classificados por nome (que tem a criação unixtime no início) e, em seguida, tarra com BSD
tar
:O
zsh-cat-archive.tar.xz
arquivo também tem 1,5 MB.tarfile
Classificação PythonFinalmente, a documentação da
TarFile.add
função Python confirma que o Pythontarfile
classifica por padrão:Por que a classificação é importante
Acho que a razão pela qual a classificação tem tanto impacto no meu caso é a seguinte:
Meus arquivos JSON contêm localizações de centenas de veículos. A cada minuto eu leio todos os locais, mas apenas alguns desses locais têm um valor diferente de minuto para minuto.
Ao classificar os arquivos por nome, dois arquivos subsequentes têm caracteres pouco diferentes entre eles. Aparentemente isso é muito favorável para a eficiência de compressão.
Tente definir os níveis de compactação na linha de comando do macOS.
Eu sei que você está perguntando,
xz
mas explicado nesta resposta aqui , em versões mais antigas do GZip, você pode definir o nível de compactação com uma variável de ambiente como esta:Dito isto, isso só parece funcionar com o GZip 1.8 e é depreciado em versões posteriores. Portanto, use a opção
-I
/--use-compress-program=COMMAND
para tar; observe que esta opção pode não funcionar no macOS, mas colocando aqui de qualquer maneira apenas por precaução. Então o comando mudaria para:E sim, esses exemplos seriam compactar o arquivo Gzip em vez de
xz
, mas você pode facilmente alterar o comando para isso para usarxz
assim:O
xz
nível de compactação varia de-0
a-9
com o padrão sendo-6
; assim-9
é o nível de compressão mais alto.Apenas observe que
xz
não está instalado no macOS por padrão. Para instalá-lo no macOS, você deve primeiro instalar o Homebrew e depois instalarxz
via Homebrew assim:http://tukaani.org/xz/
Provavelmente está usando as chamadas de função em liblzma. O Tar provavelmente está canalizando o comando xz shell.
Um comentário rápido sobre
--sort=name
:A opção sort é um aprimoramento relativamente recente do GNU tar e foi introduzido no tar versão 1.28.
Ele nunca pode ser implementado no BSD tar.