Taxa média e burst são dimensões distintas
O primeiro modelo usa um policer com um único token bucket, inicialmente cheio, reposição contínua e descarte de pacotes inteiros quando faltam tokens. A taxa é 8 000 000 bits/s, equivalente a 1 000 bytes por milissegundo, e o bucket tem 2 000 bytes. Quatro pacotes de 1 000 bytes chegam no instante zero. Os dois primeiros encontram crédito; os outros dois são descartados. Um quinto pacote chega aos 10 ms e encontra o bucket novamente cheio. A média destes 5 000 bytes numa janela de 10 ms é 4 Mb/s, abaixo de 8 Mb/s, mas essa média não descreve a pressão do burst inicial.
Caso: uma fila resolve perdas mas viola atraso
O segundo modelo é uma fila FIFO a taxa fixa de 8 Mb/s, sem crédito de burst. Não pretende reproduzir um shaper específico da Cisco. Cada pacote de 1 000 bytes demora 1 ms a ser servido. Com capacidade de 4 000 bytes, os quatro pacotes iniciais entram e terminam aos 1, 2, 3 e 4 ms; o quarto espera 3 ms antes de ser servido. Com capacidade de 2 000 bytes, os dois últimos do burst são descartados. A capacidade inclui o pacote em serviço, e transmissões concluídas no instante t libertam espaço antes das chegadas nesse instante. O resultado depende destes pressupostos explícitos.
Policing, shaping e capacidade finita
Policing pode descartar ou remarcar excesso conforme a ação configurada; o nosso modelo implementa apenas descarte. Shaping usa espera e scheduling para controlar saída, mas a fila é finita e pode perder pacotes. Não concluas que qualquer shaping elimina perdas ou que qualquer policer tem o mesmo comportamento de um único bucket. Depois de um longo período sem tráfego, o crédito do modelo continua limitado a 2 000 bytes. Um pacote de 3 000 bytes nunca cabe nesse bucket, mesmo após esperar. Em produção, confirma taxa, burst, unidades, ações e contabilização de overhead da implementação antes de escolher uma alteração.
Um excesso sustentado não cabe num pequeno orçamento
Num modelo fluido separado, entram 150 Mb/s e saem 100 Mb/s durante 200 ms. Sem perdas nem reação dos emissores, acumulam-se 1 250 000 bytes. Se as chegadas pararem nesse momento, a drenagem demora 100 ms a 100 Mb/s. Mantendo o excesso durante 20 segundos, a acumulação seria 125 000 000 bytes e a drenagem 10 segundos. Estes cálculos usam unidades decimais e não incluem overhead, TCP ou capacidade finita. Aumentar a fila pode absorver um burst breve, mas não cria capacidade para uma carga persistentemente superior ao serviço. Para APS, relaciona o limite de atraso com a janela de batch e as transações concorrentes.
Reproduzir e comparar com a rede real
Guarda o script completo abaixo como simulate.py e executa python3 simulate.py /tmp/qos-evidence.json. Só usa a biblioteca padrão Python e grava resultados locais; não acede à rede. Duas execuções foram verificadas com 19 asserções cada. Modifica uma variável de cada vez: tamanho do bucket, capacidade da FIFO, intervalo entre chegadas ou duração do excesso. Antes de transportar uma conclusão para um projeto, recolhe medições de bursts, drops, atraso e classificação na plataforma real. O modelo não contém TCP, overhead, várias filas, prioridade ou o scheduler de um fabricante. Um valor calculado não é um SLA medido nem uma configuração aprovada para produção.
# Save as simulate.py; run: python3 simulate.py /tmp/qos-evidence.json
"""Original didactic models. No network/device access or Cisco scheduler emulation."""
from collections import deque
from fractions import Fraction
import datetime,hashlib,json,pathlib,sys
def inputs(arrivals,rate_bps,capacity):
if rate_bps<=0 or capacity<=0:raise ValueError('Positive rate and capacity required')
previous=Fraction(0)
for ms,size in arrivals:
if ms<previous or ms<0 or size<=0:raise ValueError('Ordered nonnegative times and positive sizes required')
previous=Fraction(ms)
def police(arrivals,rate_bps,bucket_bytes):
"""One bucket, initially full, continuous refill, whole-packet exceed drop."""
inputs(arrivals,rate_bps,bucket_bytes);rate=Fraction(rate_bps,8000)
tokens=Fraction(bucket_bytes);last=Fraction(0);rows=[]
for index,(ms,size) in enumerate(arrivals):
at=Fraction(ms);tokens=min(Fraction(bucket_bytes),tokens+(at-last)*rate);before=tokens
admitted=tokens>=size
if admitted:tokens-=size
rows.append({'packet':index+1,'arrivalMs':float(at),'bytes':size,'tokensBefore':float(before),'admitted':admitted,'tokensAfter':float(tokens)})
last=at
return rows
def fifo(arrivals,rate_bps,capacity_bytes):
"""Fixed-rate FIFO serializer; capacity includes the packet in service.
Completions at time t free capacity before arrivals at t. No token credit.
"""
inputs(arrivals,rate_bps,capacity_bytes);pending=deque();last_finish=Fraction(0);rows=[]
for index,(ms,size) in enumerate(arrivals):
at=Fraction(ms)
while pending and pending[0][0]<=at:pending.popleft()
used=sum(s for _,s in pending);admitted=used+size<=capacity_bytes
row={'packet':index+1,'arrivalMs':float(at),'bytes':size,'occupiedBytesBefore':used,'admitted':admitted}
if admitted:
start=max(at,last_finish);finish=start+Fraction(size*8000,rate_bps)
pending.append((finish,size));last_finish=finish
row.update(startMs=float(start),finishMs=float(finish),waitMs=float(start-at),totalMs=float(finish-at))
rows.append(row)
return rows
def match(criteria,mode):
if not criteria or mode not in ['any','all']:raise ValueError('Nonempty criteria and any/all mode required')
return any(criteria) if mode=='any' else all(criteria)
def backlog(arrival_bps,service_bps,duration_ms):
if arrival_bps<0 or service_bps<=0 or duration_ms<0:raise ValueError('Invalid rate or duration')
queued=max(Fraction(0),Fraction((arrival_bps-service_bps)*duration_ms,8000))
return {'queuedBytes':float(queued),'drainMsIfArrivalsStop':float(queued*8000/service_bps)}
def run():
checks=[]
def verify(name,condition):
if not condition:raise AssertionError(name)
checks.append(name)
arrivals=[(0,1000),(0,1000),(0,1000),(0,1000),(10,1000)]
policed=police(arrivals,8_000_000,2000);wide=fifo(arrivals,8_000_000,4000);small=fifo(arrivals,8_000_000,2000)
verify('full bucket admits first two burst packets',[x['admitted']for x in policed]==[True,True,False,False,True])
verify('dropped packets do not consume unavailable tokens',policed[2]['tokensAfter']==policed[3]['tokensAfter']==0)
verify('idle refill is capped at bucket size',policed[4]['tokensBefore']==2000)
verify('four-kilobyte FIFO admits entire input',all(x['admitted']for x in wide))
verify('FIFO exposes increasing burst wait',[x['waitMs']for x in wide]==[0,1,2,3,0])
verify('FIFO completion includes one millisecond serialization',[x['finishMs']for x in wide]==[1,2,3,4,11])
verify('two-kilobyte FIFO drops excess burst packets',[x['admitted']for x in small]==[True,True,False,False,True])
oversized=police([(1000,3000)],8_000_000,2000)
verify('packet larger than bucket cannot conform even after idle',not oversized[0]['admitted'])
continuous=police([(0,1000),(1,1000),(2,1000)],8_000_000,1000)
verify('one packet per millisecond conforms at eight megabits',all(x['admitted']for x in continuous))
equality=fifo([(0,1000),(1,1000)],8_000_000,1000)
verify('same-time completion frees queue space before arrival',all(x['admitted']for x in equality))
fractional=fifo([(0,700)],8_000_000,1000)
verify('serialization preserves sub-millisecond result',fractional[0]['finishMs']==0.7)
classification=[{'dscpEF':a,'authorizedSource':b,'matchAny':match([a,b],'any'),'matchAll':match([a,b],'all')}for a,b in [(False,False),(False,True),(True,False),(True,True)]]
verify('any accepts either criterion',[x['matchAny']for x in classification]==[False,True,True,True])
verify('all requires both criteria',[x['matchAll']for x in classification]==[False,False,False,True])
short=backlog(150_000_000,100_000_000,200);long=backlog(150_000_000,100_000_000,20_000)
verify('short excess creates 1.25 MB backlog',short=={'queuedBytes':1250000.0,'drainMsIfArrivalsStop':100.0})
verify('twenty-second excess creates 125 MB backlog',long=={'queuedBytes':125000000.0,'drainMsIfArrivalsStop':10000.0})
verify('no positive backlog when service exceeds arrival',backlog(80_000_000,100_000_000,200)['queuedBytes']==0)
for label,args in [('negative time',[(-1,1000)]),('unordered times',[(2,1000),(1,1000)]),('zero packet',[(0,0)])]:
try:police(args,8_000_000,2000)
except ValueError:checks.append('rejects '+label)
else:raise AssertionError(label)
return {'checkedAt':datetime.datetime.now(datetime.timezone.utc).isoformat(),'scriptSha256':hashlib.sha256(pathlib.Path(__file__).read_bytes()).hexdigest(),'checks':checks,'inputs':{'rateBps':8000000,'bucketBytes':2000,'arrivals':arrivals},'policer':policed,'fifo4000':wide,'fifo2000':small,'oversized':oversized,'classification':classification,'fluidBacklog':{'short':short,'long':long},'assumptions':['Original deterministic teaching models, not IOS XE execution or physical measurements','Decimal rates: bps means bits/second; queue/bucket sizes mean bytes','Policer: one initially full bucket, continuous refill, drop when a whole packet cannot conform, no remarking','FIFO: fixed service rate without token credit or priority; capacity includes in-service packet; same-time departures happen first','Fluid backlog: constant offered and service rates, no feedback or losses; drain time assumes arrivals stop','No TCP dynamics, packet overhead, vendor scheduler, microburst capture or SLA measurement']}
if __name__=='__main__':
report=run();pathlib.Path(sys.argv[1]).write_text(json.dumps(report,indent=2)+'\n')
print(json.dumps({'checks':len(report['checks']),'modelOnly':True,'networkAccess':False}))
No modelo de 8 Mb/s, quatro pacotes de 1 000 bytes precisam de 4 ms de serviço; uma fila maior não reduz esse trabalho.
Armadilhas comuns
Bits confundidos com bytes; média como prova de ausência de bursts; fila infinita presumida; simulação como medição de hardware.
Tópicos relacionados: QoS: classificação, confiança e contratos · Arquitetura e capacidade sobrevivente
Explicita unidades e pressupostos, e aceita perdas e atraso com evidência que corresponda ao serviço.
Referência: Compare Traffic Policing and Traffic Shaping to Limit Bandwidth · 350-401 ENCOR v1.2, effective 2026-03-19; core component of CCNP Enterprise