Jorge Paulino

Visual Basic em Português, MVP

Recent Posts

Tags



 
Administr. da Comunidade
Portugal-a-Programar


Membro da Comunidade
Experts-Exchange


Twitter
Artigos no CodeProject
Artigos no CodeProject 

Email Notifications

Archives

Março 2010 - Posts

VS2010 RC1: Task Parallel Library – Parallel.Invoke()

Nos artigos anteriores sobre a Task Parallel Library (TPL) foram referidos os métodos Parallel.For e Parallel.ForEach que servem essencialmente para criar ciclos que podem correr em paralelo. Este método, o Parallel.Invoke, serve para executar processos que decorrem eventualmente em paralelo.

Quando os processos são invocados, são separados nos diversos núcleos do processador e o código só avança quando todos tiverem terminado.

Eis um exemplo do funcionamento:

Imports System.Threading
Imports System.Threading.Tasks

Module Module1

  Private Sub task1()
    For x As Integer = 0 To 50
      Console.WriteLine("Task1 - {0}", x.ToString())
      System.Threading.Thread.SpinWait(50000000)
    Next
  End Sub

  Private Sub task2()
    For x As Integer = 0 To 50
      Console.WriteLine("Task2 - {0}", x.ToString())
      System.Threading.Thread.SpinWait(50000000)
    Next
  End Sub

  Sub Main()

    ' Chama as diversas acções (incluindo uma Lambda)
    Dim actions() As Action = {AddressOf task1, AddressOf task2,
                                 Sub()

                                   ' Usando uma Lambda Expression
                                   For x As Integer = 0 To 50
                                     Console.WriteLine("Task3 - {0}", x.ToString())
                                     System.Threading.Thread.SpinWait(50000000)
                                   Next

                                 End Sub}

    Parallel.Invoke(actions)

    Console.WriteLine("Acções completas!")

  End Sub

End Module

Executando o mesmo, mas sequencialmente (onde o tempo de execução é significativamente superior):

Imports System.Threading
Imports System.Threading.Tasks

Module Module1

  Private Sub task1()
    For x As Integer = 0 To 50
      Console.WriteLine("Task1 - {0}", x.ToString())
      System.Threading.Thread.SpinWait(50000000)
    Next
  End Sub

  Private Sub task2()
    For x As Integer = 0 To 50
      Console.WriteLine("Task2 - {0}", x.ToString())
      System.Threading.Thread.SpinWait(50000000)
    Next
  End Sub

  Sub Main()

    task1()
    task2()

    For x As Integer = 0 To 50
      Console.WriteLine("Task3 - {0}", x.ToString())
      System.Threading.Thread.SpinWait(50000000)
    Next

  End Sub

End Module

E é o terceiro e último evento da classe Parallel. Como se pode ver nos exemplos apresentados(For, ForEach e Invoke), são muito simples de utilizar e vai de certeza ajudar a melhorar o desempenho das aplicações.

VS2010 RC1: Task Parallel Library – Parallel.ForEach()

Este método executa um ciclo semelhante à instrução For Each, mas que possibilita a execução em paralelo. É muito semelhante ao exemplo do artigo anterior, mas difere do Parallel.For() pois é usado quando os dados são colecções ou arrays.

Eis um exemplo da sua implementação/utilização:

Imports System.Threading
Imports System.Threading.Tasks

Module Module1

  Sub Main()
    Dim result1, result2 As Long
    Dim values = Enumerable.Range(0, 49)

    Dim sw As New Stopwatch
    sw.Start()

    ' ------------------------------------------------
    ' Ciclo For Each
    ' ------------------------------------------------
    For Each value As Integer In values
      Console.WriteLine("Value:{0} GUI:{1}", value.ToString(), Guid.NewGuid())
      Thread.SpinWait(50000000)
    Next

    result1 = sw.ElapsedMilliseconds

    sw.Restart() ' <- novo método no Visual Studio 2010

    ' ------------------------------------------------
    ' Ciclo Parallel.ForEach
    ' ------------------------------------------------
    Parallel.ForEach(values, Sub(value)
                               Console.WriteLine("Value:{0} GUI:{1}",
                                                 value.ToString(),
                                                 Guid.NewGuid())
                               Thread.SpinWait(50000000)
                             End Sub)

    result2 = sw.ElapsedMilliseconds
    sw.Stop()

    Console.WriteLine("Tempo de execução 'For Each' {0} ms", result1)
    Console.WriteLine("Tempo de execução 'Parallel.ForEach' {0} ms", result2)
    Console.ReadKey(True)

  End Sub

