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ística | Queue | Stream Buffer |
|---|---|---|
| Modelo de dados | Itens discretos | Fluxo contínuo de bytes |
| Tamanho do elemento | Fixo | Variável |
| Cópia de dados | Sempre copia o item | Copia apenas bytes escritos |
| Múltiplos produtores | Sim | Não (1 escritor) |
| Múltiplos consumidores | Sim | Não (1 leitor) |
| Uso típico | Eventos, comandos, estados | UART, áudio, sensores, streams |
| Overhead de CPU | Médio a alto | Baixo |
| Fronteiras de mensagem | Preservadas | Nã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.