Ring Buffer – Antecipe, otimize e evite custos excessivos
Publicado em: domingo, 5 de jul de 2020

Ring Buffer, também chamado de Circular Buffer é uma estrutura de dados muito poderosa. Seu nome já traz o spoiler e entrega o ouro, afinal não deixa de ser um buffer, só que trabalhando em formato de anel/circular. Se você não faz ideia do que seja, vem comigo nessa viagem pois vamos dissecar o assunto e ainda compará-la ao processo de uberização dos objetos custosos.

Entendendo a estrutura

Ring Buffer poderia ser considerado o Uber das estruturas de dados. Trata-se de um buffer em estrutura circular ou de anel (ring). Tem ligeiras semelhanças com uma fila (FIFO lembra?), no entanto, ao invés de somente removermos o primeiro elemento da estrutura no momento do consumo, também devolvemos após seu uso, entrando no final do ring e ficando disponível para ser consumido novamente. É essa forma de trabalhar que se assimila com algo circular, um anel, daí o nome circular/ring buffer.

Essa coreografia é a cereja do bolo quando pensamos no emprego de ring buffers. Tem potencial para aumentar substancialmente sua capacidade de processamento e throughput, se usado da forma certa, podendo ser aplicado a alguns poucos cenários, mas que são absurdamente comuns e cotidianos e seu emprego pode produzir um enorme resultado positivo.

Uber vs Ring Buffer

Se você já usou Uber ou Taxi será capaz de entender relativamente rápido o poder do Ring Buffer, pois o Uber é exatamente uma implementação de Ring Buffer do mundo real, seus carros são os elementos do Ring.

Você não se preocupa em encontrar as chaves do carro 1, da mesma forma como não se preocupa com o tempo que leva para ligar o carro, tirá-lo da garagem, esperar o ar condicionado chegar à temperatura desejada, avaliar se há combustível, enfim, se está tudo pronto para sua viagem 2 . Você pede um carro 3, e recebe um carro pronto 4, com um motorista 5, e talvez precise esperar um pouco até que algum esteja disponível 6. Na medida que você acabou de usar o serviço, simplesmente deixa o carro seguir e atender outros passageiros 7.

  1. Acoplamento – Você não precisa saber como criar ou o que é necessário para criar uma instância do teu objeto. você simplesmente pede uma instância de um objeto disponível para uso.
  2. Warm Up e tempo de Inicialização são desprezados na medida que já estão disponíveis para uso.
  3. Semelhante a pedir um objeto do Buffer.
  4. Da mesma forma como se o objeto estiver disponível, será entregue para você.
  5. No Ring Buffer é comum haver um manager para gerenciar a alocação e principalmente a devolução da instância para o buffer.
  6. Da mesma forma como acontece quando todo o buffer está em uso.
  7. É a hora em que o manager libera o item do buffer para ser reaproveitado por outro consumidor.

Se você entendeu, ótimo, você sabe o que é um ring buffer ou um buffer circular.

Não fez sentido ainda? Precisa de um exemplo de uso?

Tudo bem, um grande problema de boa parte das grandes boas soluções em software é esse: Elas não ressaltam aos olhos à primeira vista. Se você não for perspicaz e estiver atento, ignorará solenemente aquele conceito, pois não enxerga utilidade prática.

Se você se identifica essa frase, fique mais atento, há ouro nas coisas mais simples que você possa imaginar.

Ring Buffer é muito usado em:

  • Conexões
    • SGDB’s
    • Message Brokers
    • Streaming Platforms
    • gRPC
    • Socket
  • Alocação de threads (Thread Pool)
  • Manipulação de arquivos
  • Encoding de Mídia

Um tipo de objeto é candidato a usar um ring buffer quando:

  • quando sua construção e inicialização custa caro demais.
  • quando o objeto bufferizado pode ser reaproveitado por diversos consumidores (em momentos distintos).
  • quando o custo da alocação prévia e manter esse recurso no buffer é desprezível ou irrelevante em função de seus benefícios.

Se você disse sim para todos os cenários acima, com certeza você é um forte candidato a usar ring buffers. Mas a análise se dá caso-a-caso.

