A Pilha TCP/IP e a Física da Rede

por Frank de Alcantara em 07/08/2026

A Pilha TCP/IP e a Física da Rede

Nesta série, a curiosa leitora vai reconstruir a disciplina de redes a partir do ponto de vista de quem escreve software, e não de quem passa cabo. Isso não significa uma versão diluída. Significa uma versão em que cada mecanismo aparece junto com a decisão de arquitetura que ele condiciona, e em que cada afirmação vem com o número que a sustenta.

Índice da Série: Redes para Engenharia de Software

  • 1. A Pilha TCP/IP e a Física da Rede (Você está aqui)

Começamos pela parte que não é abstraível.

Existe uma classe de defeito que nenhum profiler de aplicação encontra, porque a aplicação não está lenta: ela está esperando. O perfil mostra três microssegundos de CPU e duzentos milissegundos de relógio, e a diferença não está em lugar nenhum do código. Está no fio, na fila de um comutador e na distância entre São Paulo e a Virgínia do Norte. A engenheira que não sabe decompor essa diferença vai passar a carreira otimizando o que já era rápido.

O combinado desta série é o mesmo das séries Transformers, Inteligência Artificial Aplicada e Estatística Orientada à Ciência de Dados: os exemplos de código são em C++23, completos e compiláveis, e a leitora refaz na linguagem que preferir. Os laboratórios da disciplina rodam no navegador, sem instalar nada, na suíte NetworkLabs SE.

Há ainda um combinado novo, específico desta série, e ele merece um parágrafo próprio.

A turma pode, e deve, usar ferramentas de inteligência artificial nos trabalhos. Isso não é uma concessão administrativa; é um fato de engenharia que muda o que precisa ser ensinado. Um modelo de linguagem escreve um manifesto de infraestrutura de duzentas linhas em quatro segundos, com nomes coerentes e indentação impecável, e escreve com a mesma serenidade a versão que anuncia uma sub-rede pública onde deveria haver uma privada. Produzir ficou barato. Conferir não ficou. Todo artigo desta série termina com uma seção de estrutura fixa sobre o que delegar à máquina, qual é a alucinação característica daquele assunto e como construir o verificador que a pega. É o fio que atravessa os dezesseis artigos, e é a razão pela qual isto não é apenas mais um curso de redes.

Se estiver de acordo, continuamos. Se não, obrigado, e nos vemos na próxima.

1. Duas pilhas e uma decisão de engenharia

Todo curso de redes começa desenhando sete camadas empilhadas, e boa parte dos alunos sai achando que a Internet tem sete camadas. Ela não tem. O modelo OSI, de Open Systems Interconnection (interconexão de sistemas abertos), foi publicado pela ISO em 1984 como referência conceitual, e nasceu de um esforço de padronização que competia com a pilha que já estava rodando. A pilha que estava rodando venceu. O modelo perdedor sobreviveu como vocabulário, e é por isso que engenheiros ainda dizem “problema de camada 3” ou “balanceador de camada 7” sem nenhum constrangimento.

O modelo que efetivamente roda é o TCP/IP, descrito na RFC 1122 em quatro camadas: enlace, Internet, transporte e aplicação. A correspondência com o OSI é aproximada, e a aproximação vaza exatamente onde importa. As camadas de sessão e apresentação do OSI, a quinta e a sexta, não têm implementação separada na Internet: o que elas descrevem virou responsabilidade da aplicação, ou de bibliotecas dentro dela. Quando alguém diz que TLS é “camada 6”, está fazendo uma analogia útil e imprecisa, porque TLS roda sobre TCP e é falado pela aplicação.

A Tabela 1 registra a correspondência que a série usará daqui em diante, com um exemplo por linha para que ela não fique abstrata.

OSI TCP/IP O que decide Exemplo
7 aplicação, 6 apresentação, 5 sessão aplicação o significado dos bytes trocados HTTP, DNS, gRPC, MQTT
4 transporte transporte se há entrega ordenada e confiável, e para qual processo TCP, UDP, QUIC
3 rede Internet qual caminho o pacote toma entre redes IP, ICMP, BGP
2 enlace, 1 física enlace como o bit atravessa um meio compartilhado Ethernet, Wi-Fi, fibra

Tabela 1: correspondência aproximada entre OSI e TCP/IP. As camadas 5 e 6 do OSI não têm implementação separada na pilha real, e é aí que a analogia deixa de valer.

A pergunta que interessa a quem escreve software não é quantas camadas existem. É por que a divisão está exatamente aí, e não em outro lugar? A resposta está em um artigo de 1984 de Saltzer, Reed e Clark que continua sendo a coisa mais útil já escrita sobre arquitetura de sistemas distribuídos: o argumento fim a fim.

O argumento diz o seguinte. Uma função que precisa de garantia completa, como entregar um arquivo sem corrupção, só pode ser implementada corretamente nos extremos da comunicação, com conhecimento da aplicação. Implementá-la também nas camadas baixas pode ser justificável por desempenho, nunca por correção. Se o disco do remetente corromper o arquivo antes de enviá-lo, nenhuma soma de verificação de enlace salvará ninguém. A verificação que conta é a que compara o arquivo lido no destino com o que o remetente pretendia enviar.

A consequência prática é uma rede burra no meio e inteligente nas pontas. O IP não promete entrega, não promete ordem e não promete integridade; ele promete tentativa. Essa pobreza deliberada é o que permitiu que a mesma camada 3 sobrevivesse à passagem de enlaces de nove mil bits por segundo para enlaces de quatrocentos gigabits por segundo, e de aplicações de terminal remoto para videoconferência. Uma camada que promete pouco é difícil de tornar obsoleta.

A atenta leitora deve guardar essa ideia, porque ela reaparece disfarçada em todos os artigos seguintes. Quando o Artigo 12 mostrar um service mesh interceptando cada requisição para acrescentar repetição, tempo limite e telemetria, valerá perguntar se a função foi colocada no lugar certo, ou se estamos reimplementando no meio o que só a ponta pode garantir.

Há, porém, um limite que o argumento fim a fim não remove, e é dele que trata o resto deste artigo. Nenhuma decisão de camada altera a velocidade da luz, o tamanho de uma fila ou o número de bits que cabem em um microssegundo de fio. Essas três coisas formam o piso sobre o qual todo o resto é construído, e a próxima seção começa medindo a primeira delas.

2. Encapsulamento e o custo de cada cabeçalho

Cada camada embrulha o que recebe da camada de cima. A aplicação entrega um payload, o transporte acrescenta um cabeçalho, a camada de Internet acrescenta outro, o enlace acrescenta o seu e ainda um encerramento. O resultado atravessa o fio como uma boneca russa, e é desmontado na ordem inversa no destino.

Precisamos de um número antes de prosseguir. A MTU, de Maximum Transmission Unit (unidade máxima de transmissão), é o maior tamanho de carga útil que uma camada de enlace aceita transportar em um único quadro. Em Ethernet, o valor padrão é de $1500$ bytes. Esse número não veio de nenhum princípio: veio do compromisso, tomado nos anos 1970, entre o custo de retransmitir um quadro grande corrompido e o custo de cabeçalho de quadros pequenos. Ele sobreviveu por inércia e por compatibilidade, e hoje é uma constante da profissão.

Antes de qualquer conta, é preciso dizer o que estamos contando, porque a mesma pergunta admite duas respostas diferentes conforme a fronteira escolhida. Definamos a eficiência de quadro como a fração dos bytes que atravessam o fio carregando dado útil da aplicação:

\[\eta = \frac{b_{\text{útil}}}{b_{\text{útil}} + b_{\text{env}}},\]

na qual $b_{\text{útil}}$ é o número de bytes de payload da aplicação e $b_{\text{env}}$ é o envelope, isto é, a soma dos bytes acrescentados por todas as camadas. Para TCP sobre IPv4 sobre Ethernet, sem opções em nenhum dos três, o envelope tem tamanho fixo:

\[b_{\text{env}} = \underbrace{20}_{\text{IPv4}} + \underbrace{20}_{\text{TCP}} + \underbrace{18}_{\text{Ethernet}} = 58\ \text{bytes},\]

nos quais os $18$ bytes de Ethernet são $14$ de cabeçalho, com endereços de origem e destino e tipo, mais $4$ de sequência de verificação. O preâmbulo de $8$ bytes e o intervalo entre quadros equivalente a $12$ bytes não pertencem a camada nenhuma, mas ocupam o fio; eles entram apenas nos cálculos de taxa de linha, como o do exercício 4.

Com uma MTU de $1500$ bytes, o maior payload de aplicação é $1500 - 40 = 1460$ bytes. Esse valor tem nome próprio, MSS, de Maximum Segment Size (tamanho máximo de segmento). O quadro correspondente ocupa $1460 + 58 = 1518$ bytes no fio, e a eficiência é

\[\eta_{\text{cheio}} = \frac{1460}{1518} = 0{,}9618 = 96{,}18\%.\]

Um resultado confortável, que produz a impressão errada de que cabeçalho é detalhe. Registro aqui, porque a leitora vai encontrar o número em toda parte, que a razão $1460/1500 = 97{,}33\%$ também circula como “eficiência”: ela mede a fração da MTU, ignorando o quadro Ethernet. As duas respostas estão certas para perguntas diferentes, e misturá-las na mesma tabela é uma forma barata de errar por um ponto percentual sem saber onde.

Refaçamos a conta para um payload de $64$ bytes, tamanho típico de uma mensagem de telemetria ou de um reconhecimento de aplicação. O envelope não mudou, porque ele não depende do que carrega:

\[\eta_{64} = \frac{64}{64 + 58} = \frac{64}{122} = 0{,}5246 = 52{,}46\%.\]

Quase metade do fio transporta metadados. Os mesmos $58$ bytes que consumiam $3{,}82\%$ do quadro cheio consomem agora $47{,}54\%$. A esperta leitora percebe que a eficiência não é uma propriedade do protocolo: é uma propriedade da razão entre o tamanho da mensagem e o tamanho fixo do envelope. Protocolo nenhum é eficiente ou ineficiente em abstrato; ele é eficiente para uma distribuição de tamanhos de mensagem, e essa distribuição é uma decisão da aplicação. Guardemos isso, porque o Artigo 14 vai comparar REST, gRPC e WebSocket exatamente com essa régua, e o Artigo 15 vai mostrar protocolos desenhados por gente que contava bytes.

A Figura 1 mostra os dois regimes lado a lado, com a mesma MTU e payloads de tamanhos diferentes.

Duas barras horizontais de mesma largura representam quadros Ethernet com o mesmo envelope de 58 bytes e cargas diferentes. Na barra superior, um payload de 1460 bytes ocupa 96,18 por cento dos 1518 bytes do quadro, e as faixas de Ethernet, IP e TCP aparecem como uma tira estreita à esquerda. Na barra inferior, um payload de 64 bytes ocupa 52,46 por cento dos 122 bytes do quadro, e as mesmas três faixas ocupam 47,54 por cento da barra. Figura 1: Os mesmos 58 bytes de envelope consomem 3,82% de um quadro cheio e 47,54% de um quadro com payload de 64 bytes. A eficiência não é propriedade do protocolo, e sim da razão entre mensagem e envelope.

Há um caso em que a conta muda por decisão de projeto e não de aplicação. O cabeçalho fixo do IPv6 tem $40$ bytes, o dobro do IPv4, porque os endereços passaram de quatro para dezesseis bytes cada. Mantida a MTU de $1500$ bytes, o payload máximo cai para $1440$ e o envelope sobe para $78$ bytes, com o quadro continuando a ocupar $1518$ bytes no fio:

\[\eta_{\text{IPv6}} = \frac{1440}{1518} = 94{,}86\%,\]

uma perda de $1{,}32$ ponto percentual. É pouco, e é o preço de um espaço de endereçamento que não acaba. O Artigo 9 mostrará o que acontece quando um espaço de endereçamento acaba.

Falta o caso patológico. Quando um datagrama maior que a MTU precisa atravessar o enlace, a camada de Internet o fragmenta. Um datagrama IPv4 de $4000$ bytes carrega $20$ bytes de cabeçalho e $3980$ bytes de dados. Cada fragmento pode levar no máximo $1480$ bytes de dados, porque o deslocamento de fragmento é contado em múltiplos de oito bytes e $1480$ é o maior múltiplo de oito que cabe em $1500 - 20$. O resultado são três fragmentos, com $1480$, $1480$ e $1020$ bytes de dados, e um total de $4040$ bytes no fio contra os $4000$ originais.

