Smart2Raw
Início Aplicações Como funciona Desempenho Comece

Comercial

Premium Licenciamento Investimento

Mais

Escopo técnico Citar Sobre
Fale conosco

Onde está a troca.

Escopo técnico

Esta página existe para o engenheiro que vai testar a biblioteca com o dado dele e quer saber, de antemão, exatamente o que está escolhendo. Nada aqui contradiz o resto do site — é a mesma decisão de projeto dita pelo outro lado.

Aqui está a única troca, dita sem rodeio e com o número que a mede — e os limites que vale declarar antes de você descobrir sozinho.

8 bitsa menor classe nativa. A troca inteira sai daqui, e ela é decisão, não descuido
1,91 MBo dicionário, contra os 3,82 MB do Smart2Raw: onde ele ganha, e é um fator de dois
41,01 MBo mesmo dicionário, contra uma linha de base de 30,52 MB: onde ele aumenta a coluna
7,9 msde materialização que o dicionário paga em cada varredura e o Smart2Raw não paga

O que ele é

Um classificador, não um compressor. Ele mede a amplitude real de uma coluna de inteiros, guarda na menor classe nativa que essa amplitude exige — 8, 16, 32 ou 64 bits, com ou sem sinal — e depois deixa os bytes em paz, para que toda operação rode direto em cima deles.

Daí saem três formas: o pool plano, a forma afim (v = base + passo·i, com o passo achado por gcd) e a forma em blocos (cada bloco relativo ao próprio mínimo, com metadados que respondem consultas sem tocar no payload). O s2r_recommend() mede as três; o s2r_blocked_plan() precifica todos os tamanhos de bloco candidatos a partir de uma única passagem.

A única troca, dita sem rodeio

A menor classe nativa tem 8 bits. Não há classe de 4 bits, nem de 3, nem empacotamento de bits. Então numa coluna com poucos valores distintos espalhados por uma amplitude larga — digamos 12 valores distintos — um dicionário com códigos de 4 bits guarda menos. Medido em 4 milhões de elementos: dicionário 1,91 MB contra 3,82 MB. É um fator de dois, e é real.

E o custo não é só de espaço. Nesse mesmo regime o par também responde mais rápido: 0,326 ms contra 0,468 ms, 1,44× do lado dele. Dizer que a troca custa bytes e parar aí seria contar metade — quando a amplitude é estreita o bastante para códigos de 4 bits caberem, o dicionário ganha nos dois eixos, e é honesto saber disso antes de escolher.

Essa ausência é a decisão, não um descuido. Códigos de menos de um byte são o que obriga a existir um passo de decodificação, e o passo de decodificação é o que a abordagem inteira existe para eliminar. Manter a classe em 8 bits é o que faz os bytes guardados serem um vetor que o processador já sabe ler: sem materialização, sem dicionário residente na memória, sem indireção por valor. No mesmo benchmark, materializar a coluna de dicionário custa 7,9 ms que o Smart2Raw simplesmente não paga.

Onde a amplitude é larga — que é a maior parte do dado real — a comparação se inverte, e quem cresce é o dicionário: 41,01 MB contra uma linha de base de 30,52 MB numa coluna de alta cardinalidade.

O que ele não vai fazer com o seu dado

Versões e compatibilidade de arquivo

versãoDOInota
3.5.110.5281/zenodo.21676456atual — correção de segurança no leitor em blocos
3.5.010.5281/zenodo.21623772um .s2r hostil podia escrever fora do heap — atualize
3.4.010.5281/zenodo.21614309corrupção silenciosa em coluna sem sinal acima de 2^63 — atualize
todas10.5281/zenodo.20477234DOI do projeto

Uma coluna sem passo comum é escrita pela 3.5.x byte a byte como a 3.4.0 escrevia, e abre na 3.4.0. Uma coluna com passo é fmt = 3, e a 3.4.0 a recusa corretamente em vez de ler errado. As duas direções foram medidas, não supostas. A 3.5.1 não mexe em nenhum byte do formato: ela só passou a recusar arquivos que a 3.5.0 aceitava e não devia.

O defeito da 3.5.0, e o que ele exigia

O leitor da forma em blocos dimensionava o corpo que ia ler como nblocks × metadados + bytes. Os dois termos vêm do disco, e a soma era feita em size_t puro. Um arquivo que declara nblocks = 2^22 e um bytes perto de 2^64 faz a soma dar a volta e virar 16: o leitor aloca dezesseis bytes e copia quatro megabytes para dentro.

Esse arquivo tem 64 bytes e passa por todas as validações que já existiam — magia, fmt, as quatro classes, nblocks == teto(count/block), todos os campos dentro do limite, CRC32 correto sobre o corpo real e EOF exato. Não há nada malformado nele. Corrupção acidental quebra o CRC; corrupção proposital vem com o CRC certo, e era exatamente essa diferença que faltava enxergar.

Quem era afetado: só quem chama s2r_blocked_load() sobre um arquivo que não escreveu. Escrever nunca foi afetado — ali os dois termos descrevem uma estrutura que já existe na memória. E o pool plano nunca foi afetado: ele já fazia count > SIZE_MAX/eb desde a 3.3. Foi um único lugar da família de leitores que ficou sem a guarda que as irmãs dele tinham.

A 3.5.1 fecha com duas travas: a conta passa a ser feita em aritmética checada, e o corpo declarado precisa caber no arquivo. O arquivo hostil virou teste fixo — contra o cabeçalho da 3.5.0 ele aborta sob AddressSanitizer; contra o da 3.5.1 ele passa, junto com um arquivo honesto que continua carregando e somando certo, porque uma guarda que também recusa dado real não é conserto.

Limites que vale declarar

A versão avançada

O que está publicado aqui é a versão aberta, sob AGPL-3.0-or-later. Existe uma versão mais avançada que não é publicada — veja o Smart2Raw Premium, alcançado pela licença comercial.

Por onde seguir

Confira você mesmo

Todo número desta página sai de um programa do repositório.

Desempenho →

Onde a aberta para de propósito

É exatamente ali que a versão licenciada começa.

Smart2Raw Premium →

Teste com o seu dado

A demonstração roda a biblioteca de verdade, no seu navegador.

Ir para a demonstração →