Mas afinal, o que é um Ring Buffer?

É um buffer em forma de anel, ou seja cíclico.

Enquanto ao consumirmos uma fila, removemos o primeiro elemento no ato de consumo, reduzindo a quantidade de elementos da fila, sem nunca retroalimentá-la, em um ring buffer o que temos é um conjunto finito de elementos que são entregues um-a-um para cada consumidor, e após seu consumo esse elemento volta para o ring para ser reaproveitado por outro consumidor do ring.

A analogia com o Uber vem exatamente da tentativa de mostrar de forma clara como podemos ver isso no nosso dia-a-dia.

Mas porque você veio falar disso agora?

No Docker Definitivo, enquanto na turma 1 e 2 estamos lidando com Microsserviços, criando um microsserviço de pagamento, na turma 3 ainda estamos falando do Youtube Downloader. E surgiu a necessidade de fazer update do projeto. Ao realizar esse update para o .NET 5, surgiu uma questão: Coisas internas do client do RabbitMQ mudaram e fiquei órfão da minha implementação de pipeline, que não é mais compatível com a versão mais recente do client. Outro ponto é que eu tenho um passivo nessa abstração sobre o RabbitMQ que dura 6 anos.

Resolvi desacoplar tudo e criar como eu queria ter criado desde o início.

Uma das demandas para a integração com o RabbitMQ é suportar um throughput elevado, independente de quanto esse número alcance. A intenção é que a implementação seja um facilitador, não um limitador, por isso, poder trabalhar com Buffers de Models e Connections faz todo sentido como requisito.

Ao mesmo tempo um amigo, havia sinalizado uma questão que envolvia um custo muito grande pra publicar cada mensagem (depois ele me disse que era latência), mas quando ele me procurou eu saí atirando para todos os lados citando os:

Ofensores

  • Latência
  • Rede (instabilidade)
  • Custo de criação da conexão e handshake
  • Custo de criação do canal/model
  • Roteamento
  • Tamanho da mensagem
  • Tamanho da fila
  • Quantidade de conexões
  • Quantidade de canais/models
  • Persistência
  • I/O e disco

Enfim, tudo que me vinha à cabeça como ofensor naquela hora.

E falei de algumas estratégias de mitigação:

  • Cache de Mensagens grandes
  • Trafegar somente ID’s de controle
  • Pré-alocação de Connections
  • Pré-alocação de Canais/Models
  • Ambos remetem a ⭐Ring Buffers⭐, que foi dito também
  • Aproximação dos servidores
  • Revisão das rotas de rede
  • Revisão da saúde e performance dos discos

Daí magicamente os assuntos se casaram. Eu tinha uma demanda por causa do Oragon que demandava uma solução por conta do Youtube Downloader. Lembrei que em todos esses anos eu nunca havia tocado no assunto, nem superficialmente. Então resolvi dar atenção a todos esses aspectos de uma vez só.

Então comecei um projeto no github, que ainda é um esboço. Fique de olho nos próximos posts, em breve surge novidade sobre o projeto. De cara eu fiz um teste de alocação de Connections e Canais/Models com RabbitMQ. Ou seja, eu criei connections e models mas não os usei para nada. Sequer publiquei 1 mensagem. A intenção desse teste é comparar a estratégia default, alocando recursos por demanda. Aliás é uma ótima estratégia e por isso é a estratégia default.

Mas no nosso caso, eu estava buscando reduzir os cursos de conexão e estabelecimento de um canal pronto para uso, trocando alocação dinâmica por pré-alocação, o que produz um custo também.

Benchmark

Ainda estou trabalhando para ter um benchmark não viciado, problema da maioria dos benchmarks. Nesse momento os números que eu tenho aqui são absurdos e discrepantes demais, numa ordem de grandeza muito maior que o esperado. Eu sinceramente acredito que tenha erros na forma como estou produzindo esse benchmark, portanto sequer vou divulgar agora. Mas eu comentei sobre ele no telegram.

Que tal amanhã mesmo mudar seu projeto em produção?

A primeira coisa a se notar é: Não faça otimizações prematuras.