Quarenta bytes de desperdício não seriam problema. O problema é outro, e é grave: a perda de um fragmento obriga o descarte dos quatro mil bytes, porque o destino não consegue remontar o datagrama. A fragmentação transforma uma perda de $1{,}5$ kilobyte em uma perda de $4$ kilobytes e multiplica a probabilidade de falha do datagrama pelo número de fragmentos. É por isso que a fragmentação é evitada na prática, e é por isso que a leitora vai encontrar, no Artigo 5 e no Artigo 11, serviços que abrem conexão normalmente e travam na primeira transferência grande, porque alguém esqueceu que um túnel reduz a MTU efetiva.

2.1 Exercícios da Seção 2

1. Calcule a eficiência de um quadro cheio com TCP sobre IPv4

Uma MTU de $1500$ bytes transporta um segmento TCP sobre IPv4 sobre Ethernet, sem opções em nenhuma camada. Qual fração dos bytes no fio carrega dado da aplicação? E qual fração da MTU?

Solução: O envelope soma $20 + 20 + 18 = 58$ bytes e o payload máximo é o MSS, $1500 - 40 = 1460$ bytes. O quadro ocupa $1518$ bytes no fio, portanto

\[\eta = \frac{1460}{1518} = 96{,}18\%, \qquad \eta_{\text{MTU}} = \frac{1460}{1500} = 97{,}33\%.\]

Os $58$ bytes de envelope consomem $3{,}82\%$ do que atravessa o fio. Este é o melhor caso possível para esta combinação de protocolos e é o número que costuma ser citado quando alguém quer argumentar que cabeçalho não importa. Repare que as duas frações diferem em $1{,}15$ ponto percentual apenas porque contam fronteiras diferentes, e que citar uma delas sem dizer qual é a origem de metade das discussões inúteis sobre eficiência de rede. O exercício 3 mostra o que acontece com o mesmo argumento em outro regime.

2. Refaça o cálculo para IPv6

O cabeçalho fixo do IPv6 tem $40$ bytes. Qual a nova eficiência no fio e qual a diferença em pontos percentuais?

Solução: Com $40$ bytes de IPv6, $20$ de TCP e $18$ de Ethernet, o envelope sobe para $78$ bytes e o payload máximo cai para $1500 - 60 = 1440$ bytes. O quadro continua ocupando $1440 + 78 = 1518$ bytes no fio:

\[\eta_{\text{IPv6}} = \frac{1440}{1518} = 94{,}86\%.\]

A diferença é de $96{,}18 - 94{,}86 = 1{,}32$ ponto percentual. Cada quadro cheio ocupa no fio $1460 + 20 + 20 + 18 + 8 + 12 = 1538$ bytes, contando cabeçalhos, encerramento, preâmbulo e intervalo entre quadros, o que dá $10^{9}/(1538 \times 8) = 81\,274$ quadros por segundo em um enlace de $1$ Gbit/s saturado. Vinte bytes a mais por quadro custam, portanto, $1{,}63$ Mbyte/s gastos apenas com o cabeçalho adicional. É um custo real e é pequeno diante do problema que o IPv6 resolve, o que ilustra a diferença entre custo mensurável e custo relevante.

3. Calcule a eficiência de um payload de 64 bytes

Uma mensagem de telemetria carrega $64$ bytes de dados sobre TCP e IPv4, dentro de um quadro Ethernet cujos cabeçalho e encerramento somam $18$ bytes. Qual a eficiência?

Solução: O envelope continua com $58$ bytes, e o quadro completo tem $64 + 58 = 122$ bytes.

\[\eta = \frac{64}{122} = 0{,}5246 = 52{,}46\%.\]

Menos de $53\%$ do que atravessa o fio é dado da aplicação. Comparado ao quadro cheio do exercício 1, a eficiência caiu $96{,}18 - 52{,}46 = 43{,}72$ pontos percentuais sem que nenhum protocolo tenha mudado. Mudou apenas o tamanho da mensagem. A conclusão de engenharia é direta: agrupar mensagens pequenas em mensagens maiores é uma otimização de rede antes de ser uma otimização de aplicação, e é a razão de existirem quase todos os mecanismos de agrupamento que a leitora encontra em bibliotecas de cliente.

4. Determine a taxa de pacotes de um enlace de 10 Gbit/s com quadros mínimos

O menor quadro Ethernet tem $64$ bytes, e cada quadro no fio é precedido por $8$ bytes de preâmbulo e seguido por um intervalo entre quadros equivalente a $12$ bytes, totalizando $84$ bytes de ocupação. Quantos pacotes por segundo um enlace de $10$ Gbit/s precisa processar no pior caso, e quanto tempo há por pacote?

Solução: Cada quadro ocupa $84 \times 8 = 672$ bits do fio. A taxa é

\[\lambda_{\max} = \frac{10 \times 10^{9}}{672} = 14{,}881 \times 10^{6}\ \text{pacotes/s},\]

o clássico valor de $14{,}88$ Mpps. O orçamento por pacote é o inverso:

\[\frac{1}{14{,}881 \times 10^{6}} = 67{,}2\ \text{ns}.\]

Sessenta e sete nanossegundos. Para calibrar, um acesso à memória principal que erre todos os caches custa, em uma máquina moderna, entre $60$ e $100$ ns. Ou seja: no pior caso, o orçamento inteiro de processamento de um pacote cabe em uma única falha de cache. Esse número é o motivo de existirem DPDK, XDP e eBPF, que o Artigo 5 vai apresentar, e é o motivo de o processamento de pacotes em taxa de linha ser um problema de arquitetura de memória disfarçado de problema de rede. A leitora que acompanhou a série Transformers reconhece a estrutura: também ali o gargalo raramente era a aritmética.

5. Analise a fragmentação de um datagrama de 4000 bytes

Um datagrama IPv4 de $4000$ bytes atravessa um enlace com MTU de $1500$ bytes. Quantos fragmentos são gerados, quantos bytes trafegam no fio e qual o efeito da perda de um único fragmento?

Solução: O datagrama original tem $20$ bytes de cabeçalho e $3980$ bytes de dados. Cada fragmento carrega seu próprio cabeçalho de $20$ bytes, restando $1480$ bytes para dados, e o deslocamento de fragmento é medido em múltiplos de oito bytes, o que exige que todos os fragmentos, exceto o último, transportem um múltiplo de oito. O maior múltiplo de oito menor ou igual a $1480$ é o próprio $1480$.

Fragmento Bytes de dados Deslocamento Total no fio
1 1480 0 1500
2 1480 185 1500
3 1020 370 1040
  $3980$   $4040$

São três fragmentos e $4040$ bytes no fio, ou $40$ bytes de cabeçalho adicional, correspondentes a $1{,}0\%$ de desperdício. O desperdício é irrelevante. O que importa é a confiabilidade: se cada fragmento tem probabilidade $p$ de se perder de forma independente, o datagrama chega íntegro com probabilidade $(1-p)^{3}$. Para $p = 0{,}01$, a probabilidade de falha do datagrama sobe de $1\%$ para $1 - 0{,}99^{3} = 2{,}97\%$, quase o triplo. E a perda de qualquer um dos três obriga a retransmitir os quatro mil bytes, não apenas o fragmento perdido, porque a remontagem acontece na camada de Internet e a retransmissão acontece na de transporte, que não sabe da fragmentação. É um caso exemplar de duas camadas resolvendo problemas corretos e produzindo, juntas, um comportamento ruim.

3. Latência: os quatro somandos

Latência é a palavra que a indústria usa para tudo que demora, e é justamente por isso que ela precisa ser decomposta antes de ser usada. O tempo que um pacote leva de uma ponta a outra é a soma de quatro parcelas com origens físicas distintas:

\[T = T_{\text{prop}} + T_{\text{trans}} + T_{\text{fila}} + T_{\text{proc}},\]

nas quais $T_{\text{prop}}$ é o tempo de propagação, $T_{\text{trans}}$ é o tempo de transmissão, $T_{\text{fila}}$ é o tempo de espera em filas de saída e $T_{\text{proc}}$ é o tempo de processamento nos equipamentos do caminho. Cada uma tem uma fórmula, e as fórmulas não se parecem.

O tempo de propagação é o tempo que o sinal leva para percorrer a distância física:

\[T_{\text{prop}} = \frac{d}{v},\]

na qual $d$ é a distância percorrida pelo meio e $v$ é a velocidade de propagação nesse meio. Em fibra óptica, $v = c/n$, com $c = 299\,792{,}458$ km/s e índice de refração $n \approx 1{,}5$, o que dá $v \approx 199\,862$ km/s. A série usa o valor arredondado de $200\,000$ km/s, e diz sempre quando o arredondamento importa, o que quase nunca é o caso, porque o erro relativo é de $0{,}07\%$.

Um número para levar na cabeça: a $200\,000$ km/s, o sinal percorre $200$ km por milissegundo. Ou, na forma mais útil, cada $100$ km de fibra custa $0{,}5$ ms em uma direção e $1$ ms na ida e volta.

O tempo de transmissão é o tempo para empurrar todos os bits da mensagem para dentro do fio:

\[T_{\text{trans}} = \frac{L}{R},\]

na qual $L$ é o tamanho da mensagem em bits e $R$ é a taxa do enlace em bits por segundo. Note a diferença estrutural em relação à propagação: $T_{\text{trans}}$ depende do tamanho da mensagem e da velocidade do enlace, e não depende da distância; $T_{\text{prop}}$ depende da distância e não depende do tamanho da mensagem nem da taxa do enlace. Elas escalam de forma independente, e confundi-las é o erro que produz a frase “vamos contratar mais banda para resolver a latência”.

Vejamos o que a independência significa em números. Transmitir $1$ MiB, que são $8\,388\,608$ bits, custa

\[T_{\text{trans}} = \frac{8\,388\,608}{10^{8}} = 83{,}9\ \text{ms a }100\ \text{Mbit/s},\]

e cai para $8{,}39$ ms a $1$ Gbit/s e $0{,}839$ ms a $10$ Gbit/s. Multiplicar a taxa por cem dividiu o tempo de transmissão por cem. Enquanto isso, se o enlace tem $600$ km, $T_{\text{prop}} = 600/200\,000 = 3{,}0$ ms nos três casos. A $100$ Mbit/s a transmissão dominava com $96{,}5\%$ do total; a $10$ Gbit/s a propagação domina com $78{,}1\%$. O gargalo mudou de natureza sem que ninguém tocasse na topologia.

A Figura 2 mostra os dois regimes empilhados, com as mesmas parcelas e proporções invertidas.

Duas barras empilhadas comparam a composição do atraso de um mesmo objeto de 1 MiB em enlaces de 100 Mbit por segundo e de 10 Gbit por segundo, sobre 600 quilômetros de fibra. Na primeira barra, o tempo de transmissão de 83,9 milissegundos domina e a propagação de 3,0 milissegundos aparece como faixa estreita. Na segunda, a transmissão cai para 0,84 milissegundo e a propagação de 3,0 milissegundos passa a ser a maior parcela. Figura 2: Multiplicar a taxa do enlace por cem reduz o tempo de transmissão por cem e não reduz um único microssegundo do tempo de propagação. A partir de certa taxa, comprar banda deixa de comprar tempo.

O tempo de processamento é o gasto por cada equipamento para examinar o cabeçalho, consultar a tabela de encaminhamento e decidir a porta de saída. Em um comutador ou roteador moderno, com encaminhamento em silício, ele fica na casa de $1$ a $20$ µs por salto. Doze saltos a $10$ µs somam $0{,}12$ ms, o que, comparado aos $15$ ms de propagação de $3000$ km, representa $0{,}79\%$ do total. Em redes de longa distância, portanto, o processamento é ruído. Dentro de um centro de dados, em que a propagação some, ele passa a ser o assunto, e é aí que os números do exercício 4 da Seção 2 ganham vida.

