O sistema avançado de arquivos somente leitura com desduplicação e velocidade de warp .
Um sistema de arquivos somente leitura rápido e de alta compactação para Linux e Windows.
DwarFS é um sistema de arquivos somente leitura com foco em alcançar taxas de compactação muito altas , especialmente para dados muito redundantes.
Provavelmente isso não parece muito interessante, porque se for redundante, deverá compactar bem. No entanto, descobri que outros sistemas de arquivos compactados somente leitura não fazem um bom trabalho ao usar essa redundância. Veja aqui uma comparação com outros sistemas de arquivos compactados.
O DwarFS também não compromete a velocidade e, para meus casos de uso, descobri que ele está no mesmo nível ou tem um desempenho melhor que o SquashFS. Para meu caso de uso principal, a compactação DwarFS é uma ordem de magnitude melhor que a compactação SquashFS , é 6 vezes mais rápida para construir o sistema de arquivos , normalmente é mais rápida para acessar arquivos no DwarFS e usa menos recursos de CPU.
Para se ter uma ideia do que o DwarFS é capaz, aqui está uma rápida comparação entre DwarFS e SquashFS em um conjunto de arquivos de vídeo com tamanho total de 39 GiB. A diferença é que cada arquivo de vídeo exclusivo possui dois arquivos irmãos com um conjunto diferente de fluxos de áudio (este é um caso de uso real). Portanto, há redundância nos dados de vídeo e áudio, mas como os fluxos são intercalados e os blocos idênticos geralmente estão muito distantes um do outro, é um desafio usar essa redundância para compactação. O SquashFS essencialmente não consegue compactar os dados de origem, enquanto o DwarFS é capaz de reduzir o tamanho em quase um fator de 3, o que está próximo do máximo teórico:
$ du -hs dwarfs-video-test
39G dwarfs-video-test
$ ls -lh dwarfs-video-test.*fs
-rw-r--r-- 1 mhx users 14G Jul 2 13:01 dwarfs-video-test.dwarfs
-rw-r--r-- 1 mhx users 39G Jul 12 09:41 dwarfs-video-test.squashfs
Além disso, ao montar a imagem SquashFS e realizar um teste de taxa de transferência de leitura aleatória usando fio-3.34, tanto squashfuse
quanto squashfuse_ll
atingem cerca de 230 MiB/s:
$ fio --readonly --rw=randread --name=randread --bs=64k --direct=1
--opendir=mnt --numjobs=4 --ioengine=libaio --iodepth=32
--group_reporting --runtime=60 --time_based
[...]
READ: bw=230MiB/s (241MB/s), 230MiB/s-230MiB/s (241MB/s-241MB/s), io=13.5GiB (14.5GB), run=60004-60004msec
Em comparação, o DwarFS consegue sustentar taxas de leitura aleatória de 20 GiB/s :
READ: bw=20.2GiB/s (21.7GB/s), 20.2GiB/s-20.2GiB/s (21.7GB/s-21.7GB/s), io=1212GiB (1301GB), run=60001-60001msec
Os recursos distintos do DwarFS são:
Agrupamento de arquivos por similaridade usando uma função hash de similaridade. Isso facilita a exploração da redundância entre limites de arquivos.
Análise de segmentação entre blocos do sistema de arquivos para reduzir o tamanho do sistema de arquivos descompactado. Isso economiza memória ao usar o sistema de arquivos compactado e, portanto, permite taxas de acertos de cache mais altas, pois mais dados podem ser mantidos no cache.
Estrutura de categorização para categorizar arquivos ou mesmo fragmentos de arquivos e, em seguida, processar categorias individuais de maneira diferente. Por exemplo, isso permite que você não perca tempo tentando compactar arquivos incompressíveis ou compactar dados de áudio PCM usando compactação FLAC.
Implementação altamente multithread. Tanto a ferramenta de criação de sistema de arquivos quanto o driver FUSE são capazes de fazer bom uso dos vários núcleos do seu sistema.
Comecei a trabalhar no DwarFS em 2013 e meu principal caso de uso e principal motivação era que eu tinha centenas de versões diferentes do Perl que ocupavam algo em torno de 30 gigabytes de espaço em disco e não estava disposto a gastar mais de 10% do meu disco rígido. dirigir e mantê-los por perto para quando eu precisar deles.
Até então, eu usava Cromfs para comprimi-los em um tamanho gerenciável. No entanto, eu estava ficando cada vez mais irritado com o tempo que levava para construir a imagem do sistema de arquivos e, para piorar as coisas, na maioria das vezes ela travava após cerca de uma hora ou mais.
Obviamente, eu também pesquisei o SquashFS, mas nunca cheguei perto das taxas de compactação do Cromfs.
Isso por si só não teria sido suficiente para me levar a escrever o DwarFS, mas, na mesma época, eu estava bastante obcecado com os desenvolvimentos e recursos recentes dos padrões C++ mais recentes e realmente queria um projeto de hobby em C++ para trabalhar. Além disso, já faz algum tempo que queria fazer algo com o FUSE. Por último, mas não menos importante, estive pensando um pouco sobre o problema dos sistemas de arquivos compactados e tive algumas ideias que definitivamente gostaria de experimentar.
A maior parte do código foi escrita em 2013, então eu fazia algumas limpezas, correções de bugs e refatorações de vez em quando, mas nunca cheguei a um estado em que me sentisse feliz em lançá-lo. Era muito estranho construir com sua dependência da biblioteca (bastante incrível) do Facebook e não tinha nenhuma documentação.
Ao desenterrar o projeto novamente este ano, as coisas não pareciam tão sombrias como antes. Folly agora é compilado com CMake e então eu o coloquei como um submódulo. A maioria das outras dependências pode ser satisfeita a partir de pacotes que deveriam estar amplamente disponíveis. E também escrevi alguns documentos rudimentares.
O DwarFS geralmente deve ser construído bem com alterações mínimas prontas para uso. Caso contrário, registre um problema. Configurei trabalhos de CI usando imagens Docker para Ubuntu (22.04 e 24.04), Fedora Rawhide e Arch que podem ajudar a determinar um conjunto atualizado de dependências. Observe que compilar a partir do tarball de lançamento requer menos dependências do que compilar a partir do repositório git, notadamente a ferramenta ronn
, bem como Python e o módulo mistletoe
Python não são necessários ao compilar a partir do tarball de lançamento.
Há algumas coisas que você deve estar ciente:
Há uma tendência de tentar desagregar as bibliotecas Folly e Fbthrift que estão incluídas como submódulos e são construídas junto com o DwarFS. Embora eu concorde com o sentimento, infelizmente é uma má ideia. Além do fato de que o Folly não faz nenhuma reivindicação sobre a estabilidade da ABI (ou seja, você não pode simplesmente vincular dinamicamente um binário construído em uma versão do Folly a outra versão), nem é possível vincular com segurança a uma biblioteca do Folly construída com versões de compilação diferentes. opções. Mesmo diferenças sutis, como a versão padrão C++, podem causar erros em tempo de execução. Consulte este problema para obter detalhes. Atualmente, nem é possível usar versões externas do folly/fbthrift, pois o DwarFS está construindo subconjuntos mínimos de ambas as bibliotecas; eles estão agrupados na biblioteca dwarfs_common
e são usados estritamente internamente, ou seja, nenhum dos cabeçalhos folly ou fbthrift é necessário para construir nas bibliotecas do DwarFS.
Problemas semelhantes podem surgir ao usar uma versão do GoogleTest instalada no sistema. O próprio GoogleTest recomenda que ele seja baixado como parte da compilação. No entanto, você pode usar a versão instalada do sistema passando -DPREFER_SYSTEM_GTEST=ON
para a chamada cmake
. Use por sua conta e risco.
Para outras bibliotecas incluídas (ou seja, fmt
, parallel-hashmap
, range-v3
), a versão instalada pelo sistema é usada desde que atenda à versão mínima exigida. Caso contrário, a versão preferida será obtida durante a construção.
Cada versão possui binários pré-construídos e vinculados estaticamente para Linux-x86_64
, Linux-aarch64
e Windows-AMD64
disponíveis para download. Eles devem ser executados sem dependências e podem ser úteis especialmente em distribuições mais antigas, onde você não pode construir facilmente as ferramentas a partir do código-fonte.
Além dos tarballs binários, há um binário universal disponível para cada arquitetura. Esses binários universais contêm todas as ferramentas ( mkdwarfs
, dwarfsck
, dwarfsextract
e o driver FUSE dwarfs
) em um único executável. Esses executáveis são compactados usando upx, portanto são muito menores do que as ferramentas individuais combinadas. No entanto, isso também significa que os binários precisam ser descompactados cada vez que são executados, o que pode gerar uma sobrecarga significativa. Se isso for um problema, você pode optar pelos binários individuais "clássicos" ou descompactar o binário universal, por exemplo:
upx -d dwarfs-universal-0.7.0-Linux-aarch64
Os binários universais podem ser executados através de links simbólicos com os nomes da ferramenta adequada. por exemplo:
$ ln -s dwarfs-universal-0.7.0-Linux-aarch64 mkdwarfs
$ ./mkdwarfs --help
Isso também funciona no Windows se o sistema de arquivos suportar links simbólicos:
> mklink mkdwarfs.exe dwarfs-universal-0.7.0-Windows-AMD64.exe
> .mkdwarfs.exe --help
Alternativamente, você pode selecionar a ferramenta passando --tool=<name>
como o primeiro argumento na linha de comando:
> .dwarfs-universal-0.7.0-Windows-AMD64.exe --tool=mkdwarfs --help
Observe que, assim como o binário dwarfs.exe
do Windows, o binário universal do Windows depende do winfsp-x64.dll
do projeto WinFsp. No entanto, para o binário universal, a DLL é carregada lentamente, portanto você ainda pode usar todas as outras ferramentas sem a DLL. Consulte a seção Suporte do Windows para obter mais detalhes.
DwarFS usa CMake como ferramenta de construção.
Ele usa Boost e Folly, embora o último esteja incluído como um submódulo, já que poucas distribuições realmente oferecem pacotes para ele. O próprio Folly tem várias dependências, então verifique aqui uma lista atualizada.
Ele também usa o Facebook Thrift, em particular a biblioteca frozen
, para armazenar metadados em um formato altamente eficiente em termos de espaço, mapeável em memória e bem definido. Ele também está incluído como um submódulo, e construímos apenas o compilador e uma biblioteca muito reduzida que contém apenas o suficiente para o DwarFS funcionar.
Fora isso, o DwarFS realmente depende apenas do FUSE3 e de um conjunto de bibliotecas de compressão das quais o Folly já depende (ou seja, lz4, zstd e liblzma).
A dependência do googletest será resolvida automaticamente se você construir com testes.
Um bom ponto de partida para sistemas baseados em apt é provavelmente:
$ apt install
gcc
g++
clang
git
ccache
ninja-build
cmake
make
bison
flex
fuse3
pkg-config
binutils-dev
libacl1-dev
libarchive-dev
libbenchmark-dev
libboost-chrono-dev
libboost-context-dev
libboost-filesystem-dev
libboost-iostreams-dev
libboost-program-options-dev
libboost-regex-dev
libboost-system-dev
libboost-thread-dev
libbrotli-dev
libevent-dev
libhowardhinnant-date-dev
libjemalloc-dev
libdouble-conversion-dev
libiberty-dev
liblz4-dev
liblzma-dev
libzstd-dev
libxxhash-dev
libmagic-dev
libparallel-hashmap-dev
librange-v3-dev
libssl-dev
libunwind-dev
libdwarf-dev
libelf-dev
libfmt-dev
libfuse3-dev
libgoogle-glog-dev
libutfcpp-dev
libflac++-dev
nlohmann-json3-dev
Observe que ao compilar com gcc
, o nível de otimização será definido como -O2
em vez do padrão CMake de -O3
para compilações de lançamento. Pelo menos com versões até gcc-10
, a compilação -O3
é até 70% mais lenta que uma compilação com -O2
.
Primeiro, descompacte o arquivo de lançamento:
$ tar xvf dwarfs-x.y.z.tar.xz
$ cd dwarfs-x.y.z
Alternativamente, você também pode clonar o repositório git, mas esteja ciente de que ele tem mais dependências e a construção provavelmente demorará mais porque o arquivo de lançamento vem com a maioria dos arquivos gerados automaticamente que terão que ser gerados durante a construção a partir do repositório:
$ git clone --recurse-submodules https://github.com/mhx/dwarfs
$ cd dwarfs
Depois que todas as dependências forem instaladas, você poderá construir o DwarFS usando:
$ mkdir build
$ cd build
$ cmake .. -GNinja -DWITH_TESTS=ON
$ ninja
Você pode então executar testes com:
$ ctest -j
Todos os binários usam jemalloc como alocador de memória por padrão, pois normalmente usa muito menos memória do sistema em comparação com os alocadores glibc
ou tcmalloc
. Para desabilitar o uso de jemalloc
, passe -DUSE_JEMALLOC=0
na linha de comando cmake
.
Também é possível construir/instalar as bibliotecas, ferramentas e driver FUSE DwarFS de forma independente. Isso é mais interessante ao empacotar o DwarFS. Observe que as ferramentas e o driver FUSE exigem que as bibliotecas estejam compiladas ou já instaladas. Para construir apenas as bibliotecas, use:
$ cmake .. -GNinja -DWITH_TESTS=ON -DWITH_LIBDWARFS=ON -DWITH_TOOLS=OFF -DWITH_FUSE_DRIVER=OFF
Depois que as bibliotecas forem testadas e instaladas, você poderá construir as ferramentas (ou seja, mkdwarfs
, dwarfsck
, dwarfsextract
) usando:
$ cmake .. -GNinja -DWITH_TESTS=ON -DWITH_LIBDWARFS=OFF -DWITH_TOOLS=ON -DWITH_FUSE_DRIVER=OFF
Para construir o driver FUSE, use:
$ cmake .. -GNinja -DWITH_TESTS=ON -DWITH_LIBDWARFS=OFF -DWITH_TOOLS=OFF -DWITH_FUSE_DRIVER=ON
A instalação é tão fácil quanto:
$ sudo ninja install
Embora você não precise instalar as ferramentas para brincar com elas.
A tentativa de construir binários vinculados estaticamente é altamente desencorajada e não tem suporte oficial. Dito isto, veja como configurar um ambiente onde você possa construir binários estáticos.
Isso foi testado com ubuntu-22.04-live-server-amd64.iso
. Primeiro, instale todos os pacotes listados como dependências acima. Instale também:
$ apt install ccache ninja libacl1-dev
ccache
e ninja
são opcionais, mas ajudam na compilação rápida.
Dependendo da sua distribuição, você precisará construir e instalar versões estáticas de algumas bibliotecas, por exemplo, libarchive
e libmagic
para Ubuntu:
$ wget https://github.com/libarchive/libarchive/releases/download/v3.6.2/libarchive-3.6.2.tar.xz
$ tar xf libarchive-3.6.2.tar.xz && cd libarchive-3.6.2
$ ./configure --prefix=/opt/static-libs --without-iconv --without-xml2 --without-expat
$ make && sudo make install
$ wget ftp://ftp.astron.com/pub/file/file-5.44.tar.gz
$ tar xf file-5.44.tar.gz && cd file-5.44
$ ./configure --prefix=/opt/static-libs --enable-static=yes --enable-shared=no
$ make && make install
É isso! Agora você pode tentar construir binários estáticos para DwarFS:
$ git clone --recurse-submodules https://github.com/mhx/dwarfs
$ cd dwarfs && mkdir build && cd build
$ cmake .. -GNinja -DWITH_TESTS=ON -DSTATIC_BUILD_DO_NOT_USE=ON
-DSTATIC_BUILD_EXTRA_PREFIX=/opt/static-libs
$ ninja
$ ninja test
Por favor, verifique as páginas de manual de mkdwarfs, pigmeus, pigmeus e pigmeus. Você também pode acessar as páginas de manual usando a opção --man
para cada binário, por exemplo:
$ mkdwarfs --man
A página de manual dos anões também mostra um exemplo de configuração do DwarFS com overlayfs para criar uma montagem de sistema de arquivos gravável sobre uma imagem DwarFS somente leitura.
Uma descrição do formato do sistema de arquivos DwarFS pode ser encontrada em formato anão.
Uma visão geral de alto nível da operação interna do mkdwarfs
é mostrada neste diagrama de sequência.
Usar as bibliotecas DwarFS deve ser bastante simples se você estiver usando o CMake para construir seu projeto. Para um início rápido, dê uma olhada no código de exemplo que usa as bibliotecas para imprimir informações sobre uma imagem DwarFS ( dwarfsck
) ou extraí-la ( dwarfsextract
).
Existem cinco bibliotecas individuais:
dwarfs_common
contém o código comum exigido por todas as outras bibliotecas. As interfaces são definidas dwarfs/
.
dwarfs_reader
contém todo o código necessário para ler dados de uma imagem DwarFS. As interfaces são definidas em dwarfs/reader/
.
dwarfs_extractor
contém o ccode necessário para extrair uma imagem DwarFS usando libarchive
. As interfaces são definidas em dwarfs/utility/filesystem_extractor.h
.
dwarfs_writer
contém o código necessário para criar imagens DwarFS. As interfaces são definidas dwarfs/writer/
.
dwarfs_rewrite
contém o código para reescrever imagens DwarFS. As interfaces são definidas em dwarfs/utility/rewrite_filesystem.h
.
Os cabeçalhos nas subpastas internal
só são acessíveis no momento da compilação e não serão instalados. O mesmo vale para a subpasta tool
.
As APIs do leitor e do extrator devem ser bastante estáveis. As APIs do gravador provavelmente mudarão. Observe, entretanto, que não há garantias de estabilidade da API antes que este projeto atinja a versão 1.0.0.
O suporte para o sistema operacional Windows é atualmente experimental. Tendo trabalhado praticamente exclusivamente no mundo Unix nas últimas duas décadas, minha experiência com o desenvolvimento do Windows é bastante limitada e eu espero que definitivamente haja bugs e arestas no código do Windows.
A versão Windows do driver do sistema de arquivos DwarFS depende do incrível projeto WinFsp e seu winfsp-x64.dll
deve ser detectável pelo driver dwarfs.exe
.
As diferentes ferramentas devem se comportar praticamente da mesma forma, quer você as use no Linux ou no Windows. As imagens do sistema de arquivos podem ser copiadas entre Linux e Windows e as imagens criadas em um sistema operacional devem funcionar bem no outro.
Porém, há algumas coisas que vale a pena destacar:
DwarFS oferece suporte a hardlinks e links simbólicos no Windows, assim como no Linux. No entanto, a criação de hardlinks e links simbólicos parece exigir privilégios de administrador no Windows; portanto, se você quiser, por exemplo, extrair uma imagem DwarFS que contenha algum tipo de link, poderá encontrar erros se não tiver os privilégios corretos.
Devido a um problema no WinFsp, os links simbólicos não podem apontar para fora do sistema de arquivos montado. Além disso, devido a outro problema no WinFsp, links simbólicos com uma letra de unidade aparecerão com um caminho de destino mutilado.
O driver DwarFS no Windows relata corretamente as contagens de hardlink por meio de sua API, mas atualmente essas contagens não são propagadas corretamente para a camada do sistema de arquivos do Windows. Presumivelmente, isso se deve a um problema no WinFsp.
Ao montar uma imagem DwarFS no Windows, o ponto de montagem não deve existir. Isso é diferente do Linux, onde o ponto de montagem deve realmente existir. Além disso, é possível montar uma imagem DwarFS como uma letra de unidade, por exemplo
anões.exe imagem.dwarfs Z:
As regras de filtro para mkdwarfs
sempre exigem separadores de caminho Unix, independentemente de estarem em execução no Windows ou no Linux.
Construir no Windows não é muito complicado graças ao vcpkg. Você precisará instalar:
Visual Studio e o compilador MSVC C/C++
Git
CMake
Ninja
WinFsp
Espera-se que WinFsp
seja instalado em C:Program Files (x86)WinFsp
; caso contrário, você precisará definir WINFSP_PATH
ao executar o CMake via cmake/win.bat
.
Agora você precisa clonar vcpkg
e dwarfs
:
> cd %HOMEPATH%
> mkdir git
> cd git
> git clone https://github.com/Microsoft/vcpkg.git
> git clone https://github.com/mhx/dwarfs
Então, inicialize vcpkg
:
> .vcpkgbootstrap-vcpkg.bat
E construa DwarFS:
> cd dwarfs
> mkdir build
> cd build
> ..cmakewin.bat
> ninja
Feito isso, você poderá executar os testes. Defina CTEST_PARALLEL_LEVEL
de acordo com o número de núcleos de CPU em sua máquina.
> set CTEST_PARALLEL_LEVEL=10
> ninja test
As bibliotecas e ferramentas DwarFS ( mkdwarfs
, dwarfsck
, dwarfsextract
)estão agora disponíveis no Homebrew:
$ brew install dwarfs
$ brew test dwarfs
A versão macOS do driver do sistema de arquivos DwarFS depende do incrível projeto macFUSE. Até que uma fórmula seja adicionada, você terá que construir o driver DwarFS FUSE manualmente.
Construir no macOS deve ser relativamente simples:
Instale o Homebrew
Use o Homebrew para instalar as dependências necessárias:
$ brew install cmake ninja macfuse brotli howard-hinnant-date double-conversion
fmt glog libarchive libevent flac openssl nlohmann-json pkg-config
range-v3 utf8cpp xxhash boost zstd
Ao instalar o macFUSE pela primeira vez, você precisará permitir explicitamente o software em Preferências do Sistema / Privacidade e Segurança . É bem provável que você tenha que reiniciar depois disso.
Baixe um tarball de lançamento na página de lançamentos e extraia-o:
$ wget https://github.com/mhx/dwarfs/releases/download/v0.10.0/dwarfs-0.10.0.tar.xz
$ tar xf dwarfs-0.10.0.tar.xz
$ cmake --fresh -B dwarfs-build -S dwarfs-0.10.0 -GNinja -DWITH_TESTS=ON
$ cmake --build dwarfs-build
$ ctest --test-dir dwarfs-build -j
macfuse
da brew install
e usar o seguinte em vez do primeiro comando cmake
acima: $ cmake --fresh -B dwarfs-build -S dwarfs-0.10.0 -GNinja -DWITH_TESTS=ON -DWITH_FUSE_DRIVER=OFF
$ cmake --fresh -B dwarfs-build -S dwarfs-0.10.0 -GNinja -DWITH_TESTS=ON -DWITH_LIBDWARFS=OFF -DWITH_TOOLS=OFF
$ sudo cmake --install dwarfs-build
É isso!
A astrofotografia pode gerar grandes quantidades de dados brutos de imagens. Durante uma única noite, não é improvável que acabe com algumas dezenas de gigabytes de dados. Com a maioria das câmeras astrofotográficas dedicadas, esses dados acabam na forma de imagens FITS. Geralmente são descompactados, não são compactados muito bem com algoritmos de compactação padrão e, embora existam certos formatos FITS compactados, eles não são amplamente suportados.
Um dos formatos de compactação (simplesmente chamado de "Arroz") compacta razoavelmente bem e é muito rápido. No entanto, a sua implementação para FITS comprimidos tem algumas desvantagens. As desvantagens mais graves são que a compactação não é tão boa quanto poderia ser para sensores de cores e sensores com resolução inferior a 16 bits.
DwarFS suporta a compactação ricepp
(Rice++), que se baseia na ideia básica da compactação Rice, mas faz algumas melhorias: ele compacta imagens coloridas e com baixa profundidade de bits significativamente melhor e sempre procura a solução ideal durante a compactação, em vez de depender de uma heurística .
Vejamos um exemplo usando 129 imagens (escuras, planas e claras) tiradas com uma câmera ASI1600MM. Cada imagem tem 32 MiB, totalizando 4 GiB de dados. Compactá-los com a ferramenta fpack
padrão leva cerca de 16,6 segundos e produz um tamanho total de saída de 2,2 GiB:
$ time fpack */*.fit */*/*.fit
user 14.992
system 1.592
total 16.616
$ find . -name '*.fz' -print0 | xargs -0 cat | wc -c
2369943360
No entanto, isso deixa você com arquivos *.fz
que nem todos os aplicativos podem realmente ler.
Usando DwarFS, aqui está o que obtemos:
$ mkdwarfs -i ASI1600 -o asi1600-20.dwarfs -S 20 --categorize
I 08:47:47.459077 scanning "ASI1600"
I 08:47:47.491492 assigning directory and link inodes...
I 08:47:47.491560 waiting for background scanners...
I 08:47:47.675241 scanning CPU time: 1.051s
I 08:47:47.675271 finalizing file inodes...
I 08:47:47.675330 saved 0 B / 3.941 GiB in 0/258 duplicate files
I 08:47:47.675360 assigning device inodes...
I 08:47:47.675371 assigning pipe/socket inodes...
I 08:47:47.675381 building metadata...
I 08:47:47.675393 building blocks...
I 08:47:47.675398 saving names and symlinks...
I 08:47:47.675514 updating name and link indices...
I 08:47:47.675796 waiting for segmenting/blockifying to finish...
I 08:47:50.274285 total ordering CPU time: 616.3us
I 08:47:50.274329 total segmenting CPU time: 1.132s
I 08:47:50.279476 saving chunks...
I 08:47:50.279622 saving directories...
I 08:47:50.279674 saving shared files table...
I 08:47:50.280745 saving names table... [1.047ms]
I 08:47:50.280768 saving symlinks table... [743ns]
I 08:47:50.282031 waiting for compression to finish...
I 08:47:50.823924 compressed 3.941 GiB to 1.201 GiB (ratio=0.304825)
I 08:47:50.824280 compression CPU time: 17.92s
I 08:47:50.824316 filesystem created without errors [3.366s]
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯
waiting for block compression to finish
5 dirs, 0/0 soft/hard links, 258/258 files, 0 other
original size: 3.941 GiB, hashed: 315.4 KiB (18 files, 0 B/s)
scanned: 3.941 GiB (258 files, 117.1 GiB/s), categorizing: 0 B/s
saved by deduplication: 0 B (0 files), saved by segmenting: 0 B
filesystem: 3.941 GiB in 4037 blocks (4550 chunks, 516/516 fragments, 258 inodes)
compressed filesystem: 4037 blocks/1.201 GiB written
Em menos de 3,4 segundos, ele compacta os dados para 1,2 GiB, quase metade do tamanho da saída fpack
.
Além de economizar muito espaço em disco, isso também pode ser útil quando seus dados são armazenados em um NAS. Aqui está uma comparação do mesmo conjunto de dados acessados através de uma conexão de rede de 1 Gb/s, primeiro usando os dados brutos descompactados:
find /mnt/ASI1600 -name '*.fit' -print0 | xargs -0 -P4 -n1 cat | dd of=/dev/null status=progress
4229012160 bytes (4.2 GB, 3.9 GiB) copied, 36.0455 s, 117 MB/s
E a seguir, usando uma imagem DwarFS no mesmo compartilhamento:
$ dwarfs /mnt/asi1600-20.dwarfs mnt
$ find mnt -name '*.fit' -print0 | xargs -0 -P4 -n1 cat | dd of=/dev/null status=progress
4229012160 bytes (4.2 GB, 3.9 GiB) copied, 14.3681 s, 294 MB/s
Isso é cerca de 2,5 vezes mais rápido. É muito provável que você veja resultados semelhantes com discos rígidos externos lentos.
Atualmente, o DwarFS não possui capacidade integrada para adicionar informações de recuperação a uma imagem do sistema de arquivos. No entanto, para fins de arquivamento, é uma boa ideia ter essas informações de recuperação para poder reparar uma imagem danificada.
Felizmente, isso é relativamente simples usando algo como par2cmdline:
$ par2create -n1 asi1600-20.dwarfs
Isso criará dois arquivos adicionais que você pode colocar ao lado da imagem (ou em um armazenamento diferente), pois você só precisará deles se o DwarFS detectar um problema com a imagem do sistema de arquivos. Se houver um problema, você pode executar
$ par2repair asi1600-20.dwarfs
que muito provavelmente será capaz de recuperar a imagem se menos de 5% (esse é o padrão usado por par2create
) da imagem estiver danificada.
Atributos estendidos não são suportados atualmente. Quaisquer atributos estendidos armazenados no sistema de arquivos de origem não serão preservados atualmente ao construir uma imagem DwarFS usando mkdwarfs
.
Dito isto, o inode raiz de uma imagem DwarFS montada atualmente expõe um ou dois atributos estendidos no Linux:
$ attr -l mnt
Attribute "dwarfs.driver.pid" has a 4 byte value for mnt
Attribute "dwarfs.driver.perfmon" has a 4849 byte value for mnt
O dwarfs.driver.pid
contém simplesmente o PID do driver DwarFS FUSE. O dwarfs.driver.perfmon
contém os resultados atuais do monitor de desempenho.
Além disso, cada arquivo regular expõe um dwarfs.inodeinfo
com informações sobre o inode subjacente:
$ attr -l "05 Disappear.caf"
Attribute "dwarfs.inodeinfo" has a 448 byte value for 05 Disappear.caf
O atributo contém um objeto JSON com informações sobre o inode subjacente:
$ attr -qg dwarfs.inodeinfo "05 Disappear.caf"
{
"chunks": [
{
"block": 2,
"category": "pcmaudio/metadata",
"offset": 270976,
"size": 4096
},
{
"block": 414,
"category": "pcmaudio/waveform",
"offset": 37594368,
"size": 29514492
},
{
"block": 419,
"category": "pcmaudio/waveform",
"offset": 0,
"size": 29385468
}
],
"gid": 100,
"mode": 33188,
"modestring": "----rw-r--r--",
"uid": 1000
}
Isso é útil, por exemplo, para verificar como um arquivo específico está distribuído em vários blocos ou quais categorias foram atribuídas ao arquivo.
Os testes SquashFS, xz
, lrzip
, zpaq
e wimlib
foram todos feitos em uma CPU Intel(R) Xeon(R) E-2286M de 8 núcleos a 2,40 GHz com 64 GiB de RAM.
Os testes do Cromfs foram feitos com uma versão mais antiga do DwarFS em uma CPU Intel(R) Xeon(R) D-1528 de 6 núcleos a 1,90 GHz com 64 GiB de RAM.
Os testes EROFS foram feitos usando DwarFS v0.9.8 e EROFS v1.7.1 em um Intel(R) Core(TM) i9-13900K com 64 GiB de RAM.
Os sistemas ficaram praticamente ociosos durante todos os testes.
O diretório de origem continha 1.139 instalações Perl diferentes de 284 versões distintas, um total de 47,65 GiB de dados em 1.927.501 arquivos e 330.733 diretórios. O diretório de origem foi recentemente descompactado de um arquivo tar para uma partição XFS em uma unidade NVME 970 EVO Plus de 2 TB, portanto, a maior parte de seu conteúdo provavelmente foi armazenada em cache.
Estou usando o mesmo tipo e nível de compactação para SquashFS que é a configuração padrão para DwarFS:
$ time mksquashfs install perl-install.squashfs -comp zstd -Xcompression-level 22
Parallel mksquashfs: Using 16 processors
Creating 4.0 filesystem on perl-install-zstd.squashfs, block size 131072.
[=========================================================/] 2107401/2107401 100%
Exportable Squashfs 4.0 filesystem, zstd compressed, data block size 131072
compressed data, compressed metadata, compressed fragments,
compressed xattrs, compressed ids
duplicates are removed
Filesystem size 4637597.63 Kbytes (4528.90 Mbytes)
9.29% of uncompressed filesystem size (49922299.04 Kbytes)
Inode table size 19100802 bytes (18653.13 Kbytes)
26.06% of uncompressed inode table size (73307702 bytes)
Directory table size 19128340 bytes (18680.02 Kbytes)
46.28% of uncompressed directory table size (41335540 bytes)
Number of duplicate files found 1780387
Number of inodes 2255794
Number of files 1925061
Number of fragments 28713
Number of symbolic links 0
Number of device nodes 0
Number of fifo nodes 0
Number of socket nodes 0
Number of directories 330733
Number of ids (unique uids + gids) 2
Number of uids 1
mhx (1000)
Number of gids 1
users (100)
real 32m54.713s
user 501m46.382s
sys 0m58.528s
Para o DwarFS, mantenho os padrões:
$ time mkdwarfs -i install -o perl-install.dwarfs
I 11:33:33.310931 scanning install
I 11:33:39.026712 waiting for background scanners...
I 11:33:50.681305 assigning directory and link inodes...
I 11:33:50.888441 finding duplicate files...
I 11:34:01.120800 saved 28.2 GiB / 47.65 GiB in 1782826/1927501 duplicate files
I 11:34:01.122608 waiting for inode scanners...
I 11:34:12.839065 assigning device inodes...
I 11:34:12.875520 assigning pipe/socket inodes...
I 11:34:12.910431 building metadata...
I 11:34:12.910524 building blocks...
I 11:34:12.910594 saving names and links...
I 11:34:12.910691 bloom filter size: 32 KiB
I 11:34:12.910760 ordering 144675 inodes using nilsimsa similarity...
I 11:34:12.915555 nilsimsa: depth=20000 (1000), limit=255
I 11:34:13.052525 updating name and link indices...
I 11:34:13.276233 pre-sorted index (660176 name, 366179 path lookups) [360.6ms]
I 11:35:44.039375 144675 inodes ordered [91.13s]
I 11:35:44.041427 waiting for segmenting/blockifying to finish...
I 11:37:38.823902 bloom filter reject rate: 96.017% (TPR=0.244%, lookups=4740563665)
I 11:37:38.823963 segmentation matches: good=454708, bad=6819, total=464247
I 11:37:38.824005 segmentation collisions: L1=0.008%, L2=0.000% [2233254 hashes]
I 11:37:38.824038 saving chunks...
I 11:37:38.860939 saving directories...
I 11:37:41.318747 waiting for compression to finish...
I 11:38:56.046809 compressed 47.65 GiB to 430.9 MiB (ratio=0.00883101)
I 11:38:56.304922 filesystem created without errors [323s]
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯
waiting for block compression to finish
330733 dirs, 0/2440 soft/hard links, 1927501/1927501 files, 0 other
original size: 47.65 GiB, dedupe: 28.2 GiB (1782826 files), segment: 15.19 GiB
filesystem: 4.261 GiB in 273 blocks (319178 chunks, 144675/144675 inodes)
compressed filesystem: 273 blocks/430.9 MiB written [depth: 20000]
█████████████████████████████████████████████████████████████████████████████▏100% |
real 5m23.030s
user 78m7.554s
sys 1m47.968s
Portanto, nesta comparação, mkdwarfs
é mais de 6 vezes mais rápido que mksquashfs
, tanto em termos de tempo de CPU quanto de tempo de relógio.
$ ll perl-install.*fs
-rw-r--r-- 1 mhx users 447230618 Mar 3 20:28 perl-install.dwarfs
-rw-r--r-- 1 mhx users 4748902400 Mar 3 20:10 perl-install.squashfs
Em termos de taxa de compactação, o sistema de arquivos DwarFS é 10 vezes menor que o sistema de arquivos SquashFS . Com o DwarFS, o conteúdo foi compactado para menos de 0,9% (!) do seu tamanho original . Essa taxa de compactação considera apenas os dados armazenados nos arquivos individuais, e não o espaço real em disco usado. No sistema de arquivos XFS original, de acordo com du
, a pasta de origem usa 52 GiB, então a imagem DwarFS na verdade usa apenas 0,8% do espaço original .
Aqui está outra comparação usando a compactação lzma
em vez de zstd
:
$ time mksquashfs install perl-install-lzma.squashfs -comp lzma
real 13m42.825s
user 205m40.851s
sys 3m29.088s
$ time mkdwarfs -i install -o perl-install-lzma.dwarfs -l9
real 3m43.937s
user 49m45.295s
sys 1m44.550s
$ ll perl-install-lzma.*fs
-rw-r--r-- 1 mhx users 315482627 Mar 3 21:23 perl-install-lzma.dwarfs
-rw-r--r-- 1 mhx users 3838406656 Mar 3 20:50 perl-install-lzma.squashfs
É imediatamente óbvio que as execuções são significativamente mais rápidas e as imagens resultantes são significativamente menores. Ainda assim, mkdwarfs
é cerca de 4 vezes mais rápido e produz uma imagem 12 vezes menor que a imagem do SquashFS. A imagem DwarFS tem apenas 0,6% do tamanho do arquivo original.
Então, por que não usar lzma
em vez de zstd
por padrão? A razão é que lzma
é uma ordem de magnitude mais lenta para descompactar do que zstd
. Se você estiver acessando dados em seu sistema de arquivos compactado apenas ocasionalmente, isso pode não ser um grande problema, mas se você usá-lo extensivamente, zstd
resultará em melhor desempenho.
As comparações acima não são totalmente justas. mksquashfs
por padrão usa um tamanho de bloco de 128 KB, enquanto mkdwarfs
usa blocos de 16 MiB por padrão, ou mesmo blocos de 64 MiB com -l9
. Ao usar tamanhos de bloco idênticos para ambos os sistemas de arquivos, a diferença, como era de se esperar, torna-se muito menos dramática:
$ time mksquashfs install perl-install-lzma-1M.squashfs -comp lzma -b 1M
real 15m43.319s
user 139m24.533s
sys 0m45.132s
$ time mkdwarfs -i install -o perl-install-lzma-1M.dwarfs -l9 -S20 -B3
real 4m25.973s
user 52m15.100s
sys 7m41.889s
$ ll perl-install*.*fs
-rw-r--r-- 1 mhx users 935953866 Mar 13 12:12 perl-install-lzma-1M.dwarfs
-rw-r--r-- 1 mhx users 3407474688 Mar 3 21:54 perl-install-lzma-1M.squashfs
Mesmo isso ainda não é totalmente justo, pois usa um recurso ( -B3
) que permite ao DwarFS fazer referência a pedaços de arquivos de até dois blocos anteriores do sistema de arquivos.
Mas a questão é que é aqui que o SquashFS se destaca, já que não suporta tamanhos de bloco maiores ou referência inversa. E como você verá abaixo, os blocos maiores que o DwarFS usa por padrão não afetam necessariamente negativamente o desempenho.
O DwarFS também oferece uma opção para recompactar um sistema de arquivos existente com um algoritmo de compactação diferente. Isto pode ser útil porque permite a experimentação relativamente rápida com diferentes algoritmos e opções sem exigir uma reconstrução completa do sistema de arquivos. Por exemplo, recompactando o sistema de arquivos acima com a melhor compactação possível ( -l 9
):
$ time mkdwarfs --recompress -i perl-install.dwarfs -o perl-lzma-re.dwarfs -l9
I 20:28:03.246534 filesystem rewrittenwithout errors [148.3s]
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯
filesystem: 4.261 GiB in 273 blocks (0 chunks, 0 inodes)
compressed filesystem: 273/273 blocks/372.7 MiB written
████████████████████████████████████████████████████████████████████▏100%
real 2m28.279s
user 37m8.825s
sys 0m43.256s