perf_counter_ns: meça desempenho em nanossegundos

Publicado em: 20/09/2026
Tempo de leitura: 6 minutos
Código Python sendo analisado para medir desempenho com perf_counter_ns

Medir o tempo de execução parece simples, mas pequenos detalhes podem tornar um benchmark enganoso. O módulo time oferece a função perf_counter_ns(), um relógio de alta resolução que retorna inteiros em nanossegundos. Ela é especialmente útil para comparar trechos curtos, registrar latências e evitar a perda de precisão que pode aparecer quando valores de ponto flutuante são acumulados. Neste guia, você aprenderá como usar essa função corretamente, interpretar seus resultados e construir medições mais confiáveis.

O que é perf_counter_ns

time.perf_counter_ns() devolve o valor atual de um contador de desempenho em nanossegundos. O número absoluto não representa uma data ou horário do calendário. Ele só faz sentido quando comparado com outra leitura do mesmo contador. Para medir uma duração, registre um valor antes da operação, outro depois e calcule a diferença.

from time import perf_counter_ns

inicio = perf_counter_ns()
resultado = sum(i * i for i in range(100_000))
fim = perf_counter_ns()

print(f"Duração: {fim - inicio} ns")

O contador inclui o tempo decorrido enquanto o processo está bloqueado ou aguardando o sistema, por isso é adequado para medir latência percebida. Ele também usa a maior resolução disponível no sistema operacional.

Diferença para perf_counter

perf_counter() retorna segundos como número de ponto flutuante. Já perf_counter_ns() retorna nanossegundos como inteiro. As duas funções usam o mesmo relógio de desempenho, mas a versão em nanossegundos evita conversões e problemas de arredondamento em medições pequenas ou em séries muito longas.

from time import perf_counter, perf_counter_ns

inicio_s = perf_counter()
inicio_ns = perf_counter_ns()
# operação medida
fim_s = perf_counter()
fim_ns = perf_counter_ns()

Isso não significa que o sistema realmente meça cada nanossegundo. A resolução efetiva depende da plataforma. O sufixo _ns indica apenas a unidade e o tipo inteiro do valor retornado.

Convertendo para unidades legíveis

Embora nanossegundos sejam úteis para cálculos, normalmente você apresentará resultados em microssegundos, milissegundos ou segundos.

duracao_ns = fim - inicio
microssegundos = duracao_ns / 1_000
milissegundos = duracao_ns / 1_000_000
segundos = duracao_ns / 1_000_000_000

Mantenha o valor original inteiro durante os cálculos e faça a conversão apenas no momento de exibir ou exportar. Essa prática reduz arredondamentos acumulados.

Criando um cronômetro reutilizável

Uma função auxiliar torna as medições mais consistentes.

from time import perf_counter_ns
from collections.abc import Callable
from typing import TypeVar

T = TypeVar("T")

def medir(funcao: Callable[[], T]) -> tuple[T, int]:
    inicio = perf_counter_ns()
    resultado = funcao()
    duracao = perf_counter_ns() - inicio
    return resultado, duracao

resultado, duracao = medir(lambda: sorted(range(50_000), reverse=True))
print(resultado[:3], duracao)

O retorno preserva tanto o resultado da função quanto a duração. Em código real, registre também informações como tamanho da entrada, versão do Python, plataforma e número de repetição.

Usando um context manager

Para medir blocos maiores, um gerenciador de contexto deixa o código mais limpo.

from contextlib import contextmanager
from time import perf_counter_ns

@contextmanager
def cronometro(rotulo: str):
    inicio = perf_counter_ns()
    try:
        yield
    finally:
        duracao = perf_counter_ns() - inicio
        print(f"{rotulo}: {duracao / 1_000_000:.3f} ms")

with cronometro("processamento"):
    dados = [x ** 2 for x in range(200_000)]

O bloco finally garante que a duração seja registrada mesmo quando uma exceção ocorre.

Por que uma única medição é insuficiente

O sistema operacional agenda processos, o interpretador aquece caches, o coletor de lixo pode executar e outros programas disputam CPU e memória. Uma medição isolada pode capturar um ruído incomum. Repita várias vezes e analise a distribuição.

from statistics import median
from time import perf_counter_ns

amostras = []
for _ in range(30):
    inicio = perf_counter_ns()
    sum(range(100_000))
    amostras.append(perf_counter_ns() - inicio)

print("mediana:", median(amostras), "ns")
print("mínimo:", min(amostras), "ns")

A mediana costuma representar melhor a execução típica do que a média quando existem picos. O menor valor pode aproximar o custo sem interrupções externas, mas não representa necessariamente a experiência real em produção.

Warm-up e caches

A primeira execução pode pagar custos de importação, criação de estruturas, leitura de disco, resolução de DNS ou preenchimento de caches. Separe aquecimento de medição quando isso fizer sentido.

def tarefa():
    return sum(i * i for i in range(20_000))

for _ in range(5):
    tarefa()

# medições reais começam depois do aquecimento

Não esconda o warm-up quando ele fizer parte da experiência do usuário, como na inicialização de uma aplicação. A decisão depende do que você deseja medir.

perf_counter_ns não substitui timeit