O tempo de fila é o único dos quatro que não tem fórmula fechada a partir de parâmetros do enlace, porque depende do que os outros estão fazendo. Ele é o assunto da Seção 7, e é, de longe, o mais mal compreendido dos quatro.

Um exercício mental fecha a seção. Existe uma distância a partir da qual a propagação supera a transmissão, e essa distância depende do tamanho do objeto e da taxa do enlace. Igualando as duas parcelas,

\[\frac{d}{v} = \frac{L}{R} \quad\Longrightarrow\quad d^{*} = \frac{L\,v}{R}.\]

Para um objeto de $100$ KiB em um enlace de $1$ Gbit/s, $L = 819\,200$ bits e

\[d^{*} = \frac{819\,200 \times 200\,000}{10^{9}} = 163{,}8\ \text{km}.\]

Cento e sessenta e quatro quilômetros. Além disso, o tempo é dominado pela geografia, e a única otimização que resta é reduzir $d$, que é exatamente o que uma rede de distribuição de conteúdo faz e o que o Artigo 10 vai quantificar.

3.1 Exercícios da Seção 3

1. Calcule o tempo de propagação de um enlace de 600 quilômetros

Qual o tempo de propagação em fibra óptica para uma distância de $600$ km?

Solução: Com $v = 200\,000$ km/s,

\[T_{\text{prop}} = \frac{600}{200\,000} = 3{,}0 \times 10^{-3}\ \text{s} = 3{,}0\ \text{ms}.\]

Com o valor mais preciso $v = 199\,862$ km/s, o resultado é $3{,}002$ ms. A diferença de dois microssegundos justifica o arredondamento em todos os cálculos desta série, exceto quando a leitora estiver comparando duas rotas que diferem em menos de $1\%$, situação em que a precisão do modelo já não é o fator limitante de qualquer maneira.

2. Calcule o tempo de transmissão de 1 MiB em três taxas

Quanto tempo leva para colocar $1$ MiB no fio a $100$ Mbit/s, $1$ Gbit/s e $10$ Gbit/s?

Solução: Um mebibyte tem $1024 \times 1024 \times 8 = 8\,388\,608$ bits. Aplicando $T_{\text{trans}} = L/R$:

Taxa $T_{\text{trans}}$
$100$ Mbit/s $83{,}9$ ms
$1$ Gbit/s $8{,}39$ ms
$10$ Gbit/s $0{,}839$ ms

Observe que a série usa $\text{Mbit/s}$ como potência de dez, $10^{6}$ bits por segundo, e $\text{MiB}$ como potência de dois, $2^{20}$ bytes. As duas convenções coexistem porque taxas de enlace sempre foram contadas em potências de dez e armazenamento em potências de dois. Trocar uma pela outra introduz um erro de $4{,}86\%$ em megabytes e de $7{,}37\%$ em gigabytes, o que é suficiente para invalidar um dimensionamento e insuficiente para ser notado.

3. Determine qual parcela domina em cada regime

Para o objeto de $1$ MiB do exercício anterior, atravessando $600$ km de fibra, calcule a fração do atraso total devida à transmissão nos três regimes.

Solução: A propagação é constante em $3{,}0$ ms. Somando as duas parcelas:

Taxa $T_{\text{trans}}$ $T_{\text{prop}}$ Total Fração de $T_{\text{trans}}$
$100$ Mbit/s $83{,}9$ ms $3{,}0$ ms $86{,}9$ ms $96{,}55\%$
$1$ Gbit/s $8{,}39$ ms $3{,}0$ ms $11{,}39$ ms $73{,}66\%$
$10$ Gbit/s $0{,}84$ ms $3{,}0$ ms $3{,}84$ ms $21{,}85\%$

Passar de $100$ Mbit/s para $1$ Gbit/s cortou o atraso total de $86{,}9$ para $11{,}39$ ms, uma redução de $86{,}9\%$. Passar de $1$ para $10$ Gbit/s cortou de $11{,}39$ para $3{,}84$ ms, uma redução de $66{,}3\%$. O próximo fator de dez cortaria para $3{,}08$ ms, uma redução de apenas $19{,}7\%$. O retorno decrescente não é uma opinião sobre investimento em infraestrutura: é a consequência aritmética de somar uma parcela que encolhe a uma parcela que não encolhe.

4. Decomponha o atraso de um pacote pequeno em uma rota longa

Um pacote de $64$ bytes atravessa $3000$ km de fibra passando por doze comutadores, cada um com $10$ µs de processamento, em enlaces de $10$ Gbit/s. Decomponha o atraso.

Solução: A propagação é $3000/200\,000 = 15{,}0$ ms. O processamento é $12 \times 10 = 120$ µs $= 0{,}12$ ms. A transmissão de $64$ bytes a $10$ Gbit/s é $512/10^{10} = 51{,}2$ ns por salto; com treze transmissões, incluindo a origem, chega a $0{,}67$ µs, ou $0{,}00067$ ms. Ignorando a fila:

Parcela Valor Fração
propagação $15{,}0$ ms $99{,}21\%$
processamento $0{,}12$ ms $0{,}79\%$
transmissão $0{,}00067$ ms $0{,}004\%$

A geografia responde por $99{,}21\%$ do atraso. Um engenheiro que gaste três meses reduzindo o processamento por salto de $10$ para $5$ µs terá melhorado o atraso total em $0{,}4\%$, resultado que não sobrevive nem ao ruído da medição. É o análogo de rede daquilo que a série Transformers observa sobre kernels limitados por memória: reduzir a operação errada é afiar a faca para um duelo de pistolas.

5. Encontre a distância em que a propagação passa a dominar

A partir de que distância a propagação supera a transmissão, para um objeto de $100$ KiB em um enlace de $1$ Gbit/s? E para um objeto de $1$ KiB?

Solução: Igualando $d/v = L/R$, obtemos $d^{*} = Lv/R$. Para $100$ KiB, $L = 102\,400 \times 8 = 819\,200$ bits:

\[d^{*} = \frac{819\,200 \times 200\,000}{10^{9}} = 163{,}84\ \text{km}.\]

Para $1$ KiB, $L = 8192$ bits e $d^{*} = 1{,}638$ km. Ou seja: para mensagens pequenas, que são a maioria absoluta do tráfego de uma aplicação típica, a propagação já domina a partir de um quilômetro e meio. Praticamente qualquer comunicação que saia do prédio está no regime dominado pela geografia. Essa é a justificativa quantitativa para a existência de caches de borda, e é o número que a leitora deve ter em mente quando alguém propuser resolver latência com hardware mais rápido.

4. O piso físico do RTT

O RTT, de Round-Trip Time (tempo de ida e volta), é o intervalo entre enviar um pacote e receber a confirmação de que ele chegou. Ele é a unidade de conta de quase tudo em redes, porque quase todo protocolo interessante precisa de pelo menos uma confirmação antes de prosseguir. O TCP precisa de uma ida e volta para abrir a conexão, o TLS precisa de outra, o DNS precisa de uma para resolver o nome. O Artigo 2 e o Artigo 3 vão contabilizar cada uma dessas.

Existe um valor de RTT que nenhum código, nenhum protocolo e nenhum orçamento melhora: o dobro do tempo de propagação pela menor distância geodésica entre os dois pontos. É o piso físico, e ele é calculado com a fórmula da Seção 3 aplicada à distância de grande círculo.

A Tabela 2 traz o piso a partir de São Paulo para cinco regiões de nuvem, com as distâncias calculadas pela fórmula do semiverseno sobre as coordenadas das cidades.

Destino Distância (km) Ida (ms) Piso do RTT (ms)
Ashburn, Virgínia, Estados Unidos $7665$ $38{,}3$ $76{,}6$
Frankfurt, Alemanha $9829$ $49{,}1$ $98{,}3$
Cidade do Cabo, África do Sul $6345$ $31{,}7$ $63{,}4$
Sydney, Austrália $13\,357$ $66{,}8$ $133{,}6$
Pequim, China $17\,599$ $88{,}0$ $176{,}0$

Tabela 2: piso físico do RTT a partir de São Paulo, em fibra a $200\,000$ km/s, sobre a distância de grande círculo. Nenhuma medição real pode ficar abaixo destes valores.

A palavra piso faz trabalho pesado nessa tabela. Medições reais ficam acima, tipicamente entre $30\%$ e $80\%$ acima, e por três razões que vale nomear.

A primeira é que a fibra não segue o grande círculo. Cabos submarinos contornam plataformas continentais, evitam zonas sísmicas e vão de estação de amarração a estação de amarração; cabos terrestres seguem ferrovias, rodovias e dutos. O caminho real é mais longo que o geodésico por um fator que a literatura estima entre $1{,}1$ e $2{,}0$, dependendo do par de cidades.

A segunda é o processamento e a comutação, que a Seção 3 quantificou como pequenos por salto e que se acumulam ao longo de dezenas de saltos.

A terceira é a fila, que é variável, depende da hora do dia e é o assunto da Seção 7.

A leitora que queira verificar isso não precisa acreditar em nenhuma das minhas afirmações. O programa da Seção 8 mede, e a plataforma RIPE Atlas oferece medições públicas entre milhares de pontos, com histórico. Medir é barato; supor é que sai caro.

O que fazer com esses números é a parte que interessa a quem projeta software. A Tabela 2 diz que, para um usuário em São Paulo, cada ida e volta a Frankfurt custa pelo menos $98{,}3$ ms. Uma arquitetura que exija três idas e volta sequenciais antes do primeiro byte útil, o que é o caso de uma resolução DNS fria seguida de abertura de TCP e de TLS, gasta

\[3 \times 98{,}3 = 294{,}9\ \text{ms}\]

antes de qualquer dado. Trezentos milissegundos é o limiar em que o cérebro humano deixa de perceber a resposta como imediata. A aplicação, note, ainda não fez absolutamente nada.

Inverta agora a conta, porque é assim que ela vira decisão de projeto. Um ponto de presença a $300$ km do usuário tem piso de RTT de $3{,}0$ ms. Quantas idas e voltas locais cabem no orçamento de uma única ida e volta a Frankfurt?

\[\left\lfloor \frac{98{,}3}{3{,}0} \right\rfloor = 32.\]

Trinta e duas. Uma arquitetura local pode ser trinta e duas vezes mais conversadora que uma arquitetura transatlântica e ainda assim entregar a resposta antes. Esse é o argumento inteiro a favor de redes de distribuição de conteúdo, de caches de borda e de computação na borda, expresso em uma divisão. Ele reaparece com números concretos no Artigo 10.

4.1 Exercícios da Seção 4

1. Calcule o piso do RTT entre São Paulo e a Virgínia do Norte

A distância de grande círculo entre São Paulo e Ashburn, na Virgínia, é de $7665$ km. Qual o menor RTT fisicamente possível em fibra?

Solução: O tempo de ida é $7665/200\,000 = 38{,}3$ ms, e o RTT é o dobro:

\[\text{RTT}_{\min} = \frac{2 \times 7665}{200\,000} = 76{,}65\ \text{ms} \approx 76{,}6\ \text{ms}.\]

Com $v = 199\,862$ km/s o resultado seria $76{,}70$ ms. Nenhuma otimização de software, nenhuma escolha de protocolo e nenhuma compra de banda produz um RTT de $50$ ms nesse par de cidades, porque isso exigiria um sinal a $306\,600$ km/s, acima da velocidade da luz no vácuo. Quando alguém prometer isso, a conversa acabou.

2. Construa a tabela de pisos para cinco regiões

Calcule o piso do RTT de São Paulo para Frankfurt, Cidade do Cabo, Sydney e Pequim, com as distâncias da Tabela 2.

Solução: Aplicando $\text{RTT}_{\min} = 2d/v$ com $v = 200\,000$ km/s:

Destino $d$ (km) $\text{RTT}_{\min}$ (ms)
Cidade do Cabo $6345$ $63{,}45$
Ashburn $7665$ $76{,}65$
Frankfurt $9829$ $98{,}29$
Sydney $13\,357$ $133{,}57$
Pequim $17\,599$ $175{,}99$

Note que a Cidade do Cabo, do outro lado do Atlântico Sul, é o destino intercontinental mais próximo de São Paulo, mais perto que a Virgínia. A intuição de que “os Estados Unidos são perto” vem da densidade de cabos e da economia, não da geografia. É um bom lembrete de que a topologia da Internet não é a topologia do planeta, tema que o Artigo 4 desenvolve ao tratar de sistemas autônomos.