End Module

E os resultados são uma vez mais significativos:

É mais um ciclo disponível da Task Parallel Library (TPL) – Parallel Extensions, que quando bem aplicado pode melhorar bastante o desempenho da aplicação.

VS2010 RC1: Task Parallel Library – Parallel.For()

Data parallelism refere-se a situações em que a mesma operação é realizada simultaneamente (ou seja em paralelo) em elementos de uma colecção ou array. A classe System.Threading.Tasks.Parallel, do namespace System.Threading.Tasks, fornece um conjunto de métodos que permitem a execução de ciclos. Com a utilização do Parallel.For, Parallel.ForEach e Parallel.Invoke, incluindo inúmeros overloads disponíveis, é possível melhorar o desempenho das aplicações.

O método Parallel.For executa um ciclo que pode correr em paralelo. O sintaxe é o seguinte: Parallel.For(Int32, Int32, Action(Of Int32))

Executando um ciclo simples ou “normal”:

      Dim sw As New Stopwatch()
      sw.Start()

      ' Executa o método For ... Next
      For x As Integer = 0 To 500
        Console.WriteLine("Thread ID={0} - x={1}",
                          Thread.CurrentThread.ManagedThreadId, x)
        Thread.Sleep(100)
      Next x

      Console.WriteLine("Tempo de execução: {0} ms", sw.ElapsedMilliseconds)
      sw.Stop()

Como é possível ver nos seguintes resultados, o tempo de execução é longo, e o valor da variável x que é mostrado é continuo. A thread é também sempre a mesma (neste caso 9).

Para realizarmos o mesmo ciclo, mas desta vez recorrendo às Parallel Extensions, e melhorando desta forma o desempenho, podemos usar o método Parallel.For. Neste exemplo é usado Lambda para mostrar os resultados:

      Dim sw As New Stopwatch()
      sw.Start()

      ' Executa o método Parallel.For()
      Parallel.For(0, 500, Sub(x)
                             Console.WriteLine("Thread ID={0} - x={1}",
                                               Thread.CurrentThread.ManagedThreadId, x)
                             Thread.Sleep(100)
                           End Sub)

      Console.WriteLine("Tempo de execução: {0} ms", sw.ElapsedMilliseconds)
      sw.Stop()

Como é possível nos seguintes resultado, o tempo de execução é inferior, sendo usadas diversas threads, que são distribuídas e executadas nos diversos núcleos do processador. Isto justifica também porque o valor de x não é mostrado de forma continua.

Os resultados falam por si, ma é importante lembrar, que num exemplo simples sem que existam tarefas complexas (que possam demorar algum tempo a executar), a utilização dos métodos Parallel.For ou Parallel.ForEach é mais lento do que um ciclo simples (For … Next ou For Each … Next). Isto porque em cada ciclo é criada um Delegate (System.Action) que irá ser chamado e isso irá aumentar o tempo de execução.

É por isto muito importante definir bem onde utilizar, desenhar a aplicação de modo a poder incluir a utilização de Parallel Extensions, neste caso as Task Parallel Library (TPL), e testar sempre (analisando os resultados)!

VS2010 RC1: Parallel Extensions

A evolução dos CPU’s deixou, nos últimos anos, de estar relacionada com a velocidade do CPU, mas sim com o número de núcleos que este tem. Estes núcleos (cores) permitem executar diversos processos em simultâneo, melhorando a utilização e performance das aplicações, sendo para isso necessário que elas estejam desenhadas e preparadas para o fazer.

O Visual Studio 2010 e a .NET Framework 4.0 trazem um conjunto de melhorias com o objectivo de tirar partido do hardware, nomeadamente melhorias no namespace System.Theading, a introdução das Parallel Extensions, que são um conjunto de classes que permitem implementar a programação de tarefas em paralelo e a Unified Cancellation Model, que é, de uma forma sucinta, uma simplificação do processo de cancelamento de diversas threads usando um só comando.

São também novidade duas ferramentas de diagnóstico: Parallel Stacks Window  e Parallel Tasks Window.

Sobre as Parallel Extensions, as principais novidades são:

O Visual Studio 2010 e a plataforma .NET Framework 4.0 vêm assim melhorar e simplificar o o processo de criação de threads, substituindo de certa forma, com as Parallel Extensions, a classe System.Theading.Thead (embora possível de utilizar).

VS2010 RC1: Dynamic Support