Para microbenchmarks, o módulo timeit é geralmente mais seguro porque repete a operação, reduz interferências comuns e oferece uma interface específica. perf_counter_ns() é melhor quando você precisa instrumentar fluxos reais, medir blocos assíncronos, registrar latências ou criar métricas personalizadas.

Você pode revisar como medir código com timeit, dicas de otimização em Python, profiling com cProfile e logging em Python.

Medindo operações assíncronas

O contador funciona normalmente com asyncio.

import asyncio
from time import perf_counter_ns

async def main():
    inicio = perf_counter_ns()
    await asyncio.sleep(0.05)
    duracao = perf_counter_ns() - inicio
    print(duracao / 1_000_000, "ms")

asyncio.run(main())

Nesse caso, a duração inclui o período de espera. Isso é correto quando o objetivo é medir latência de ponta a ponta. Para medir apenas CPU, use ferramentas e relógios apropriados, como process_time_ns().

Comparação com outros relógios

time_ns() representa tempo de calendário e pode sofrer ajustes do sistema. monotonic_ns() não volta para trás e é adequado para intervalos. perf_counter_ns() também é monotônico e seleciona o contador de maior resolução para desempenho. process_time_ns() mede tempo de CPU consumido pelo processo e exclui períodos de espera. Escolha o relógio de acordo com a pergunta que deseja responder.

Erros comuns

Não compare valores coletados em máquinas diferentes ou após reinicializações. Não trate o número absoluto como timestamp. Evite incluir print, logging ou preparação de dados dentro do bloco quando esses custos não fazem parte do teste. Também não conclua que uma versão é melhor com base em diferenças mínimas e poucas amostras.

Outro erro é testar entradas artificiais que não representam produção. Desempenho depende do tamanho, distribuição dos dados, cache, disco, rede e concorrência. Documente o ambiente e repita o teste sob condições comparáveis.

Registrando percentis de latência

Em aplicações, a média esconde picos. Registre várias durações e calcule percentis como p50, p95 e p99. Uma API pode ter média baixa, mas ainda apresentar caudas lentas que prejudicam usuários. Armazene as durações em nanossegundos ou converta para milissegundos no sistema de métricas.

Boas práticas

Defina claramente o início e o fim da operação, aqueça quando apropriado, faça várias repetições, use entradas realistas, separe tempo de CPU de latência total e evite otimizações antes de encontrar um gargalo real. Compare resultados no mesmo ambiente e versionamento.

A referência principal é a documentação oficial do módulo time. Para princípios de benchmarking, consulte também a documentação do módulo timeit.

Conclusão

perf_counter_ns() é uma ferramenta simples e poderosa para medir durações com valores inteiros e alta resolução. Ela funciona bem em instrumentação, testes de latência, context managers e medições personalizadas. Resultados confiáveis, porém, dependem mais do método do que do relógio: repita, aqueça quando necessário, controle o ambiente, escolha métricas adequadas e interprete variações com cuidado. Com essas práticas, você transforma números de nanossegundos em decisões de otimização muito mais seguras.

Compartilhe:

Facebook
WhatsApp
Twitter
LinkedIn

Conteúdo do artigo

    Artigos relacionados

    Código Python representando uma fila de prioridade com heapq max-heap
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    heapq max-heap: filas de prioridade máximas

    Aprenda a usar as funções de max-heap do módulo heapq para filas de prioridade, rankings e algoritmos eficientes no Python.

    Ler mais

    Tempo de leitura: 5 minutos
    19/09/2026
    Servidores representando workers paralelos do ProcessPoolExecutor
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    ProcessPoolExecutor kill_workers: encerre processos

    Aprenda terminate_workers e kill_workers no ProcessPoolExecutor para encerrar processos travados com segurança e controlar tarefas pendentes.

    Ler mais

    Tempo de leitura: 6 minutos
    19/09/2026
    Terminal de linha de comando usado em uma ferramenta Python com argparse
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    argparse suggest_on_error: melhore erros de CLI

    Aprenda argparse suggest_on_error no Python para sugerir opções corretas, melhorar erros de CLI e manter compatibilidade entre versões.

    Ler mais

    Tempo de leitura: 5 minutos
    18/09/2026
    Código e estrutura de arquivos para compressão Zstandard no Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    compression.zstd: compacte dados com Zstandard

    Aprenda a compactar e descompactar dados com compression.zstd no Python, usando streams, dicionários e limites seguros.

    Ler mais

    Tempo de leitura: 5 minutos
    18/09/2026
    Programador gerenciando uma fila assíncrona com asyncio.Queue.shutdown
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    asyncio.Queue.shutdown: encerre filas sem deadlocks

    Aprenda asyncio.Queue.shutdown no Python para encerrar filas, liberar workers, drenar tarefas e evitar deadlocks em pipelines assíncronos.

    Ler mais

    Tempo de leitura: 6 minutos
    17/09/2026
    Depuração de processo Python em execução com pdb -p
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    pdb -p no Python: depure processos

    Aprenda a usar pdb -p no Python para anexar o depurador a processos em execução, analisar travamentos e investigar pilhas

    Ler mais

    Tempo de leitura: 7 minutos
    17/09/2026