3. Estime o excesso de uma medição real sobre o piso

Uma medição de $115$ ms para a Virgínia é observada de São Paulo. Qual o excesso relativo sobre o piso, e como atribuí-lo?

Solução: O excesso absoluto é $115 - 76{,}65 = 38{,}35$ ms, e o excesso relativo é

\[\frac{38{,}35}{76{,}65} = 0{,}5003 = 50{,}03\%.\]

Cinquenta por cento acima do piso é um valor comum e não indica problema. A atribuição segue as três causas da seção. Um fator de rota de $1{,}3$ sobre a geodésica explicaria $23$ ms; trinta saltos a $10$ µs explicam $0{,}3$ ms, portanto o processamento não é o assunto; o resto, cerca de $15$ ms, é fila e comutação. A ordem de grandeza da atribuição já elimina hipóteses: qualquer explicação que apele para “processamento nos roteadores” está errada por duas ordens de grandeza, e o Artigo 8 vai formalizar esse tipo de eliminação como método.

4. Calcule o custo de três idas e voltas sequenciais

Uma requisição exige resolução DNS fria, abertura de conexão TCP e negociação TLS antes do primeiro byte de aplicação. Qual o tempo mínimo para um usuário em São Paulo acessando Frankfurt?

Solução: Cada etapa custa ao menos uma ida e volta ao destino, e as três são sequenciais porque cada uma depende do resultado da anterior:

\[T = 3 \times 98{,}29 = 294{,}87\ \text{ms}.\]

Quase trezentos milissegundos antes de o servidor sequer ver a requisição. Esse número é a motivação inteira do QUIC, que o Artigo 2 vai apresentar: reduzir o handshake de duas idas e voltas para uma, ou para zero em conexões retomadas, economiza aqui entre $98$ e $196$ ms. E é também a motivação da Seção 3 do Artigo 3, sobre cache de DNS, porque a resolução quente custa menos de um milissegundo em vez de uma ida e volta inteira.

5. Determine quantas idas e voltas locais cabem em uma transatlântica

Um ponto de presença a $300$ km do usuário oferece RTT de $3{,}0$ ms. Quantas idas e voltas locais cabem no orçamento de uma única ida e volta a Frankfurt?

Solução: O RTT local é $2 \times 300/200\,000 = 3{,}0$ ms. A razão é

\[\left\lfloor \frac{98{,}29}{3{,}0} \right\rfloor = 32.\]

Trinta e duas idas e voltas. Uma consequência menos óbvia: isso significa que uma arquitetura de microsserviços com uma cadeia de dez chamadas internas, cada uma com RTT de $0{,}5$ ms dentro do mesmo centro de dados, gasta $5$ ms de rede, valor que desaparece diante da única ida e volta ao usuário. A conversa entre serviços internos quase nunca é o problema de latência; a conversa com o usuário quase sempre é. O Artigo 12 volta a esse cálculo quando somar o custo dos sidecars de um service mesh.

5. Largura de banda, vazão e o produto banda-atraso

Três palavras costumam ser usadas como sinônimas e não são. A largura de banda, ou capacidade, é a taxa máxima nominal do enlace: o que está no contrato. A vazão, ou throughput, é a taxa efetivamente obtida por um fluxo, contando tudo que atravessa o fio, inclusive cabeçalhos e retransmissões. O goodput é a taxa de dado útil da aplicação, descontados cabeçalhos e retransmissões. Um enlace de $1$ Gbit/s saturado com quadros cheios entrega $1$ Gbit/s de vazão e $1460/1538 \times 10^{9} = 949{,}3$ Mbit/s de goodput, e a diferença não é erro de medição: é o envelope da Seção 2. Com as opções de marcação temporal do TCP ativadas, que consomem mais doze bytes por segmento, o goodput cai para $941{,}5$ Mbit/s, que é o número que a leitora encontra na literatura.

Se um fluxo sofre $2\%$ de retransmissão e $3\%$ de cabeçalho, o goodput é

\[G = R \times (1 - 0{,}02) \times (1 - 0{,}03) = 0{,}9506\,R,\]

ou $95{,}06\%$ da vazão. A curiosa leitora que já discutiu um contrato de serviço sabe que a diferença entre essas três palavras costuma valer dinheiro.

Chegamos agora ao conceito que faz esta seção existir. Considere um enlace com taxa $R$ e ida e volta $\text{RTT}$. Enquanto o remetente espera a confirmação do primeiro bit, ele poderia estar transmitindo. A quantidade de dados que cabe “em voo” no caminho é o produto banda-atraso, ou BDP, de Bandwidth-Delay Product:

\[\text{BDP} = R \cdot \text{RTT}\]

Dimensionalmente, bits por segundo vezes segundos dá bits. O BDP é o volume do cano: quantos bits o caminho comporta simultaneamente, como a água que já saiu da bomba e ainda não chegou à torneira.

Para $R = 100$ Mbit/s e $\text{RTT} = 100$ ms,

\[\text{BDP} = 10^{8} \times 0{,}1 = 10^{7}\ \text{bits} = 1{,}25\ \text{MB}\]

Um megabyte e um quarto de dados em trânsito, existindo em nenhum computador, apenas no fio. Se o protocolo não conseguir manter essa quantidade em voo, o enlace fica ocioso esperando confirmações.

E aqui está o resultado que a esperta leitora deve levar da seção. Um protocolo com janela $W$, que envia $W$ bits e para até receber a confirmação, tem vazão máxima

\[R_{\text{efetiva}} = \frac{W}{\text{RTT}}.\]

A janela clássica do TCP, anunciada em um campo de $16$ bits, vale no máximo $65\,535$ bytes, ou $64$ KiB. Sobre o enlace de $100$ Mbit/s com $100$ ms de ida e volta:

\[R_{\text{efetiva}} = \frac{65\,535 \times 8}{0{,}1} = 5\,242\,800\ \text{bit/s} = 5{,}24\ \text{Mbit/s}.\]

Cinco vírgula vinte e quatro megabits por segundo em um enlace de cem. A conexão usa $5{,}24\%$ da capacidade contratada, e o problema não está no enlace, não está no servidor e não está na aplicação: está em um campo de dezesseis bits definido em 1981. É por isso que existe o escalonamento de janela da RFC 7323, e é o primeiro exemplo desta série de um número de cabeçalho decidindo a arquitetura de um sistema.

O caso simétrico também merece a conta. Para saturar $1$ Gbit/s com $\text{RTT} = 20$ ms, típico de uma conexão entre regiões próximas, é preciso manter em voo

\[\text{BDP} = \frac{10^{9} \times 0{,}02}{8} = 2\,500\,000\ \text{bytes} = 2{,}5\ \text{MB},\]

o que exige janela de $2{,}5$ MB, quarenta vezes maior que o máximo sem escalonamento, e exige também buffers de socket dimensionados para isso nas duas pontas. Um sistema que transfere volumes grandes entre regiões e não ajusta esses buffers está deixando, tipicamente, mais de $90\%$ da capacidade parada. O Artigo 2 mostrará que a janela do TCP tem, na verdade, duas restrições simultâneas, e que só uma delas é do receptor.

Fecho a seção com uma comparação que costuma incomodar. Transferir $10$ GB por um enlace de $1$ Gbit/s saturado leva $80$ s. Enviar um disco de $10$ GB por transporte aéreo, com doze horas porta a porta, leva $43\,200$ s. A rede vence por um fator de $540$. Agora inverta: qual o volume em que o avião empata? Em doze horas, o enlace de $1$ Gbit/s move $5{,}4$ TB. Acima disso, o transporte físico vence, e é exatamente por isso que os provedores de nuvem vendem serviços de migração baseados em caminhões com discos. A largura de banda de um caminhão carregado de discos é assombrosa; a latência é péssima. As duas grandezas continuam independentes, como a Seção 3 estabeleceu, mesmo quando uma delas envolve pneus.

5.1 Exercícios da Seção 5

1. Calcule o produto banda-atraso de um enlace intercontinental

Qual o BDP de um enlace de $100$ Mbit/s com RTT de $100$ ms?

Solução: Aplicando a definição,

\[\text{BDP} = 10^{8}\ \text{bit/s} \times 0{,}1\ \text{s} = 10^{7}\ \text{bits}.\]

Convertendo, $10^{7}/8 = 1\,250\,000$ bytes, ou $1{,}25$ MB. Esse é o volume de dados que o caminho comporta em trânsito. A interpretação operacional: para manter o enlace ocupado, o remetente precisa poder ter $1{,}25$ MB não confirmados a qualquer instante. Qualquer limite abaixo disso, venha do protocolo, do sistema operacional ou da aplicação, transforma o enlace em um recurso subutilizado.

2. Determine a vazão de uma janela de 64 KiB

Qual a vazão máxima de uma conexão limitada a uma janela de $64$ KiB, com RTT de $100$ ms? Que fração do enlace de $100$ Mbit/s ela usa?

Solução: A janela máxima sem escalonamento é $65\,535$ bytes, ou $524\,280$ bits. A vazão é

\[R_{\text{efetiva}} = \frac{524\,280}{0{,}1} = 5{,}2428\ \text{Mbit/s},\]

correspondente a $5{,}24\%$ do enlace. Aumentar o enlace para $1$ Gbit/s não muda nada: a vazão continua $5{,}24$ Mbit/s, agora usando $0{,}52\%$ da capacidade. Este é o exemplo canônico de um gargalo que se desloca sem que ninguém perceba, e é o motivo pelo qual o primeiro passo de qualquer diagnóstico de vazão baixa é comparar a janela em uso com o BDP, e não olhar o gráfico de utilização do enlace.

3. Dimensione a janela necessária para saturar 1 Gbit/s

Qual janela é necessária para saturar um enlace de $1$ Gbit/s com RTT de $20$ ms?

Solução: A janela precisa cobrir o BDP:

\[W = R \cdot \text{RTT} = 10^{9} \times 0{,}02 = 2 \times 10^{7}\ \text{bits} = 2{,}5\ \text{MB}.\]

Como $2{,}5$ MB excede em quase quarenta vezes o máximo de $64$ KiB do campo de janela, é obrigatório o escalonamento de janela da RFC 7323, que multiplica o valor anunciado por $2^{s}$, com $s$ entre $0$ e $14$. O menor $s$ que serve satisfaz $65\,535 \times 2^{s} \ge 2\,500\,000$, ou seja $2^{s} \ge 38{,}15$, portanto $s = 6$, que permite anunciar até $4{,}19$ MB. Registre que o escalonamento é negociado apenas no pacote inicial da conexão: se ele não foi acordado ali, não há como habilitá-lo depois, e a conexão inteira viverá com o teto de $64$ KiB.

4. Separe vazão de goodput

Uma transferência sofre $2\%$ de retransmissão e $3\%$ de cabeçalho. Qual o goodput como fração da vazão?

Solução: As duas perdas são multiplicativas, porque a retransmissão consome capacidade que já vinha reduzida pelo cabeçalho:

\[G = R \times 0{,}98 \times 0{,}97 = 0{,}9506\,R.\]

O goodput é $95{,}06\%$ da vazão, e a soma ingênua $R \times (1 - 0{,}05) = 0{,}95\,R$ erraria por $0{,}06$ ponto percentual, o que aqui é irrelevante e deixa de ser quando as frações crescem. Para $20\%$ e $30\%$, a forma multiplicativa dá $0{,}56\,R$ e a soma ingênua daria $0{,}50\,R$, um erro de $12\%$ sobre o resultado. A regra é sempre compor perdas por multiplicação de sobrevivências, nunca por soma de perdas.

5. Compare a rede com o transporte físico

Transferir $10$ GB por um enlace de $1$ Gbit/s saturado, ou enviar um disco por transporte aéreo em doze horas: qual vence, e em que volume o resultado se inverte?

Solução: Pela rede, $T = 10 \times 10^{9} \times 8 / 10^{9} = 80$ s. Pelo avião, $12 \times 3600 = 43\,200$ s. A rede vence por um fator de $43\,200/80 = 540$.

O ponto de inversão é o volume que o enlace consegue mover no mesmo tempo de voo:

