Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

English

A pirâmide

Um ano de leituras uma por segundo são 31,5 milhões de pontos. Uma tela tem uns 2.000 pixels de largura. Ler os 31,5 milhões de pontos para desenhar 2.000 colunas, sessenta vezes por segundo enquanto a pessoa arrasta, é lento demais — e mandá-los pela rede, mais ainda. A pirâmide é como o Nonius desenha esse ano a partir de alguns milhares de números, e ainda desenha exatamente a linha que todos os pontos desenhariam.

Um resumo: o bucket

Um bucket resume os pontos de um trecho de tempo em oito números:

CampoO que guarda
first_t, first_vo tempo e o valor do primeiro ponto
last_t, last_vo tempo e o valor do último ponto
min, maxo menor e o maior valor
countquantos pontos
weighta soma dos pesos

Dois buckets vizinhos se juntam num só: o primeiro do primeiro, o último do segundo, o menor dos dois mínimos, o maior dos dois máximos, as contagens e os pesos somados. Assim um bucket de um minuto sai de quatro de quinze segundos, sem voltar aos pontos.

Níveis

A pirâmide é feita de níveis de buckets. Os buckets do nível 0 têm a largura da base — digamos, um segundo. Cada nível acima é quatro vezes mais largo: 4 s, 16 s, 64 s, e assim por diante. Os buckets são alinhados a 1970, então todo cliente e todo servidor os cortam nos mesmos lugares. Um trecho sem pontos não tem bucket.

NívelLargura do bucket, base 1 s
01 s
14 s
216 s
44 min 16 s
818 h 12 min

O que uma vista lê

Uma vista é um intervalo de tempo sobre um número de colunas de pixels. Cada coluna cobre um trecho de tempo; o gráfico escolhe o nível mais grosso cujos buckets cabem pelo menos 16 numa coluna, e os junta num resumo por coluna. Quando nem o nível 0 serve — menos de 16 larguras de base por coluna, no fundo de um zoom —, ele lê os pontos crus.

Então um quadro lê entre 16 e 64 buckets por coluna, alguns milhares de números para a tela inteira, seja qual for o tamanho do histórico. E a linha é exata: uma coluna desenhada do menor ao maior valor, ligada à seguinte do seu último valor ao primeiro da próxima, é a mesma, pixel por pixel, que a linha desenhada com todos os pontos. A pirâmide muda quanto se lê, nunca o que se desenha.

Escolher a base e os níveis

Dois números, definidos onde a pirâmide é construída:

  • A base: mais ou menos o espaçamento dos seus pontos onde eles são mais densos. Uma leitura por segundo, base de um segundo. Pontos em rajadas — mil por segundo durante dez segundos a cada hora — usam o espaçamento dentro da rajada, um milissegundo. Uma base muito mais fina que os pontos custa memória (64 bytes por bucket); muito mais grossa, e um zoom profundo lê mais pontos crus por coluna do que precisa.

  • Os níveis: o suficiente para a sua vista mais larga. Uma vista lê no máximo 64 buckets por coluna enquanto o nível do topo for fino o bastante; depois disso, os buckets do topo se acumulam. Use níveis suficientes para que

    base × 4^(níveis − 1) ≥ intervalo mais largo ÷ (64 × largura do gráfico)

    com a largura do gráfico em pixels do dispositivo (pixels CSS × a razão de pixels). Use pelo menos 2, mesmo quando a regra dá 1: o servidor sempre mantém o nível do topo, então um nível 0 sozinho, com um ponto por bucket, fica inteiro (O que custa). Três dias de leituras a cada 10 segundos, base de 10 s, custam 88 bytes por ponto num arquivo de pirâmide com 1 nível e 40 com 2. Níveis a mais custam pouco — 44 bytes com 3, 46 com 9 —, então na dúvida use mais.

Exemplo: pontos a um segundo de distância, um ano mostrado em 2.000 pixels. O intervalo mais largo é 31.536.000 s; dividido por 64 × 2.000, dá uns 246 s. Com base de 1 s, o nível do topo precisa ter pelo menos 246 s: 4⁴ = 256 s é o nível 4, então 5 níveis.

Nenhum dos dois números é uma promessa que você não pode mudar: um arquivo de série guarda os pontos, não uma pirâmide, que é construída a partir deles quando o gráfico ou o servidor carrega o arquivo. (O arquivo de pirâmide de um servidor guarda uma já construída, e é construído de novo para mudá-la.)

Onde eles são definidos

OndeComoPadrão
NavegadorcreateChart(canvas, { baseNs, levels })1 s, 9 níveis
.NETnew Series(exponent, baseNs, levels)1 s, 9 níveis
nonius-server--base-ns N --levels L1 ms, 12 níveis
RustPyramid::new(base, levels)—

Os padrões cobrem anos de pontos a um segundo de distância. A base de um milissegundo do nonius-server serve para dados rápidos; para um sensor que lê a cada poucos segundos, passe --base-ns. Um gráfico alimentado por tiles usa a pirâmide do servidor, dos seus metadados: os ajustes do próprio gráfico são para pontos que você entrega.

No Rust, construir uma pirâmide são duas chamadas:

#![allow(unused)]
fn main() {
// Summaries from one millisecond up, eight levels.
let mut pyramid = Pyramid::new(Width::new(1_000_000)?, 8)?;
pyramid.extend_sorted(series.iter())?;
}

O que custa

Um bucket tem 64 bytes. O nível 0 de uma base de um segundo sobre um ano de pontos a cada segundo tem 31,5 milhões deles — tantos quanto os pontos, com quase três vezes o tamanho. Esse nível não rende nada: um bucket de um ponto é o ponto. Por isso o nonius-server libera um nível cujos buckets têm em média menos de 4 pontos (--sparse 4, o padrão) — qualquer nível menos o do topo, que ele sempre mantém — e responde uma vista nessa profundidade com os próprios pontos. Cada nível acima tem um quarto dos buckets do de baixo, então o resto da pirâmide junto custa mais ou menos um terço do primeiro nível mantido.

No exemplo de Servir, isso dá: 24 bytes por ponto para os próprios pontos, 16 para o nível 1 (um bucket de 64 bytes a cada 4 pontos), e uns 5 para os níveis acima — 45 bytes por ponto.

Servir mostra um servidor construindo uma pirâmide e dizendo o que manteve.