Fractal de Mandelbrot Paralelo — usando Golang
Este artigo demonstra uma implementação do Fractal de Mandelbrot usando do poder das Goroutines de Go para a realização dos cálculos do conjunto de forma paralela.
O repositório pode ser acessado aqui: https://github.com/daniellferreira/parallel-mandelbrot-go
O que é o Fractal de Mandelbrot?
Para entender um pouco mais o que é esse tal Fractal de Mandelbrot você pode dar uma olhada aqui.
Mas em resumo o cálculo da cor correspondente as coordenadas x e y se baseia em:
x = x*x - y*y + ay = 2*x*y + b
Por questões de performance nós também verificamos se x² + y² > 4 para não realizar os demais cálculos em pontos que não se enquadram no conjunto de Mandelbrot.
O restante do processo matemático não faz tanto sentido explicar aqui, o mais legal está em como usamos as goroutines e o resultado.
O Algoritmo
Nosso programa baseia-se em 4 parâmetros principais que impactarão diretamente no resultado.
maxIter = 1000
samples = 200numBlocks = 64
numThreads = 16
- maxIter: define quantas vezes a equação de Mandelbrot será executada para cada ponto.
- samples: define o número de iterações para melhorar a definição da cor RGB (isso é extremamente custoso).
- numBlocks: define em quantos blocos será dividida a imagem (número de trabalhos a serem executados pelas threads/goroutines).
- numThreads: define o número de goroutines que serão criadas para processar o buffer de trabalhos.
Para renderizar o resultado dos trabalhos sendo executados, nós optamos por usar a biblioteca Pixel (https://github.com/faiface/pixel) que nos permitiu controlar melhor o main loop de renderização. Na nossa função main temos algo assim:
Como a janela precisa rodar na thread principal e ela toma o controle da nossa thread principal, precisamos passar por referência uma função que será responsável por gerir o início do programa:
A função run() neste caso toma o lugar do que seria nosso main, nela inicializamos as variáveis usadas no decorrer do código, a janela de renderização, os channels que controlam os buffers, as goroutines iniciais e o main loop que atualizará o conteúdo da janela.
O workBuffer é responsável por armazenar as coordenadas dos trabalhos a serem executados pelas goroutines. Cada trabalho equivale a um bloco, portanto, o valor de numBlocks que foi definido anteriormente será a quantidade de trabalhos a serem executados.
Na função workBufferInit nós definimos cada trabalho que será realizado e colocamos cada item com suas coordenadas iniciais e finais de x e y em uma posição do buffer. Esperamos em numBlocks um número que tenha uma raiz exata para realizar a computação em uma matriz quadrada.
O threadBuffer é responsável por definir as goroutines que realizarão o trabalho pesado. O controle do número de threads e do start dos trabalhos está dentro da função workersInit.
Primeiro inicializamos um channel a partir do número de threads definido em numThreads e posteriormente, varremos este channel de forma que uma iteração somente ocorrerá caso exista alguma posição pendente no buffer (a lógica de conclusão de uma goroutine que vai liberar uma iteração no for fica ao final da função workerThread).
A função workerThread realiza as operações do Fractal de Mandelbrot a partir do workItem recebido, coloca o resultado no drawBuffer (responsável por armazenar os pixels que serão renderizados na tela) e ao final do processamento coloca um true no threadBuffer (essa ação que vai liberar uma nova iteração no for acima).
Algumas funções que auxiliam na computação do fractal e na definição da cor do pixel:
Por fim, o drawBuffer é responsável por armazenar os pixels prontos para serem renderizados na tela. Seguindo a mesma lógica do for que fazemos no buffer de threads, em uma goroutine responsável apenas por desenhar na tela (drawThread), nós iteramos o drawBuffer de forma que cada vez que um pixel é computado nós atualizamos a tela.
E o resultado final é este:
Resumindo, conforme as goroutines são concluídas, novas são instanciadas e seguem processando os blocos até que toda a imagem esteja renderizada na tela.
Espero que este artigo possa ter te ajudados de alguma forma! 🚀
Este programa foi um collab meu e da Gisela, segue o link para o artigo irmão (gringo) desse aqui: https://giselamirandadifini.com/parallel-mandelbrot-set-using-golang