\[V^{*} = \frac{10^{9} \times 43\,200}{8} = 5{,}4 \times 10^{12}\ \text{bytes} = 5{,}4\ \text{TB}.\]

Acima de $5{,}4$ TB, o transporte físico entrega antes. A moral não é sobre logística: é que largura de banda e latência são grandezas independentes, e que um canal pode ter largura de banda gigantesca e latência ridícula ao mesmo tempo. Todo raciocínio que trata “rápido” como uma grandeza única vai errar em algum dos dois eixos.

6. Jitter e a estatística da espera

Se a latência fosse constante, esta seção não existiria. Ela não é. O mesmo par de máquinas, medido cem vezes seguidas, produz cem valores diferentes, e a dispersão desses valores tem nome e consequência.

O jitter é a variação do atraso, e não o atraso. A definição operacional que a série usa é a da RFC 3550, que padroniza o RTP: para pacotes enviados nos instantes $S_i$ e recebidos nos instantes $R_i$, define-se a diferença de espaçamento entre dois pacotes consecutivos como

\[D_i = (R_i - R_{i-1}) - (S_i - S_{i-1}),\]

e o estimador incremental de jitter como

\[J_i = J_{i-1} + \frac{|D_i| - J_{i-1}}{16},\]

com $J_0 = 0$. O divisor $16$ é uma média móvel exponencial com ganho $1/16$, escolhida para ser calculável com deslocamento de bits em hardware de 1996 e mantida por compatibilidade. Ela tem uma consequência que a leitora deve conhecer: o estimador leva aproximadamente $16$ atualizações para se aproximar do valor de regime, o que significa que os primeiros pacotes de um fluxo produzem uma estimativa sistematicamente baixa.

Vamos aos números. Considere um fluxo de oito pacotes enviados a cada $20$ ms, com os instantes de chegada abaixo, todos em milissegundos:

$i$ $S_i$ $R_i$ $D_i$ $J_i$
0 0 12 0,000
1 20 35 3 0,188
2 40 51 −4 0,426
3 60 88 17 1,462
4 80 93 −15 2,308
5 100 112 −1 2,226
6 120 152 20 3,337
7 140 154 −18 4,253

Os atrasos individuais foram $12$, $15$, $11$, $28$, $13$, $12$, $32$ e $14$ ms, com desvio padrão amostral de $8{,}11$ ms e amplitude de $21$ ms. O estimador da RFC 3550 devolve $J = 4{,}25$ ms depois de sete atualizações, cerca de metade do desvio padrão. Não é erro do estimador: é o regime transitório do ganho $1/16$ operando como anunciado. Quem lê $J$ nos primeiros segundos de um fluxo está lendo um número que ainda não convergiu.

Agora a parte que muda decisões de produto. Considere dez medições de atraso de ida, em milissegundos:

\[18,\ 21,\ 19,\ 24,\ 20,\ 22,\ 19,\ 140,\ 21,\ 20.\]

A média é $32{,}4$ ms e a mediana é $20{,}5$ ms. A média é maior que nove das dez observações. Removendo o valor $140$, a média cai $36{,}9\%$, para $20{,}44$ ms, praticamente igual à mediana, enquanto a mediana cai $2{,}4\%$, para $20$ ms. Um único ponto em dez controla a média e não controla a mediana.

Este é o motivo pelo qual a média é a estatística menos útil em latência. Ela responde a uma pergunta que ninguém faz. Ninguém experimenta a latência média; cada usuário experimenta a latência da sua requisição, e a distribuição dessas experiências tem cauda. As estatísticas que importam são os percentis, e a série usa a notação $p_{50}$, $p_{95}$ e $p_{99}$ para o quantil correspondente.

Uma advertência honesta, que a série de Estatística possui em detalhe no artigo sobre percentis e boxplot: com dez observações, o $p_{95}$ não é uma medida, é uma opinião sobre como interpolar. Ordenando a amostra acima, obtemos $18, 19, 19, 20, 20, 21, 21, 22, 24, 140$. O $p_{95}$ por interpolação linear cai entre o nono e o décimo valores e vale $87{,}8$ ms; pelo método do vizinho mais próximo, vale $140$ ms. Os dois estão certos dentro do seu método e nenhum dos dois mede coisa alguma, porque não há dados suficientes na cauda. Percentis altos exigem amostras grandes: para estimar $p_{99}$ com alguma estabilidade, é preciso ordem de mil observações, e para $p_{99{,}9}$, ordem de dez mil.

Feita a ressalva, o efeito de produto é brutal e vale a conta. Um serviço com $p_{50}$ de $20$ ms e $p_{99}$ de $900$ ms parece saudável no painel e quebrado para quem o usa. Com $2000$ requisições por minuto, $1\%$ delas excede $900$ ms, o que dá $20$ requisições por minuto e $28\,800$ por dia. Se cada usuário faz uma requisição, são vinte e oito mil e oitocentas pessoas por dia com a impressão de que o sistema travou, enquanto a média fica em torno de $29$ ms e ninguém percebe nada.

Pior: as caudas se compõem. Uma página que dispara $30$ requisições em paralelo e só termina quando a última responde tem, se as requisições forem independentes e cada uma tiver $1\%$ de chance de exceder $900$ ms, probabilidade

\[P(\text{ao menos uma lenta}) = 1 - (1 - 0{,}01)^{30} = 1 - 0{,}99^{30} = 0{,}2603 = 26{,}03\%.\]

Vinte e seis por cento. O percentil 99 de um componente virou, na prática, o percentil 74 da página. Essa composição é a razão de a cauda ser o assunto de qualquer sistema com fan-out, e ela reaparece no Artigo 10, quando um balanceador distribuir requisições, e no Artigo 12, quando uma cadeia de microsserviços somar as caudas de cada salto.

Vale registrar o que o jitter faz com aplicações interativas. Voz e vídeo em tempo real acumulam pacotes em um buffer de reprodução antes de tocá-los, precisamente para absorver a variação de chegada. Um buffer de $50$ ms tolera $50$ ms de variação e acrescenta $50$ ms de atraso a todo mundo. É um compromisso puro: aumentar o buffer reduz a fração de pacotes que chegam tarde demais e piora a interatividade de todos os pacotes, inclusive os que teriam chegado a tempo. O ponto ótimo depende da distribuição de atrasos, e essa distribuição depende da fila, que é o assunto da próxima seção.

Este é o momento de a leitora abrir o Laboratório 01 da suíte NetworkLabs e fazer um experimento específico: fixe a latência base em um destino, deixe-a intocada, e varie apenas o jitter máximo. Observe que o tempo médio de resposta quase não se move enquanto a experiência de uso se degrada visivelmente. É a Seção 6 inteira em uma tela.

6.1 Exercícios da Seção 6

1. Calcule média, mediana e desvio de uma amostra com cauda

Para os dez atrasos $18, 21, 19, 24, 20, 22, 19, 140, 21, 20$ ms, calcule média, mediana e desvio padrão amostral, e compare com os valores obtidos ao remover a maior observação.

Solução: A soma das dez observações é $324$, portanto a média é $32{,}4$ ms. Ordenando, $18, 19, 19, 20, 20, 21, 21, 22, 24, 140$; com $n$ par, a mediana é a média dos dois valores centrais, $(20 + 21)/2 = 20{,}5$ ms. O desvio padrão amostral é $37{,}85$ ms.

Removendo o $140$, a soma cai para $184$ em nove observações, dando média $20{,}44$ ms e mediana $20$ ms.

Estatística Com o valor 140 Sem o valor 140 Variação
média $32{,}40$ ms $20{,}44$ ms $-36{,}9\%$
mediana $20{,}50$ ms $20{,}00$ ms $-2{,}4\%$
desvio padrão $37{,}85$ ms $1{,}81$ ms $-95{,}2\%$

Uma observação em dez controla a média e o desvio, e não controla a mediana. Em latência, em que a cauda é a regra e não a exceção, isso significa que a média reporta a cauda disfarçada de tendência central. Reportar média de latência não é errado por convenção: é errado porque o número resultante não descreve a experiência de nenhum usuário concreto.

2. Aplique o estimador de jitter da RFC 3550

Para o fluxo de oito pacotes da tabela desta seção, reconstrua os valores de $D_i$ e $J_i$.

Solução: Os pacotes são enviados a cada $20$ ms, portanto $S_i - S_{i-1} = 20$ para todo $i$. Com os instantes de chegada $12, 35, 51, 88, 93, 112, 152, 154$:

$i$ $R_i - R_{i-1}$ $D_i$ $|D_i|$ $J_i = J_{i-1} + (|D_i| - J_{i-1})/16$
1 23 3 3 $0 + (3-0)/16 = 0{,}188$
2 16 −4 4 $0{,}188 + (4-0{,}188)/16 = 0{,}426$
3 37 17 17 $0{,}426 + (17-0{,}426)/16 = 1{,}462$
4 5 −15 15 $1{,}462 + (15-1{,}462)/16 = 2{,}308$
5 19 −1 1 $2{,}308 + (1-2{,}308)/16 = 2{,}226$
6 40 20 20 $2{,}226 + (20-2{,}226)/16 = 3{,}337$
7 2 −18 18 $3{,}337 + (18-3{,}337)/16 = 4{,}253$
O valor final é $J = 4{,}25$ ms. Repare na linha $5$: um $ D_i $ pequeno faz o estimador cair, porque a média móvel puxa na direção da nova observação. O estimador mede dispersão recente, não dispersão acumulada, e por isso esquece um evento ruim depois de algumas dezenas de pacotes. Isso é desejável em um controle de reprodução e é péssimo em um relatório de incidente, no qual a leitora precisa exatamente do evento esquecido.

3. Traduza um percentil em número de pessoas

Um serviço tem $p_{50}$ de $20$ ms e $p_{99}$ de $900$ ms, com $2000$ requisições por minuto. Quantos usuários por dia encontram mais de $900$ ms?

Solução: Por definição, $1\%$ das requisições excede o $p_{99}$. Em um minuto, $2000 \times 0{,}01 = 20$ requisições. Em um dia,

\[20 \times 60 \times 24 = 28\,800\ \text{requisições}.\]

Se cada requisição pertence a um usuário distinto, são $28\,800$ pessoas por dia com a impressão de sistema travado. A média das latências, supondo que os $99\%$ restantes fiquem próximos de $20$ ms, seria de aproximadamente $0{,}99 \times 20 + 0{,}01 \times 900 = 28{,}8$ ms, número que qualquer painel exibiria em verde. O exercício mostra que média e percentil não discordam sobre os dados: eles respondem a perguntas diferentes, e apenas uma delas tem sujeito humano.

4. Componha as caudas de requisições paralelas

Uma página dispara $30$ requisições em paralelo e só termina quando a última responde. Se cada requisição tem $1\%$ de chance independente de exceder $900$ ms, qual a probabilidade de a página exceder?

Solução: A página não excede apenas se nenhuma das trinta exceder. Sob independência,

\[P(\text{nenhuma lenta}) = (1 - 0{,}01)^{30} = 0{,}99^{30} = 0{,}7397,\]

logo

\[P(\text{ao menos uma lenta}) = 1 - 0{,}7397 = 0{,}2603 = 26{,}03\%.\]

O $p_{99}$ do componente virou o $p_{74}$ da página. Vale registrar que a hipótese de independência é otimista: requisições paralelas ao mesmo serviço compartilham fila, pool de conexões e coletor de lixo, o que correlaciona positivamente os eventos lentos e aumenta a probabilidade calculada. Duas consequências de projeto seguem daí, e as duas aparecem adiante na série: reduzir o fan-out melhora mais que reduzir a mediana, e requisições redundantes com corte, tratadas no Artigo 18, trocam capacidade por cauda.

5. Dimensione um buffer de reprodução

Para a mesma amostra de dez atrasos, calcule a fração de pacotes que chegaria tarde demais com um buffer de reprodução de $30$ ms, e o efeito de aumentá-lo para $100$ ms.

Solução: Um buffer de $B$ ms tolera atrasos até $B$; o que exceder chega tarde e é descartado. Ordenando os atrasos, $18, 19, 19, 20, 20, 21, 21, 22, 24, 140$:

$B$ Pacotes tardios Fração Atraso adicional imposto a todos
$30$ ms 1 (o de 140) $10\%$ $30$ ms
$100$ ms 1 (o de 140) $10\%$ $100$ ms
$150$ ms 0 $0\%$ $150$ ms

Aumentar de $30$ para $100$ ms não melhorou nada e piorou a interatividade de todos os pacotes em $70$ ms. Só a $150$ ms a perda zera, ao custo de um atraso fixo cinco vezes maior que a mediana da rede. É o compromisso puro: o buffer é dimensionado pela cauda e cobrado de todo mundo. Note também que a amostra tem dez pontos, portanto a fração de $10\%$ carrega a mesma fragilidade dos percentis discutida no texto; com mil pacotes, a decisão seria tomada sobre o $p_{99}$ observado, e não sobre um único valor extremo.

7. Filas: o mecanismo que transforma carga em latência

Faltou o quarto somando. O tempo de fila é o que separa uma rede que funciona de uma rede que funciona hoje, e é o único dos quatro que não se calcula a partir da folha de dados do equipamento, porque depende do comportamento dos outros usuários.

Um enlace de saída é um servidor com uma fila. Pacotes chegam a uma taxa média $\lambda$ e são transmitidos a uma taxa média $\mu$, ambas em pacotes por segundo. A utilização é a razão

\[\rho = \frac{\lambda}{\mu},\]

adimensional, e representa a fração do tempo em que o enlace está ocupado. Para $\rho \ge 1$ a fila cresce sem limite e o sistema é instável; toda a discussão que segue supõe $\rho < 1$.

O modelo mais simples que captura o essencial é a fila M/M/1: chegadas segundo um processo de Poisson, tempos de serviço exponenciais e um único servidor. Ele é grosseiro para tráfego real, que é mais rajado que Poisson, e por isso subestima a espera. Mesmo assim, ele acerta o que importa, que é a forma da curva. Para M/M/1, o tempo médio no sistema, incluindo espera e serviço, é

\[W = \frac{1}{\mu - \lambda} = \frac{1}{\mu(1 - \rho)},\]

e o número médio de pacotes no sistema é

\[L = \frac{\rho}{1 - \rho}.\]

O denominador $1 - \rho$ é toda a história. Com $\mu = 1000$ pacotes/s, a Tabela 3 mostra o que acontece quando a carga sobe.

$\lambda$ (pacotes/s) $\rho$ $W$ (ms) $L$ (pacotes)
500 0,50 2,0 1,0
800 0,80 5,0 4,0
900 0,90 10,0 9,0
950 0,95 20,0 19,0
990 0,99 100,0 99,0

Tabela 3: tempo médio no sistema em uma fila M/M/1 com $\mu = 1000$ pacotes/s. Entre $\rho = 0{,}5$ e $\rho = 0{,}99$, a carga dobra e a espera se multiplica por cinquenta.

Leia a tabela duas vezes. Da primeira para a última linha, a taxa de chegada quase dobrou, de $500$ para $990$, e o tempo de espera se multiplicou por cinquenta. Entre $\rho = 0{,}9$ e $\rho = 0{,}95$, um aumento de carga de $5{,}6\%$ dobrou a latência. A relação não é linear, não é quadrática: é hiperbólica, com assíntota vertical em $\rho = 1$.

A Figura 3 mostra a curva, e vale olhá-la por um instante antes de continuar.

Gráfico da espera média em milissegundos contra a utilização do enlace, para uma fila M M 1 com taxa de serviço de mil pacotes por segundo. A curva é praticamente plana até uma utilização de 0,7, sobe de forma perceptível a partir de 0,8 e cresce sem limite ao se aproximar de 1. Marcadores destacam os pontos de utilização 0,5 com 2 milissegundos, 0,8 com 5 milissegundos, 0,9 com 10 milissegundos e 0,95 com 20 milissegundos. Figura 3: A espera é praticamente insensível à carga até 70% de utilização e explode depois de 90%. Dimensionar um enlace pela sua capacidade nominal é dimensionar para o ponto em que a curva já subiu.

A consequência de engenharia é direta e contraintuitiva para quem vem de fora: um enlace de $1$ Gbit/s não pode ser usado a $1$ Gbit/s. Se o requisito for manter a espera abaixo de $5$ ms com $\mu = 1000$ pacotes/s, resolvemos

\[\frac{1}{1000 - \lambda} \le 0{,}005 \quad\Longrightarrow\quad \lambda \le 800,\]

ou seja, $\rho \le 0{,}8$. Vinte por cento da capacidade contratada não é desperdício: é o preço da previsibilidade. Toda vez que a leitora vir um painel de capacidade com uma linha vermelha em $80\%$, é esta a conta por trás, e não uma superstição de operações.

Há um segundo resultado, mais geral e mais bonito, que se aplica a qualquer sistema estável, sem hipótese nenhuma sobre a distribuição de chegadas ou serviços. É a lei de Little:

\[L = \lambda W,\]

na qual $L$ é o número médio de itens no sistema, $\lambda$ é a taxa média de chegada e $W$ é o tempo médio que um item passa no sistema. A generalidade é o que a torna útil: ela vale para pacotes em um roteador, requisições em um servidor, pedidos em uma fábrica e pessoas em uma fila de banco.

Um uso imediato. Um serviço que atende $2000$ requisições por segundo com tempo médio de resposta de $50$ ms tem, em média,

\[L = 2000 \times 0{,}05 = 100\]

requisições simultaneamente dentro dele. Esse número dimensiona o pool de conexões, o número de threads, o limite de concorrência e o tamanho do buffer de aceitação. Um pool de vinte conexões nesse serviço não vai atender $2000$ requisições por segundo; ele vai formar uma segunda fila antes da primeira, e a leitora vai passar a tarde procurando o problema no lugar errado. A lei de Little reaparece no Artigo 10, para dimensionar instâncias, e no Artigo 24, para planejar capacidade.

Fecho com a patologia que resume a seção. Memória ficou barata, e a reação natural de quem projeta equipamento de rede foi colocar buffers generosos, para não descartar pacotes. O resultado é o bufferbloat. Considere um enlace de $10$ Mbit/s com um buffer de $256$ MB completamente ocupado. O tempo para drenar esse buffer é

\[T_{\text{fila}} = \frac{256 \times 10^{6} \times 8}{10 \times 10^{6}} = 204{,}8\ \text{s}.\]

Duzentos e cinco segundos. Mais de três minutos de fila. Nesse regime, o pacote de controle que informaria ao remetente para desacelerar chega três minutos atrasado, e o mecanismo de controle de congestionamento do TCP, que o Artigo 2 vai detalhar, opera com informação inútil. O buffer grande não evitou o descarte: ele substituiu descarte por atraso, e atraso longo é indistinguível de falha para qualquer aplicação interativa. Guardar mais é, aqui, exatamente o oposto de servir melhor.

Este é o momento de manipular os números em vez de lê-los. O laboratório abaixo compõe as quatro parcelas da Seção 3 com a fila desta seção, e mostra o orçamento inteiro de uma página se rearranjar conforme a leitora muda distância, taxa do enlace, tamanho do objeto, utilização, número de saltos e número de requisições.

Duas escolhas de modelagem precisam ser declaradas, porque elas determinam o que a leitora vai ver. A parcela de fila reúne dois efeitos distintos e ambos reais: a espera do primeiro pacote no gargalo, que vale $\rho/(1-\rho)$ vezes o tempo de serviço de um quadro cheio, e a disputa durante a transferência, porque um enlace ocupado a $\rho$ entrega ao fluxo apenas $(1-\rho)R$. A parcela de transmissão continua sendo o $L/R$ puro, para que fique visível o que é física do enlace e o que é competição com terceiros. A segunda escolha está no cálculo da página: ao paralelizar $n$ requisições, a latência é paga uma vez e a transmissão é paga $n$ vezes, porque os objetos disputam o mesmo enlace. Latência paraleliza; largura de banda não.

Vale começar pelos presets, que reproduzem as rotas da Tabela 2, e depois empurrar a utilização de $0{,}50$ para $0{,}95$ sem tocar em mais nada. Na rota São Paulo–Virgínia com objetos de $256$ KiB, a fila salta de $17{,}8\%$ para $80{,}5\%$ do atraso de cada requisição, e o total de uma página com oito objetos em paralelo passa de $413$ ms para $3437$ ms. Nenhum equipamento foi trocado, nenhum código foi alterado e nenhuma distância mudou.

7.1 Exercícios da Seção 7

1. Calcule espera e ocupação para três cargas

Para uma fila M/M/1 com $\mu = 1000$ pacotes/s, calcule $\rho$, $W$ e $L$ para $\lambda \in {500, 900, 990}$.

Solução: Aplicando $\rho = \lambda/\mu$, $W = 1/(\mu - \lambda)$ e $L = \rho/(1-\rho)$:

$\lambda$ $\rho$ $W$ $L$
500 $0{,}50$ $1/500 = 2{,}0$ ms $0{,}5/0{,}5 = 1{,}0$
900 $0{,}90$ $1/100 = 10{,}0$ ms $0{,}9/0{,}1 = 9{,}0$
990 $0{,}99$ $1/10 = 100{,}0$ ms $0{,}99/0{,}01 = 99{,}0$

Entre a primeira e a terceira linha, $\lambda$ cresceu $98\%$ e $W$ cresceu $4900\%$. Verifique também que a lei de Little é respeitada em cada linha: $L = \lambda W$ dá $500 \times 0{,}002 = 1$, $900 \times 0{,}010 = 9$ e $990 \times 0{,}100 = 99$. A consistência não é coincidência: a fórmula de $L$ para M/M/1 é derivada de forma que Little valha, como tem de valer para qualquer sistema estável.

2. Determine a utilização máxima que respeita um orçamento de espera

Com $\mu = 1000$ pacotes/s, qual a maior taxa de chegada que mantém a espera média abaixo de $5$ ms?

Solução: Impondo $W \le 0{,}005$ s,

\[\frac{1}{1000 - \lambda} \le 0{,}005 \quad\Longrightarrow\quad 1000 - \lambda \ge 200 \quad\Longrightarrow\quad \lambda \le 800.\]

A utilização máxima é $\rho = 0{,}8$. Um enlace de $1$ Gbit/s dimensionado para esse orçamento pode carregar, em média, $800$ Mbit/s. Os $200$ Mbit/s restantes não estão sobrando: eles são o que compra a previsibilidade. Um requisito mais rigoroso, de $2$ ms, exigiria $\lambda \le 500$, ou metade do enlace, e é assim que orçamentos de latência viram decisões de compra de capacidade.

3. Aplique a lei de Little ao dimensionamento de um pool

Um serviço atende $2000$ requisições por segundo com tempo médio de resposta de $50$ ms. Quantas requisições estão, em média, dentro dele? Qual o efeito de um pool de $20$ conexões?

Solução: Pela lei de Little,

\[L = \lambda W = 2000 \times 0{,}05 = 100\ \text{requisições simultâneas}.\]

Com um pool de $20$ conexões, o serviço só consegue ter $20$ requisições em voo. Invertendo Little para descobrir a vazão sustentável nessa restrição,

\[\lambda_{\max} = \frac{L}{W} = \frac{20}{0{,}05} = 400\ \text{requisições/s}.\]

As outras $1600$ requisições por segundo formam uma fila antes do serviço, e o tempo de resposta observado pelo cliente cresce sem que nenhuma métrica interna do serviço se mova: internamente, cada requisição continua levando $50$ ms. É a assinatura mais comum de um problema de concorrência mal dimensionada, e ela engana porque todos os painéis do serviço permanecem saudáveis. O Artigo 21 volta a esse caso ao tratar de onde instrumentar.

4. Quantifique o bufferbloat

Um enlace de $10$ Mbit/s tem um buffer de saída de $256$ MB. Qual o atraso de fila quando ele está cheio? E com um buffer de $64$ MB?

Solução: O tempo de drenagem é o volume dividido pela taxa:

\[T_{256} = \frac{256 \times 10^{6} \times 8}{10^{7}} = 204{,}8\ \text{s}, \qquad T_{64} = \frac{64 \times 10^{6} \times 8}{10^{7}} = 51{,}2\ \text{s}.\]

