A Pilha TCP/IP e a Física da Rede
por Frank de Alcantara em 07/08/2026
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.
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.
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.
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: )