MCU & FPGA RTOS FreeRTOS: Stream Buffers, comparação com Queues e critérios de escolha

FreeRTOS: Stream Buffers, comparação com Queues e critérios de escolha

Stream Buffers vs Queues: comparação técnica detalhada e critérios de escolha

A escolha entre Stream Buffers e Queues no FreeRTOS não é apenas uma decisão de API, mas uma decisão arquitetural, que influencia diretamente desempenho, previsibilidade temporal, uso de memória e clareza do modelo de software. Embora ambos sejam mecanismos de comunicação entre tarefas, eles partem de pressupostos completamente diferentes sobre a natureza dos dados trafegados no sistema.

Do ponto de vista conceitual, a queue trata dados como mensagens atômicas, enquanto o stream buffer trata dados como um fluxo contínuo de bytes. Essa diferença impacta todo o comportamento do sistema. Em uma queue, o receptor sempre obtém exatamente um item completo por vez; em um stream buffer, o receptor obtém “o que estiver disponível”, até o limite solicitado. Isso torna o stream buffer naturalmente mais adequado para protocolos de comunicação, recepção serial e pipelines de dados.

Comparação estrutural

CaracterísticaQueueStream Buffer
Modelo de dadosItens discretosFluxo contínuo de bytes
Tamanho do elementoFixoVariável
Cópia de dadosSempre copia o itemCopia apenas bytes escritos
Múltiplos produtoresSimNão (1 escritor)
Múltiplos consumidoresSimNão (1 leitor)
Uso típicoEventos, comandos, estadosUART, áudio, sensores, streams
Overhead de CPUMédio a altoBaixo
Fronteiras de mensagemPreservadasNão preservadas

Essa tabela deixa claro que nenhum dos dois mecanismos é “melhor” de forma absoluta. O que existe é adequação ao problema.

Critério 1: natureza do dado

Se o dado que você está transferindo possui semântica própria, como um comando, uma estrutura de estado ou um evento, a queue é a escolha correta. Forçar esse tipo de dado em um stream buffer geralmente resulta em código mais complexo, exigindo protocolos manuais de empacotamento e desempacotamento.

Por outro lado, se o dado representa uma sequência contínua, como bytes vindos de uma UART, amostras de ADC ou dados de rede, o stream buffer se encaixa naturalmente. Ele elimina a necessidade de dividir artificialmente o fluxo em mensagens fixas, reduzindo código, latência e consumo de CPU.

Critério 2: desempenho e latência

Em sistemas onde o tempo de resposta é crítico, especialmente em microcontroladores de menor porte, o custo de cópia das queues pode se tornar um gargalo. Um stream buffer minimiza esse custo, copiando apenas os bytes efetivamente transmitidos e evitando operações redundantes.

Esse ganho é particularmente visível quando o produtor é uma ISR. O uso de xStreamBufferSendFromISR() permite transferir dados para uma tarefa com impacto mínimo no tempo de interrupção, algo essencial em sistemas com múltiplas fontes de interrupção ou requisitos de hard real-time.

Critério 3: topologia produtor–consumidor

Se o sistema exige múltiplos produtores ou consumidores, a queue é praticamente obrigatória. Os stream buffers não oferecem garantias de segurança nesse cenário, e tentar contornar essa limitação com mutexes ou outras proteções geralmente elimina suas vantagens de desempenho.

Em contrapartida, quando a topologia é claramente um produtor para um consumidor, como uma UART RX → parser de protocolo, o stream buffer oferece uma solução mais simples, eficiente e elegante.

Exemplo comparativo: UART RX

Usando Queue (modelo menos eficiente):

void USART_IRQHandler(void)
{
    uint8_t byte;
    BaseType_t xHigherPriorityTaskWoken = pdFALSE;

    byte = USART_ReadByte();

    xQueueSendFromISR(
        xQueue,
        &byte,
        &xHigherPriorityTaskWoken
    );

    portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
}

Aqui, cada byte é tratado como uma mensagem individual, gerando alto overhead.

Usando Stream Buffer (modelo ideal):

void USART_IRQHandler(void)
{
    uint8_t byte;
    BaseType_t xHigherPriorityTaskWoken = pdFALSE;

    byte = USART_ReadByte();

    xStreamBufferSendFromISR(
        xStream,
        &byte,
        1,
        &xHigherPriorityTaskWoken
    );

    portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
}

Nesse caso, o byte é simplesmente inserido no fluxo, e a tarefa consumidora decide quando e quanto ler.


0 0 votos
Classificação do artigo
Inscrever-se
Notificar de
guest
0 Comentários
mais antigos
mais recentes Mais votado
Feedbacks embutidos
Ver todos os comentários

Related Post

FreeRTOS na Prática: Threads, Semáforos, Filas, Mutexes, Timers e Boas Práticas em Sistemas EmbarcadosFreeRTOS na Prática: Threads, Semáforos, Filas, Mutexes, Timers e Boas Práticas em Sistemas Embarcados

Este artigo faz parte de uma série técnica semanal sobre FreeRTOS e apresenta, de forma didática e rigorosa, os principais conceitos de concorrência e sincronização em sistemas embarcados. São explicados

0
Adoraria saber sua opinião, comente.x