O Visual Studio 2010 introduz suporte para utilização de objectos de linguagens dinâmicas, como IronPython ou IronRuby, e a criação de objecto dinâmicos, dentro da própria linguagem (VB ou C#). Isto torna possível a inclusão de métodos dinâmicos em linguagens estáticas, como são o VB.NET ou C#, através da Dynamic Language Runtime (DLR).

 

No caso do Visual Basic, isto permite outra forma, que não usando o tipo Object, de se ligar a objecto dinâmicos em latebind, ou seja, sem que o compilador tente interpretar o código e dê erro, sendo o código interpretado em modo de execução(run-time). Mais informação e vantagens sobre ambos os métodos: Early and Late Binding

Desta forma podemos em alguns casos, com a devida manipulação é claro, utilizar um sintaxe mais agradável, ou seja, não fazer:

        Dim reader As SqlClient.SqlDataReader = command.ExecuteReader()
        Me.txtNome.Text = reader.Item("nome")
        Me.txtMorada.Text = reader.Item("morada")
        Me.txtTelefone.Text = reader.Item("telefone")

Mas sim algo mais simples (uma vez que em ambos os casos não temos intellisense):

        Dim reader As Object = New DynamicCommand(command)
        Me.txtNome.Text = reader.nome
        Me.txtMorada.Text = reader.morada
        Me.txtTelefone.Text = reader.telefone


Isto consegue-se utilizando a classe DynamicObject(), do namespace System.Dynamic, permitindo a criação de objectos dinâmicos. De uma forma geral, podemos criar uma nova classe, que herda as propriedades e métodos do DynamicObject(), e através dos métodos TryGetMember() e TrySetMember() podemos identificar se estamos a definir uma propriedade ou a verificar o valor de uma propriedade. Existem mais métodos disponíveis, mas para o seguinte exemplo, iremos apenas utilizar estes dois.

Este pequeno exemplo irá guardar uma propriedade e depois verificar o seu valor. Bastante simples apenas para se entender o funcionamento geral.

Definição da classe dinâmica:

Public Class DynamicObjectTest
  Inherits DynamicObject

  Private m_dictionary As New Dictionary(Of String, Object)


  ''' <summary>
  ''' Verifica no Dictionary o valor guardado
  ''' </summary>
  ''' <returns></returns>
  ''' <remarks></remarks>
  Public Overrides Function TryGetMember(
                  ByVal binder As GetMemberBinder,
                  ByRef result As Object) As Boolean

    result = m_dictionary.Item(binder.Name.ToLower)
    Return True

  End Function

  ''' <summary>
  ''' Adiciona o nome e o valor no Dictionary
  ''' </summary>
  ''' <returns></returns>
  ''' <remarks></remarks>
  Public Overrides Function TrySetMember(
                  ByVal binder As System.Dynamic.SetMemberBinder,
                  ByVal value As Object) As Boolean

    m_dictionary.Add(binder.Name.ToLower, value)
    Return True
  End Function

End Class

Depois, criando uma nova instância e adicionado alguns valores:

    ' Cria uma nova instância da classe DynamicObjectTest
    Dim myDynamicObject As Object = New DynamicObjectTest()

    ' Adiciona uma nova label (com algumas definições)
    myDynamicObject.CustomObject = New Label() With
              {
               .Text = "Teste",
               .Name = "lblTeste",
               .Location = New Point(10, 10)
              }

    ' Adiciona uma mensagem
    myDynamicObject.Msg = "Dynamic Object Test"

Finalmente, a verificação e utilização dos valores guardados:

    ' Adiciona ao Form a label
    Me.Controls.Add(myDynamicObject.CustomObject)

    ' Define como texto do Form a mensagem guardada
    Me.Text = myDynamicObject.Msg


Uma funcionalidade que promete, que para além de permitir utilizar linguagens dinâmicas no VB.NET ou no C#, o que poderá trazer grandes vantagens, permite ainda criar objectos dinâmicos que podem ser utilizados na própria linguagem (onde aqui poderá ter menos utilização), ou mesmo em linguagens externas (desde que tenham suporte DLR)

MSDN Magazine - Março de 2010
Já está disponível a MSDN Magazine de Março de 2010

Nesta edição é possível ler diversos artigos como:

Entre outras! Para os leitores de Visual Basic, duas colunas que gostaria de destacar:

A não perder!

Posted: 7-3-2010 10:43 por Jorge Paulino | with no comments
Filed under: ,