Criar rotinas que executem métodos em paralelo e analisar as diferenças entre a execução sem paralelismo dos mesmos métodos.
Nota: para saber mais sobre o conceito de paralelismo, visite o outro artigo do blog: Conceito: Programacao Paralela
Exemplos
Para os códigos abaixo, considere que foi criado um novo projeto do tipo Console Application, com o import/using do namespace System.Threading.Tasks.
Loop For
http://msdn.microsoft.com/pt-br/library/dd783539(v=vs.110).aspx
Utilizaremos o loop for para analisar a ordem das iterações e o consumo de CPU em cada situação.
static void Main(string[] args)
{
Console.Title = "Parallel";
Console.WriteLine("Loop For Comum");
for (int i = 0; i < 100; i++)
{
Console.Write(i.ToString() + "; ");
}
Console.WriteLine();
Console.WriteLine();
Console.WriteLine("Loop For Paralelo");
Parallel.For(0, 100, i =>
{
Console.Write(i.ToString() + "; ");
});
Console.ReadLine();
}
Os dois loops vão de 0 até 99, onde o primeiro é sequencial e o segundo é em paralelo, que não garante a ordem das iterações do loop.
Abaixo temos o output do código executado, mostrando que as iterações em loop comum são ordenadas e em paralelo podem ser desordenadas.
Abaixo temos a comparação de uso de CPU entre um loop for comum e um loop for em paralelo, podemos ver que o comum usa apenas um núcleo da CPU e o paralelo usa todos que estão disponíveis.
For Comum |
For Paralelo |
Parallel.Invoke
Utiliza-se o método Parallel.Invoke para executar um conjunto de métodos de forma paralela. Este método é bloqueante e aguarda todos os métodos serem executados para avançar para a próxima linha de código. Neste exemplo, vamos analisar e comparar o tempo de execução de um código.
static void Main(string[] args)
{
Console.Title = "Parallel";
DateTime dataInicio, dataFim;
DateTime dataInicioParallel, dataFimParallel;
dataInicio = DateTime.Now;
Esperar(1000);
Esperar(2000);
Esperar(3000);
dataFim = DateTime.Now;
Console.WriteLine();
dataInicioParallel = DateTime.Now;
List<Action> listaAcoes = new List<Action>();
listaAcoes.Add(() => Esperar(1000));
listaAcoes.Add(() => Esperar(2000));
listaAcoes.Add(() => Esperar(3000));
Parallel.Invoke(listaAcoes.ToArray());
dataFimParallel = DateTime.Now;
Console.WriteLine();
Console.WriteLine();
Console.WriteLine("Duração comum: " + (dataFim - dataInicio).TotalMilliseconds.ToString("F0") + " ms");
Console.WriteLine("Duração paralelismo: " + (dataFimParallel - dataInicioParallel).TotalMilliseconds.ToString("F0") + " ms");
Console.ReadLine();
}
static void Esperar(int milissegundos)
{
Console.WriteLine("Esperando: " + milissegundos.ToString() + " ms");
System.Threading.Thread.Sleep(milissegundos);
Console.WriteLine("Ok! - " + milissegundos.ToString() + " ms");
}
Output da execução do código:
O método Esperar() foi chamado três vezes, passando por parâmetro 1000, 2000 e 3000 em cada chamada, o que significa que ele iria esperar 1 segundo, depois 2 e depois 3.
Chamando o método as três vezes de maneira tradicional, temos que esperar 6 segundos para a execução total. Chamando paralelamente as três execuções do método, podemos ver que o tempo de execução caiu para 3 segundos; isso se dá pelo motivo que as três chamadas foram iniciadas ao mesmo tempo, ou seja, a contagem de tempo de cada execução do método foi iniciada no mesmo momento, não tendo que esperar o primeiro método acabar para só assim executar o próximo.
Conclusão
Não existem apenas essas duas possibilidades de se usar código em paralelo, esses casos foram citados apenas para servir de análise e comparação de um código comum e um código paralelo.
Não há como dizer se é melhor usar paralelismo ou se é melhor usar a maneira tradicional, pois é a necessidade que faz o uso de um código ou de outro se tornar viável para a aplicação.
Para saber como limitar o uso de CPU de uma rotina paralela, dê uma olhada no próximo artigo: Tutorial: Limitar uso de CPU do paralelismo
Nenhum comentário:
Postar um comentário