Olá pessoal. Tudo certo?!
Como vocês devem ter notado, a frequência dos posts aqui no blog diminuiu bastante. Infelizmente, alguns compromissos (não técnicos) estão me tomando mais tempo do que eu esperava. Com o tempo, retomarei o ritmo habitual.
Nesse post, continuo falando sobre as novidades do Framework 4.5. Hoje, falo sobre a TPL Dataflow. Trata-se de mais um esforço da Microsoft para tornar a programação concorrente mais fácil.
Usando a definição que está no MSDN:
The Task Parallel Library (TPL) provides dataflow components to help increase the robustness of concurrency-enabled applications. These dataflow components are collectively referred to as the TPL Dataflow Library. This dataflow model promotes actor-based programming by providing in-process message passing for coarse-grained dataflow and pipelining tasks. The dataflow components build on the types and scheduling infrastructure of the TPL and integrate with the C#, Visual Basic, and F# language support for asynchronous programming.
Trata-se de uma nova biblioteca .NET que provê um conjunto de componentes (primitivas) para gestão de mensagens in-process, dataflow e pipelining.
TPL Dataflow está sendo distribuída de forma independente, através de um pacote NuGet. Basta procurar por Microsoft.Tpl.Dataflow.
Se você quer começar a aprender TPL Dataflow, recomendo que faça isso pelo componente ActionBlock.
Vejamos um exemplo simples:
Aqui você vê o ActionBlock em uso. Trata-se de um target que recebe e “enfileira” mensagens para que sejam processadas. Considere:
Abaixo, você o resultado da execução do programa.
Por default, todas as Tasks são executadas em sequência. Para mudar esse comportamento, podemos criar um objeto de configuração como indicado abaixo:
Pegou a ideia?!
A qualquer momento, novas mensagens podem ser enfileiradas. Quando desejamos garantir que nenhuma mensagem seja processada, chamamos o método Complete.
Certo!?
TransformBlock é outro bloco provido pela TPL Dataflow. Seu propósito é “alterar” dados – semelhante ao map da programação funcional. Veja o exemplo que segue:
Pegou a ideia?! TransformationBlock recebe mensagens, transforma e encaminha. No exemplo, recebo strings, transformo e “encaminho” para a ActionBlock que foi “ligada”.
Deixando a coisa mais interessante, veja:
Imaginou as possibilidades?
BufferBlock armazena dados. Ele faz um “balanceamento” para outros blocos. Veja meu exemplo:
Cada block, aqui, apresenta desempenho diferente. Veja a execução:
Acho que ficou claro. Não?!
Há alguns outros blocos, mas acho que serviu para você pegar a ideia.
Ficou interessado?! Quer saber mais?! Dê uma olhada na página de referências.
Era isso.
Hum legal! isso vai cair como uma luva no nosso projeto.
Ola Emerson, conseguiu implementar legal ai? Por curiosidade, qual é o trabalho que você vai usar este modelo? Vlw!!
Parabéns pelo post Elemar, já escrevi algo sobre Dataflow programming para versões anteriores do framework!
http://rafaelzaccanini.net/2012/07/06/dataflow-programming-com-c-criando-variaveis-e-operacoes/
Seu post ficou show! abs.
Puts to com Moral, fui citado ai no código!
Pingback: SNIPPET: Usando TPL Dataflow para orquestrar a descoberta de links de uma página « Elemar DEV
Elemar, sabe me dizer se tem como saber quanto de MaxDegreeOfParallelism é usado em um código que usa TPL?
Ouvi dizer por ai que MaxDegreeOfParallelism depende uma porção de coisas no momento atual do sistema.
Mas o que eu realmente gostaria de fazer é em vez de diminuir o MaxDegreeOfParallelism para um valor N qualquer, eu gostaria de diminuir em função do valor que o OS/CPU suporta.
Ex:
Suponha 8 cores de CPU e que o default neste momento é 100 degree of parallelism sendo que estes 100 vai fazer o OS vai funcionar no gargalo do CPU fazendo acontecer alguns travamentos, para não desonerar tanto o desempenho atual do PC para que o usuário continue trabalhando sem lentidões eu gostaria de diminuir a 50% este valor sendo entao MaxDegreeOfParallelism = 50 e não apenas MaxDegreeOfParallelism = 4.
A pergunta é, como saber usar o MaxDegreeOfParallelism a nível de porcentagem? Pois por constantes qualquer a gente nunca vai saber se a constante 4 é muita ou pouca pra aquele PC no instante.
Obrigado!!
Olá,
Não conheço formas bacanas de indicar percentualmente o nível de paralelismo. Entretanto, acredito que o propósito dessa propriedade não é aquele que você está relatando.
MaxDegreeOfParalelism, IMHO, deve ser utilizado com certos algoritmos que tem desempenho ótimo com um número determinado de operações ocorrendo em paralelo. Principalmente devido ao fato de que o overhead de gestão de mais threads não compensaria o resultado computacional.
Geralmente, esse número é constante mesmo e não varia conforme plataforma operacional.
Quanto a “estabilidade” do sistema operacional durante processamentos pesados, trata-se de uma responsabilidade do SO
O que você acha?
[]s
Elemar Jr
O valor default do MaxDegreeOfParallelism é -1 que leva ao OS a decidir pelo teste que andei fazendo e pelo que li por ai.
Se colocar um numero alto, dizem por ai que nem por isto irá obedecer conforme a capacidade da plataforma, então eu vejo esta propriedade como um valor base apenas. Até ai tudo bem!
E falando da “estabilidade”, sim, é de responsabilidade do OS, so que é o seguinte, ele estima em um nível a deixar deixar a CPU sempre ocupada sem pouca ou nenhuma ociosidade onde o usuário a qualquer momento pode requisitar uma operação mais pesada como assistir um video em HD e o OS terá de levar alguns segundos para reequilibrar e o usuário perceberia isto facilmente.
Para evitar isto imagino eu o ideal é avisar a TPL a usar apenas 50% – 70% da ociosidade do processador deixando o restante pronto uso imediato para outras tarefas, desta forma não aconteceria a reajustada que acontece hoje se não especificarmos o MaxDegreeOfParallelism para um valor mais baixo so que ai entraremos em outra questão que é a de não ser se estimamos o valor para tão baixo.
Por esta questão que eu levando não existir, acredito que seja por conta de inviabilidade técnica da TPL para não existir esta função.
Talvez é questão de tempo para implementarem ou talvez nunca a implementem.
Eu questiono tudo isto porque estou implementando um grid, onde amigos instalariam um agente em suas respectivas máquinas, e se uma tarefa usar TPL/multi-thread para uma atividade paralela e me questionarem de lentidão mesmo sobre um plano de execução estratégico( ex: executar apenas quando houver ociosidade e etc…) eu teria de pensar N vezes em decidir usar TPL. Entendes?!!
Algo mais a acrescentar?
Valew Elemar!!!
Parece estar havendo uma confusão fundamental aqui. MaxDegreeOfParalelism indica quantas operações devem acontecer simultaneamente. Apenas isso.
Não tem nada haver com a ocupação da unidade de processamento.
Pois então, é isto mesmo, só que quanto mais operações acontecem simultaneamente, mais ocupado a CPU estará, não concorda?!
Dado um algoritmo X onde se executado 10 vezes em um período de 5 minutos, vai deixar a CPU no gargalo por 5 minutos.
Mas de for executado apenas 4 vezes ainda no período de 5 minutos a CPU não mais ficará no gargalo, estou pesando errado? uma coisa não influenciará a outra?
Aparentemente, você está equivocado. Paralelismo real só ocorre quando você tem mais de uma unidade de processamento.
Quando você tem apenas uma unidade, o que ocorre é a transição entre uma thread e outra (gerenciado pelo SO).
O SO tem um intrincado sistema de prioridades para determinar quanto cada processo tem de tempo do CPU.
Percebe?
Sim, percebo, já vi a respeito deste escalonamento de threads.
Mas o que me deixa com dúvida no nosso exemplo tomando como premissa chips com mais de um core como os processadores Intel multi core. Como nós desenvolvedores tomariam uma sábia politica de uso destas unidades de processamento sem chegar perto do limite se quer por alguns segundos?
Não tem jeito de estimar números de operações máxima pelo estado atual da plataforma sem gerar pequenos travamentos?
Eu fiz um teste calculando números primos na casa dos 100 milhões usando 4 cores + 4 virtuais( hyperthreading da intel ) um total de “8 cores”.
Usei a função Interlocked.Increment para contar o numero de thread usando pelo Parallel.For, sem setar a propriedade MaxDegreeOfParallelism chegavam a 10 a 12 threads, mas também esquece, eu poderia parar de usar o OS, ficava muito lento. Mudei a propriedade para 4, beleza, todos os “8 cores” começaram a operar 50% em média apenas e o OS continuou respondendo tranquilamente.
Dai me isto me leva uma situação onde nunca saberei o MaxDegreeOfParallelism que deverei usar pra cada plataforma. Será que pra responder esta pergunta eu terei de sempre rodar um teste de benchmark?
Complicado rs… é um pouco confuso pra mim.
Obrigado pelas respostas!!