Reduzir o buffer por quatro reduziu o atraso máximo por quatro, o que é o resultado esperado e não é o ponto. O ponto é que ambos são absurdos: um pacote de controle enfileirado atrás de $51$ segundos de dados chega tarde demais para controlar coisa alguma. O controle de congestionamento do TCP usa a perda de pacote e o RTT como sinal, e um buffer enorme suprime o primeiro e distorce o segundo, deixando o algoritmo cego. Daí a família de disciplinas de fila com gerência ativa, como CoDel e FQ-CoDel, que descartam de propósito para preservar o sinal. Descartar cedo é, aqui, uma forma de comunicação.

5. Explique por que a rajada piora a estimativa de M/M/1

O modelo M/M/1 supõe chegadas de Poisson. Tráfego real é mais rajado. Em que direção o erro do modelo aponta, e por quê?

Solução: A espera em uma fila com um servidor cresce com a variabilidade dos intervalos entre chegadas e dos tempos de serviço. A fórmula de Pollaczek–Khinchine para filas M/G/1 torna isso explícito: a espera é proporcional a $1 + c_s^{2}$, na qual $c_s$ é o coeficiente de variação do tempo de serviço. Para o caso exponencial, $c_s = 1$ e o fator vale $2$; para serviço determinístico, $c_s = 0$ e o fator vale $1$, ou seja, metade da espera; para serviço com cauda pesada, $c_s > 1$ e a espera é maior.

Para chegadas, a aproximação de Kingman para filas G/G/1 mostra que a espera é proporcional a $(c_a^{2} + c_s^{2})/2$, com $c_a$ o coeficiente de variação dos intervalos entre chegadas. Poisson tem $c_a = 1$; tráfego real, com rajadas em múltiplas escalas de tempo, tem $c_a > 1$. A consequência conjunta é que M/M/1 subestima a espera real, e a subestimação piora conforme a utilização sobe, porque o fator de variabilidade multiplica o termo $\rho/(1-\rho)$ que já estava explodindo. A moral prática: use M/M/1 para entender a forma da curva e para tomar decisões de ordem de grandeza, como escolher entre $\rho = 0{,}8$ e $\rho = 0{,}95$; não use para prometer um $p_{99}$ a ninguém. Prometer percentil exige medição, e a medição é o assunto da próxima seção.

8. Medindo o que se afirma, em C++23

Nada nas seções anteriores vale se a leitora não conseguir medir. E medir latência é mais fácil de fazer errado do que parece: relógio errado, sem aquecimento, média em vez de percentil, uma repetição só, e o resultado é um número bonito que não descreve nada.

Quatro regras de metodologia, cada uma corrigindo um erro comum.

Primeira, relógio monotônico. Use std::chrono::steady_clock, nunca system_clock. O relógio de sistema pode andar para trás quando o serviço de sincronização de horário o ajusta, e a leitora vai obter durações negativas em algum momento entre a meia-noite e o incidente.

Segunda, aquecimento descartado. As primeiras execuções pagam falhas de cache, alocação de páginas e ajuste de frequência do processador. Descarte-as explicitamente.

Terceira, repetições cronometradas individualmente. Medir mil repetições e dividir por mil devolve a média, e a Seção 6 explicou por que a média é a estatística menos útil aqui. Guarde cada amostra.

Quarta, percentis, não média. E, para calculá-los, não é preciso ordenar o vetor inteiro: std::nth_element posiciona o elemento de ordem desejada em tempo linear médio, o que importa quando as amostras são milhões.

O programa abaixo implementa as quatro regras. Ele calcula percentis por interpolação linear, o estimador de jitter da RFC 3550 e traz uma função de medição genérica que aplica aquecimento e repetição a qualquer operação. Os dados embutidos são exatamente os das Seções 6 e 7, para que a leitora possa conferir a saída contra os números do texto.

// latencia.cpp -- estatisticas de atraso e jitter segundo a RFC 3550.
// MSVC:  cl /std:c++latest /W4 /EHsc /O2 latencia.cpp
// Clang: clang++ -std=c++23 -stdlib=libc++ -O2 -Wall -Wextra latencia.cpp -o latencia

#include <algorithm>
#include <chrono>
#include <cmath>
#include <cstddef>
#include <expected>
#include <print>
#include <span>
#include <string_view>
#include <vector>

namespace rede {

enum class Erro { amostra_vazia, percentil_invalido };

constexpr std::string_view descrever(Erro e) {
    switch (e) {
        case Erro::amostra_vazia:      return "amostra vazia";
        case Erro::percentil_invalido: return "percentil fora de [0, 1]";
    }
    return "erro desconhecido";
}

// Percentil por interpolacao linear entre as ordens vizinhas.
// Recebe uma copia porque nth_element reordena o intervalo, e uma funcao de
// consulta que embaralha o argumento do chamador e uma armadilha.
std::expected<double, Erro> percentil(std::vector<double> amostras, double p) {
    if (amostras.empty()) return std::unexpected(Erro::amostra_vazia);
    if (p < 0.0 || p > 1.0) return std::unexpected(Erro::percentil_invalido);
    if (p == 0.0) return *std::ranges::min_element(amostras);
    if (p == 1.0) return *std::ranges::max_element(amostras);

    const double k = (static_cast<double>(amostras.size()) - 1.0) * p;
    const auto   i = static_cast<std::size_t>(std::floor(k));
    const double f = k - static_cast<double>(i);

    auto pos = amostras.begin() + static_cast<std::ptrdiff_t>(i);
    std::nth_element(amostras.begin(), pos, amostras.end());
    const double baixo = *pos;
    if (f == 0.0) return baixo;

    const double alto = *std::ranges::min_element(std::span(pos + 1, amostras.end()));
    return baixo + f * (alto - baixo);
}

// Estimador incremental de jitter da RFC 3550, secao A.8:
//   J <- J + (|D| - J) / 16,  com  D = (R_i - R_{i-1}) - (S_i - S_{i-1}).
double jitter_rfc3550(std::span<const double> envio, std::span<const double> chegada) {
    double J = 0.0;
    const std::size_t n = std::min(envio.size(), chegada.size());
    for (std::size_t i = 1; i < n; ++i) {
        const double D = (chegada[i] - chegada[i - 1]) - (envio[i] - envio[i - 1]);
        J += (std::abs(D) - J) / 16.0;
    }
    return J;
}

// Metodologia de medicao: aquecimento descartado, repeticoes cronometradas
// uma a uma com relogio monotonico, resultado devolvido em milissegundos.
template <class Operacao>
std::vector<double> medir(Operacao&& op, int aquecimento, int repeticoes) {
    for (int i = 0; i < aquecimento; ++i) op();

    std::vector<double> amostras;
    amostras.reserve(static_cast<std::size_t>(repeticoes));
    for (int i = 0; i < repeticoes; ++i) {
        const auto t0 = std::chrono::steady_clock::now();
        op();
        const auto t1 = std::chrono::steady_clock::now();
        amostras.push_back(std::chrono::duration<double, std::milli>(t1 - t0).count());
    }
    return amostras;
}

}  // namespace rede

int main() {
    // Os dez atrasos de ida da Secao 6, em milissegundos.
    const std::vector<double> atrasos =
        {18.0, 21.0, 19.0, 24.0, 20.0, 22.0, 19.0, 140.0, 21.0, 20.0};

    std::println("n      = {} amostras", atrasos.size());
    for (const double p : {0.50, 0.90, 0.95, 0.99}) {
        const auto v = rede::percentil(atrasos, p);
        if (v) std::println("p{:02.0f}    = {:.2f} ms", p * 100, *v);
        else   std::println("p{:02.0f}    = {}", p * 100, rede::descrever(v.error()));
    }

    // O fluxo de oito pacotes da Secao 6, enviados a cada 20 ms.
    const std::vector<double> envio   = {0, 20, 40, 60, 80, 100, 120, 140};
    const std::vector<double> chegada = {12, 35, 51, 88, 93, 112, 152, 154};
    std::println("jitter = {:.2f} ms", rede::jitter_rfc3550(envio, chegada));

    // O tipo obriga a tratar o caso degenerado, que aqui e provocado de proposito.
    const auto vazio = rede::percentil({}, 0.5);
    std::println("amostra vazia: {}",
                 vazio ? "aceita" : rede::descrever(vazio.error()));

    // A mesma maquinaria aplicada a uma operacao local qualquer.
    volatile double acumulador = 0.0;
    const auto medidas = rede::medir(
        [&] { for (int i = 0; i < 100000; ++i) acumulador = acumulador + 1.0; },
        10, 200);
    std::println("op p50 = {:.2f} ms", rede::percentil(medidas, 0.50).value_or(0.0));
    std::println("op p99 = {:.2f} ms", rede::percentil(medidas, 0.99).value_or(0.0));
    return 0;
}

n = 10 amostras p50 = 20.50 ms p90 = 35.60 ms p95 = 87.80 ms p99 = 129.56 ms jitter = 4.25 ms amostra vazia: amostra vazia

n      = 10 amostras
p50    = 20.50 ms
p90    = 35.60 ms
p95    = 87.80 ms
p99    = 129.56 ms
jitter = 4.25 ms

Os valores conferem com a Seção 6, incluindo o $p_{95}$ de $87{,}80$ ms que ali serviu para mostrar a fragilidade de percentis altos em amostras pequenas. As duas últimas linhas da saída, omitidas acima, medem uma operação local e variam de máquina para máquina, como devem.

Três observações sobre o código, porque nele há decisões e não apenas sintaxe.

A função percentil devolve std::expected<double, Erro> porque há duas formas legítimas de ela não ter resposta: amostra vazia e percentil fora do intervalo. Um valor sentinela, como NaN, obrigaria o chamador a lembrar de testá-lo; o tipo obriga. O programa exercita o caso degenerado de propósito, e a linha amostra vazia: amostra vazia da saída é a prova de que o caminho de erro existe e funciona.

Ela recebe o vetor por valor, e isso também é deliberado: std::nth_element reordena o intervalo, e uma função de consulta que modifica silenciosamente o argumento do chamador é uma armadilha. A cópia custa uma alocação; a alternativa custa uma tarde de depuração.

A função jitter_rfc3550 recebe std::span<const double>, e não const std::vector<double>&, porque span aceita vetor, arranjo nativo, std::array e qualquer bloco contíguo, sem forçar o chamador a converter. É a assinatura correta para uma função que só quer ler uma sequência contígua.

O acumulador da medição é volatile para impedir que o otimizador elimine o laço inteiro, o que ele faria com prazer sob -O2. Medir código morto é o erro clássico do primeiro benchmark de qualquer pessoa, e o sintoma é uma operação que aparenta custar zero nanossegundos.

A leitora que quiser levar isso adiante tem um exercício aberto e útil: substituir os dados embutidos por medições reais, obtidas com envio de pacotes a um destino escolhido, e comparar os percentis observados com o piso físico da Tabela 2. O Artigo 8 vai formalizar a coleta, a redução e a anonimização desses dados, que é o que separa uma medição de um vazamento.

9. O que delegar à máquina e como conferir

Chegamos à seção que se repete em todos os dezesseis artigos, sempre com a mesma estrutura e sempre com conteúdo específico do assunto.

O que um modelo de linguagem faz bem aqui. Converter um orçamento de latência descrito em prosa em uma tabela de somandos, com as unidades acertadas; gerar o esqueleto de um teste de carga a partir da descrição de um cenário; explicar o que uma métrica exibida em um painel significa; e traduzir entre unidades, que é onde a atenção humana falha primeiro. Todas essas tarefas têm em comum o fato de que a saída é verificável em segundos por quem a pediu.

A alucinação característica deste assunto. É a confusão entre largura de banda e latência, e ela aparece com uma regularidade que impressiona. Pedi a um assistente uma análise de um serviço em São Paulo consumindo uma API hospedada na Virgínia, com $300$ ms de tempo de resposta observado, e recebi, entre outras coisas, esta recomendação:

Aumentar a largura de banda do enlace de saída de 100 Mbit/s para 1 Gbit/s deve reduzir significativamente o tempo de resposta observado, já que a latência está diretamente relacionada à capacidade disponível do enlace.

