Para comunicação entre tarefas ou para compartilhar dados entre duas tarefas do RTOS, usamos Filas. Mas o problema com as filas é que elas são lentas ... Elas copiam dados no buffer, depois no Mutex Handling e na transferência de dados. É irritantemente lento se você precisar transferir grandes dados. Outro problema é se a mesma fila for acessada por várias tarefas. Em seguida, a imagem fica assim: - Primeiro, aguarde para acessar a fila e, em seguida, na fila Processamento interno de mutex e depois na transferência de dados.
Isso aumenta a sobrecarga no sistema. Qual poderia ser o substituto eficiente para filas?
(Acho que essa pergunta é independente do RTOS que usamos. A maioria dos RTOS manipula filas somente dessa maneira)
Respostas:
As filas operam dessa maneira porque esse é um modelo de transação seguro para threads para comunicação entre tarefas. Você corre o risco de corrupção de dados e / ou problemas de propriedade em qualquer esquema menos rigoroso.
Você está copiando os dados em um buffer na memória e passando um ponteiro com os elementos da fila ou tentando passar todos os dados nos próprios elementos da fila? Se você não estiver passando ponteiros, obterá um aumento no desempenho fazendo isso, em vez de passar um byte de cada vez através dos elementos da fila.
fonte
Uma maneira fácil é colocar um ponteiro para os dados na fila e consumir os dados usando o ponteiro.
Observe que você está negociando segurança por desempenho dessa maneira, pois precisa se certificar de que:
Se você não estiver usando memória alocada dinamicamente, não precisará desalocá-la, mas precisará garantir que a área de memória não seja reutilizada antes que os dados sejam consumidos.
fonte
As filas sem bloqueio podem ser implementadas para o caso de produtor único / consumidor único, e muitas vezes você pode arquitetar seu software para minimizar o número de filas de múltiplos produtores ou vários consumidores.
Uma fila sem bloqueio pode ser construída da seguinte forma: aloque uma matriz dos elementos a serem comunicados e também dois números inteiros, chamados Head e Tail. Head é um índice na matriz, onde o próximo item será adicionado. Tail é um índice na matriz, onde o próximo item está disponível para remoção. A tarefa do produtor lê H e T para determinar se há espaço para adicionar um item; grava o item no índice H e atualiza H. As tarefas do consumidor lê H e T para determinar se há dados disponíveis, lê dados do índice T e atualiza T. Basicamente, é um buffer de anel acessado por duas tarefas e o A ordem das operações (insira, atualize H; remova e atualize T) garante que a corrupção de dados não ocorra.
Se você tiver uma situação com vários produtores e um único consumidor, ou um único produtor e vários consumidores, terá efetivamente algum tipo de limitação de recurso e não há mais nada além de usar a sincronização, pois é mais provável que o limitador de desempenho ser o único produtor / consumidor que uma sobrecarga do SO com o mecanismo de bloqueio.
Mas se você tem vários produtores e consumidores, vale a pena gastar tempo (no espaço de design) para ver se você não consegue um mecanismo de comunicação mais coordenado; em um caso como esse, serializar tudo através de uma única fila definitivamente torna a eficiência da fila o determinante central do desempenho.
fonte
Pode-se obter uma operação eficiente em uma fila de um único produtor sem bloqueios se a própria fila contiver itens pequenos o suficiente para trabalhar com uma primitiva exclusiva para carregamento de cargas, troca de comparação ou similar e pode-se usar um valor reservado ou valores reservados para um slot de fila vazio. Ao escrever na fila, o gravador faz uma troca de comparação para tentar armazenar seus dados no próximo slot vazio; se isso falhar, o gravador tenta o seguinte slot. Embora a fila mantenha um ponteiro para o próximo slot vazio, o valor do ponteiro é "consultivo". Observe que, se um sistema usa troca de comparação em vez de exclusivo da loja de carregamento, pode ser necessário ter uma 'família' de valores diferentes de 'espaço vazio'. Caso contrário, se entre o tempo em que o gravador encontrar um slot de fila vazio e tentar gravar nele, outro escritor escreve o slot e o leitor lê, o primeiro escritor, sem saber, colocava seus dados em um local onde o leitor não os via. Esse problema não ocorre em sistemas que usam exclusivo de armazenamento de carga, pois o exclusivo de armazenamento detectaria que os dados foram gravados, mesmo tendo sido gravados novamente no valor antigo.
fonte
Você pode acessar as filas com mais eficiência escrevendo na parte superior da fila. Normalmente, a maior parte do RTOS oferece suporte para adicionar à frente da fila, o que não requer a aquisição de mutex. Mas certifique-se de usar a adição à frente da fila o mínimo possível, onde você deseja executar os dados mais rapidamente. Normalmente, as estruturas de filas têm limite máximo de tamanho, portanto, você não pode colocar todos os dados na fila, portanto, passar o ponteiro é sempre fácil.
Felicidades!!
fonte
Filas não são inerentemente lentas. A implementação deles pode ser.
Se você estiver copiando dados às cegas e usando uma fila síncrona, verá um desempenho atingido.
Como outros pôsteres indicaram, existem alternativas sem bloqueio. O caso de produtor único / consumidor único é direto; para vários produtores e consumidores, o algoritmo de fila sem bloqueio de Michael e Scott (esses são os sobrenomes) é o padrão e é usado como base para o ConcurrentLinkedQueue do Java .
É possível otimizar a necessidade de filas em certos casos, mas eles fornecem garantias de simultaneidade que geralmente fornecem enormes benefícios de simplificação para os sistemas, permitindo dissociar tarefas.
fonte