Se você não sabe que tem um problema, ou você avalia se há um problema para diagnosticar e assim tratar (e ringbuffer pode te ajudar) ou você não tem um problema.

Buscar mais performance só faz sentido se você percebe que está faltando performance.

Mais performance não é um bom motivo em si. Afinal, você não está usando go, c, c++….

Sair alterando seus projetos sem um motivo plausível, é amadorismo.

Aliás, existem poucas implementações de ringbuffer thread safe aqui no mundo .NET. Eu estou trabalhando em uma, mas ainda não estou tão perto de finalizar.

Conclusão

Circular Buffer é uma estratégia que tem tradeoffs. Manter conexões por longos períodos pode não ser eficiente no teu cenário. Você precisa levar isso em conta.

Lembra que a estratégia que estou usando pré-aloca conexões de rede e canais. Se sua aplicação escalar horizontalmente, tenha em mente que pode alcançar o limite do teu serviço/servidor. Isso em geral acontece do lado do servidor para o qual sua conexão está estabelecida.

Tendo um problema que justifique, faz todo sentido usar Ring Buffer é uma estratégia bem válida, muito útil e com potencial para entregar performance extrema. Mas não é para todo mundo, nem é necessária em todos os casos.

Luiz Carlos Faria

Mensagem do Autor

Espero que goste desse post. Não deixe de comentar e falar o que achou. 

Se acha que esse post pode ajudar alguém que você conheça, compartilhe!

 

Próximos Eventos

2020 agosto

  • Sem eventos
  • Sem eventos
  • Sem eventos
  • Sem eventos
  • Sem eventos
  • Sem eventos
  • Sem eventos
  • Sem eventos
  • Sem eventos
  • Sem eventos
  • Sem eventos
  • Sem eventos
  • Sem eventos
  • Sem eventos
  • Sem eventos
  • Sem eventos
  • Sem eventos
  • Sem eventos
  • Sem eventos
  • Sem eventos
  • Sem eventos
  • Sem eventos
  • Sem eventos
  • Sem eventos
  • Sem eventos
  • Sem eventos

Publicidade

Assine

0 comentários

Enviar um comentário

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *

Esse site utiliza o Akismet para reduzir spam. Aprenda como seus dados de comentários são processados.

Lives

Fique de olho nas lives

Fique de olho nas lives no meu canal do Youtube, no Canal .NET e nos Grupos do Facebook e Instagram.

Aceleradores

Existem diversas formas de viabilizar o suporte ao teu projeto. Seja com os treinamentos, consultoria, mentorias em grupo.

Agenda & Eventos

julho

agosto 2020

setembro
DOM
SEG
TER
QUA
QUI
SEX
SAB
26
27
28
29
30
31
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
1
2
3
4
5
Eventos para agosto

1st

Eventos para agosto

2nd

Eventos para agosto

3rd

Sem Eventos
Eventos para agosto

4th

Sem Eventos
Eventos para agosto

5th

Sem Eventos
Eventos para agosto

6th

Sem Eventos
Eventos para agosto

7th

Sem Eventos
Eventos para agosto

8th

Sem Eventos
Eventos para agosto

9th

Sem Eventos
Eventos para agosto

10th

Sem Eventos
Eventos para agosto

11th

Eventos para agosto

12th

Sem Eventos
Eventos para agosto

13th

Eventos para agosto

14th

Sem Eventos
Eventos para agosto

15th

Sem Eventos
Eventos para agosto

16th

Sem Eventos
Eventos para agosto

17th

Eventos para agosto

18th

Sem Eventos
Eventos para agosto

19th

Sem Eventos
Eventos para agosto

20th

Sem Eventos
Eventos para agosto

21st

Sem Eventos
Eventos para agosto

22nd

Sem Eventos
Eventos para agosto

23rd

Sem Eventos
Eventos para agosto

24th

Sem Eventos
Eventos para agosto

25th

Sem Eventos
Eventos para agosto

26th

Sem Eventos
Eventos para agosto

27th

Sem Eventos
Eventos para agosto

28th

Sem Eventos
Eventos para agosto

29th

Sem Eventos
Eventos para agosto

30th

Sem Eventos
Eventos para agosto

31st

Sem Eventos
Share This