A frase é gramaticalmente impecável, tecnicamente confiante e fisicamente falsa. Pela Tabela 2, o piso do RTT nesse par de cidades é $76{,}6$ ms; a resposta de $300$ ms comporta cerca de três idas e voltas mais processamento. Aumentar a taxa do enlace reduz $T_{\text{trans}}$, que para uma requisição de API de alguns kilobytes já era da ordem de dezenas de microssegundos. A recomendação custa dinheiro mensal recorrente e entrega, na melhor das hipóteses, uma melhora de $0{,}1\%$. A correção é a decomposição da Seção 3: identificar quantas idas e voltas a arquitetura exige antes do primeiro byte, e removê-las, que é o que os Artigos 2, 3 e 10 vão ensinar a fazer.

Note por que essa alucinação é difícil de pegar: ela não erra um fato verificável em um documento. Ela erra um modelo físico, e modelos físicos errados produzem texto perfeitamente plausível.

O verificador, em três camadas. Esta é a estrutura que a série usa do primeiro ao último artigo, sempre nesta ordem.

A primeira camada é a sintaxe: o artefato é bem formado? Aqui, isso significa perguntar se as unidades fecham. Uma recomendação que compara $\text{Mbit/s}$ com $\text{ms}$ sem uma terceira grandeza no meio está dimensionalmente errada antes de estar factualmente errada. A análise dimensional é o linter deste assunto, é barata e pega a maior parte dos disparates.

A segunda camada é a semântica: o número proposto respeita a física? Todo valor de latência tem um piso, $2d/v$, e todo valor de vazão tem um teto, $\min(R, W/\text{RTT})$. Uma afirmação que viole qualquer um dos dois é falsa independentemente de quão bem escrita esteja. Esta camada é a mais valiosa da série, porque ela não depende de confiar em ninguém: depende de uma divisão.

A terceira camada é o raio de explosão, tradução que a série usa para blast radius: se a recomendação estiver errada, o que para de funcionar e quem percebe? Uma recomendação de aumentar banda tem raio de explosão financeiro e reversível. Uma recomendação de reduzir o buffer de um enlace tem raio de explosão operacional e imediato. Uma recomendação de aumentar o tempo limite de uma chamada tem raio de explosão sutil, porque ela funciona até o dia em que a fila cresce e o sistema inteiro para em vez de degradar. A terceira camada é a que os alunos esquecem, e é a que decide se o erro custa um comando de reversão ou um domingo.

Exercício de auditoria. Um assistente produziu o relatório abaixo sobre um serviço com $p_{50}$ de $40$ ms e $p_{99}$ de $1200$ ms, consumido de São Paulo, com servidores em Frankfurt. Encontre os quatro defeitos antes de ler a resposta.

O tempo médio de resposta é de 51,6 ms, dentro do objetivo de 100 ms. O aumento observado no percentil 99 é compatível com sobrecarga de CPU nos servidores de aplicação. Recomenda-se aumentar a largura de banda do enlace e habilitar compressão, o que deve trazer o percentil 99 para próximo da média.

O primeiro defeito é usar a média para declarar conformidade com um objetivo, quando a Seção 6 mostrou que a média de uma distribuição com cauda não descreve a experiência de ninguém. Com $p_{50}$ de $40$ ms e $p_{99}$ de $1200$ ms, a média $0{,}99 \times 40 + 0{,}01 \times 1200 = 51{,}6$ ms é puxada pela cauda, e o objetivo deveria ser expresso em percentil.

O segundo defeito só aparece com a Tabela 2 na mão. O piso do RTT entre São Paulo e Frankfurt é de $98{,}3$ ms, e o relatório afirma um $p_{50}$ de $40$ ms para um consumo de São Paulo. Os dois números não podem descrever a mesma coisa. Ou a medição foi tomada no lado do servidor, e então mede processamento e não experiência do usuário, ou há um cache de borda no caminho e o serviço em Frankfurt não é quem responde à mediana das requisições. O relatório não diz onde mediu, e essa omissão invalida todas as conclusões seguintes. É o defeito mais valioso do exercício, porque uma divisão o derruba.

O terceiro defeito é a atribuição a “sobrecarga de CPU” sem nenhuma evidência que a distinga de fila de rede, de reconexão ou de resolução de nome fria. É uma hipótese apresentada como conclusão, e não vem acompanhada do teste que a falsificaria. O Artigo 8 transforma isso em método.

O quarto defeito é a recomendação de banda, já discutida, agravada pela promessa de que o $p_{99}$ ficaria “próximo da média”, que é uma afirmação sobre a forma de uma distribuição feita sem olhar a distribuição. Compressão, aliás, pode até ajudar, mas por outro motivo: ela reduz $L$ e, portanto, $T_{\text{trans}}$, o que só importa se o objeto for grande, coisa que o relatório não verificou.

Quatro defeitos, três camadas. O primeiro e o quarto morrem na semântica e na análise dimensional; o segundo morre no piso físico, que é a forma mais barata de falsificação que esta série oferece; o terceiro morre no raio de explosão, porque agir sobre a hipótese errada custa tempo de gente e, com frequência, uma mudança de infraestrutura que depois ninguém reverte.

10. Conclusão

Este artigo não construiu nenhum protocolo. Ele construiu a régua com que os quinze artigos seguintes serão medidos.

A leitora agora sabe que o encapsulamento cobra um envelope de tamanho fixo, e que a eficiência resultante é propriedade da mensagem e não do protocolo. Sabe decompor a latência em quatro parcelas de origens distintas, e sabe que duas delas escalam de formas independentes, o que torna “comprar banda” uma resposta correta para uma minoria dos problemas. Sabe calcular o piso físico de um RTT e usá-lo como critério de falsificação de qualquer promessa. Sabe que a quantidade de dados em voo é o produto banda-atraso, e que um campo de dezesseis bits definido em 1981 ainda limita transferências intercontinentais. Sabe que a média é a estatística menos útil em latência e que caudas se compõem, transformando o percentil 99 de um componente no percentil 74 de uma página. E sabe que a espera não cresce com a carga, ela explode com a carga, o que faz de $80\%$ de utilização um número de engenharia e não de contabilidade.

Sabe, por fim, que um modelo de linguagem confunde largura de banda com latência com a mesma serenidade com que acerta o resto, e que a defesa contra isso não é desconfiança genérica: é uma divisão, feita em dez segundos, comparando o número proposto com $2d/v$.

A física fixa o piso. O que está acima do piso é decisão de protocolo, e a primeira decisão que qualquer protocolo de transporte precisa tomar é o que fazer quando um pacote se perde: pedir de novo e esperar, ou seguir em frente e aceitar o buraco. O próximo artigo abre o transporte, e mostra que essa escolha aparentemente simples determina a arquitetura inteira de um sistema distribuído.

Acrônimos e Abreviações neste artigo

Acrônimo Definição em Inglês Tradução em Português
BDP Bandwidth-Delay Product Produto Banda-Atraso
BGP Border Gateway Protocol Protocolo de Roteamento de Borda
CPU Central Processing Unit Unidade Central de Processamento
DNS Domain Name System Sistema de Nomes de Domínio
ICMP Internet Control Message Protocol Protocolo de Mensagens de Controle da Internet
IP Internet Protocol Protocolo de Internet
ISO International Organization for Standardization Organização Internacional de Normalização
MSS Maximum Segment Size Tamanho Máximo de Segmento
MTU Maximum Transmission Unit Unidade Máxima de Transmissão
OSI Open Systems Interconnection Interconexão de Sistemas Abertos
QUIC QUIC (nome próprio, originalmente Quick UDP Internet Connections) QUIC
RFC Request for Comments Pedido de Comentários
RTP Real-time Transport Protocol Protocolo de Transporte em Tempo Real
RTT Round-Trip Time Tempo de Ida e Volta
TCP Transmission Control Protocol Protocolo de Controle de Transmissão
TLS Transport Layer Security Segurança da Camada de Transporte
UDP User Datagram Protocol Protocolo de Datagrama de Usuário

Referências

BONAVENTURE, O. Computer Networking: Principles, Protocols and Practice. 3. ed. Université catholique de Louvain. Disponível em: https://www.computer-networking.info/. Acesso em: 10 ago. 2026.

BORMAN, D.; BRADEN, B.; JACOBSON, V.; SCHEFFENEGGER, R. (ed.). RFC 7323: TCP Extensions for High Performance. IETF, 2014. Disponível em: https://www.rfc-editor.org/rfc/rfc7323. Acesso em: 10 ago. 2026.

BRADEN, R. (ed.). RFC 1122: Requirements for Internet Hosts — Communication Layers. IETF, 1989. Disponível em: https://www.rfc-editor.org/rfc/rfc1122. Acesso em: 10 ago. 2026.

EDDY, W. (ed.). RFC 9293: Transmission Control Protocol (TCP). IETF, 2022. Disponível em: https://www.rfc-editor.org/rfc/rfc9293. Acesso em: 10 ago. 2026.

GETTYS, J.; NICHOLS, K. Bufferbloat: Dark Buffers in the Internet. Communications of the ACM, v. 55, n. 1, p. 57–65, 2012. Disponível em: https://queue.acm.org/detail.cfm?id=2071893. Acesso em: 10 ago. 2026.

GRIGORIK, I. High Performance Browser Networking. Sebastopol: O’Reilly, 2013. Disponível em: https://hpbn.co/. Acesso em: 10 ago. 2026.

INTERNATIONAL ORGANIZATION FOR STANDARDIZATION. ISO/IEC 7498-1: Information technology — Open Systems Interconnection — Basic Reference Model: The Basic Model. Genebra: ISO, 1994. Disponível em: https://www.iso.org/standard/20269.html. Acesso em: 10 ago. 2026.

KINGMAN, J. F. C. The Single Server Queue in Heavy Traffic. Mathematical Proceedings of the Cambridge Philosophical Society, v. 57, n. 4, p. 902–904, 1961. DOI: 10.1017/S0305004100036094.

KLEINROCK, L. Queueing Systems, Volume 1: Theory. New York: Wiley-Interscience, 1975.

LITTLE, J. D. C. A Proof for the Queuing Formula $L = \lambda W$. Operations Research, v. 9, n. 3, p. 383–387, 1961. DOI: 10.1287/opre.9.3.383.

MASSACHUSETTS INSTITUTE OF TECHNOLOGY. 6.02 Introduction to EECS II: Digital Communication Systems. MIT OpenCourseWare. Disponível em: https://ocw.mit.edu/courses/6-02-introduction-to-eecs-ii-digital-communication-systems-fall-2012/. Acesso em: 10 ago. 2026.

NICHOLS, K.; JACOBSON, V. Controlling Queue Delay. ACM Queue, v. 10, n. 5, 2012. Disponível em: https://queue.acm.org/detail.cfm?id=2209336. Acesso em: 10 ago. 2026.

PETERSON, L.; DAVIE, B. Computer Networks: A Systems Approach. Edição aberta. Disponível em: https://book.systemsapproach.org/. Acesso em: 10 ago. 2026.

POSTEL, J. RFC 791: Internet Protocol. IETF, 1981. Disponível em: https://www.rfc-editor.org/rfc/rfc791. Acesso em: 10 ago. 2026.

RIPE NCC. RIPE Atlas. Disponível em: https://atlas.ripe.net/. Acesso em: 10 ago. 2026.

SALTZER, J. H.; REED, D. P.; CLARK, D. D. End-to-End Arguments in System Design. ACM Transactions on Computer Systems, v. 2, n. 4, p. 277–288, 1984. DOI: 10.1145/357401.357402.

SCHULZRINNE, H.; CASNER, S.; FREDERICK, R.; JACOBSON, V. RFC 3550: RTP — A Transport Protocol for Real-Time Applications. IETF, 2003. Disponível em: https://www.rfc-editor.org/rfc/rfc3550. Acesso em: 10 ago. 2026.

STANFORD UNIVERSITY. CS 144: Introduction to Computer Networking. Disponível em: https://cs144.github.io/. Acesso em: 10 ago. 2026.

UNIVERSITY OF CALIFORNIA, BERKELEY. CS 168: Introduction to the Internet — Architecture and Protocols. Disponível em: https://cs168.io/. Acesso em: 10 ago. 2026.

Índice da Série: Redes para Engenharia de Software

  • 1. A Pilha TCP/IP e a Física da Rede (Você está aqui)

(Updated: )