Pular para o conteúdo principal

Utilizando os múltiplos núcleos do computador - Usando Parallel.For

Estes dias estive remodelando algumas aplicações que já tinha feito, Especialmente meu plug-in para exportação de pontos do AutoCAD para o Google Earth e PLS-CADD.

O problema que vinha sendo enfrentado era quando a quantidade de pontos era muito elevada o programa acabava por demorar muito tempo para realizar a coleta de pontos. Isso isso acontece porque desde o inicio dos computadores todos os códigos e loop's foram estruturados para executar ações sequencialmente. Se você observar quando o computador executa cálculos complexos seja no AutoCAD ou qualquer outro programa como PLS-CADD, Excel, etc... apenas um núcleo é utilizado para processamento, ou seja, se o seu computador é Dual Core 50% da capacidade em utilização, Quad-core 25% e assim por diante... O fato é que temos uma geração de aplicativos que está adaptada para uma geração atrás de Computadores e é natural que certas atividades as vezes acabam por demandar muito mais tempo e subaproveitar o computador.


Eu realmente nunca tinha me utilizado da programação paralela mas dada a demanda resolvi me aventurar por esse novo mundo.

Meu objetivos era apenas acelerar o processo de importação dos pontos, por dois motivos:
  1. Precisava criar pontos no meio das retas - quando queria representar estradas e afins, como o programa original só pegava os vértices, os pontos no meio das retas não ficavam representados corretamente, assim, resolvi que teria que criar esses pontos de alguma maneira, o objeto Line já tinha um recuro para pegar pontos ao longo das retas mas erá necessário fazer isso muitas vezes, uma reta de 1200 queria que fosse dividida em 2400 partes!
  2. O programa já apresentava uma demora grande na importação dos pontos naturalmente que somado ao recurso acima ia se tornar impraticável criar o arquivo PLS-CADD a partir de um desenho de AutoCAD.
A solução foi utilizar o recurso de processamento paralelo oferecido pelo Visual Studio já implementado no .NET Framework. 

Essa foi uma grande vantagem de ter migrado do VBA para .NET, um recurso que só está disponível porque os desenvolvedores de .NET Framework fizeram para outras aplicações.

A minha principal fonte de informação foi o site da MSDN que apresenta bem simples como implenetar os loop's de programação paralela, veja aqui o Parallel.For, a Simple Parallel For Loop.

Quando você escreve um loop para execuçaõ paralela tem que ter em mente duas coisas:
  1. A sequência de montagem, se você está montando um vetor, não existe mais. Isso acontece porque a função será executada ao mesmo tempo por vários núcleos e o .NET Framework vai fazer a divisão to tamanho total da amostra pelos núcleos e em função do tempo de processamento de cada um a saída vai ser gerada.
  2. Dentro dos Loop's paralelos não pode ter acesso a entidades de acesso exclusivo, como StreamWrite, ou GetObject() porque dois processadores podem acessar o mesmo objeto ao mesmo tempo causando um erro.
Atentando para esses detalhes consegui montar um código que escrevia bem rápido a nuvem de pontos. Dentro do Loop da Transaction fiz a entrada no objeto é com o método GetPointAtDist() consegui gerar a minha nuvem de pontos bem rápido.

  foreach (SelectedObject acSSObj in acSSet)  
             {  
               String txt = "";  
               if (acSSObj != null)  
               {  
                 Entity acEnt = null;  
                 try  
                 {  
                   acEnt = (Entity)acTrans.GetObject(acSSObj.ObjectId, OpenMode.ForRead);  
                 }  
                 catch  
                 {  
                 }  
                 if (acEnt != null)  
                 {  
                   // If a "lightweight" (or optimized) polyline  
                   Polyline lwp = acEnt as Polyline;  
                   if (lwp != null)  
                   {  
                     if (!notshowdialog)  
                     {  
                       //Formulário para confirmação da elevação da linha  
                       frmte.txtbox_x.Text = lwp.NumberOfVertices.ToString() + "pontos, total de " + lwp.Length + "m";  
                       frmte.txtbox_y.Text = "ignore";  
                       frmte.txtbox_z.Text = lwp.Elevation.ToString("#################0.0###############", new CultureInfo("en-US"));  
                       frmte.cmbbox_fea.Items.AddRange(FeatureCodeList.GetListString().ToArray());  
                       if (frmte.ShowDialog() != System.Windows.Forms.DialogResult.OK)  
                         return;  
                     }  
                     //Cria os Vertices  
                     int vn = lwp.NumberOfVertices;  
                     Parallel.For(0, vn, i =>  
                     {  
                       Point2d pt = lwp.GetPoint2dAt(i);  
                       txt += frmte.txtbox_description.Text + "\t" + pt.X.ToString().Replace(",", ".") + "\t" + pt.Y.ToString().Replace(",", ".") +  
                            "\t" + lwp.Elevation.ToString("#################0.0###############", new CultureInfo("en-US")).Replace(",", ".") + "\t" + frmte.txtbox_h.Text.Replace(",", ".") + "\t" + frmte.cmbbox_fea.SelectedItem.ToString().Split(' ')[0] +  
                            "\t" + frmte.txt_profile_comment.Text + "\t" + frmte.txt_plan_comment.Text + "\r\n";  
                     });  
                     //Cria os pontos Intermediários  
                     vn = (int)lwp.Length;  
                     Parallel.For(0, vn, i =>  
                     {  
                       Point3d pt = lwp.GetPointAtDist((double)i);  
                       txt += frmte.txtbox_description.Text + "\t" + pt.X.ToString().Replace(",", ".") + "\t" + pt.Y.ToString().Replace(",", ".") +  
                            "\t" + lwp.Elevation.ToString("#################0.0###############", new CultureInfo("en-US")).Replace(",", ".") + "\t" + frmte.txtbox_h.Text.Replace(",", ".") + "\t" + frmte.cmbbox_fea.SelectedItem.ToString().Split(' ')[0] +  
                            "\t" + frmte.txt_profile_comment.Text + "\t" + frmte.txt_plan_comment.Text + "\r\n";  
                     });  
                     continue;  
                   }  
                   // If an old-style, 2D polyline  
                   Polyline2d p2d = acEnt as Polyline2d;  
                   if (p2d != null)  
                   {  
                     List<Vertex2d> v2dList = new List<Vertex2d>();  
                     //// Use foreach to get each contained vertex  
                     foreach (ObjectId vId in p2d)  
                     {  
                       v2dList.Add((Vertex2d)acTrans.GetObject(vId, OpenMode.ForRead));  
                       //acDoc.Editor.WriteMessage("\n" + v2d.Position.ToString());  
                     }  
                     Parallel.For(0, v2dList.Count, i =>  
                     {  
                       txt += frmte.txtbox_description.Text + "\t" + v2dList[i].Position.X.ToString().Replace(",", ".") + "\t" + v2dList[i].Position.Y.ToString().Replace(",", ".") +  
                       "\t" + v2dList[i].Position.Z.ToString("#################0.0###############", new CultureInfo("en-US")).Replace(",", ".") + "\t" + frmte.txtbox_h.Text.Replace(",", ".") + "\t" + frmte.cmbbox_fea.SelectedItem.ToString().Split(' ')[0] +  
                       "\t" + frmte.txt_profile_comment.Text + "\t" + frmte.txt_plan_comment.Text + "\r\n";  
                     });  
                     Parallel.For(0, (int)p2d.Length, i =>  
                     {  
                       Point3d pt = p2d.GetPointAtDist((double)i);  
                       txt += frmte.txtbox_description.Text + "\t" + pt.X.ToString().Replace(",", ".") + "\t" + pt.Y.ToString().Replace(",", ".") +  
                       "\t" + pt.Z.ToString("#################0.0###############", new CultureInfo("en-US")).Replace(",", ".") + "\t" + frmte.txtbox_h.Text.Replace(",", ".") + "\t" + frmte.cmbbox_fea.SelectedItem.ToString().Split(' ')[0] +  
                       "\t" + frmte.txt_profile_comment.Text + "\t" + frmte.txt_plan_comment.Text + "\r\n";  
                     });  
                     continue;  
                   }  
                   // Use foreach to get each contained vertex  
                 }  
               }  
               frm_plscadd.RichTextBox_Output.Text += txt;  
             }  

A parte paralela está na escrita dos valores no texto, que demora muito, nesse ponto a escrita paralela reduz mais e 10x o tempo de processamento. Não consegui medir ao certo quanto tempo demorava porque quando o volume era grande eu acabava por abortar e não media, mas demorava inicialmente mais de 4 horas para processar um .*dwg de elevações de 10Mb e agora com o Parallel.For gasta em torno de 5 minutos.


 vn = (int)lwp.Length;  
Parallel.For(0, vn, i =>  
{  
      Point3d pt = lwp.GetPointAtDist((double)i);  
      txt += frmte.txtbox_description.Text + "\t" + pt.X.ToString().Replace(",", ".") + "\t" + pt.Y.ToString().Replace(",", ".") +  
       "\t" + lwp.Elevation.ToString("#################0.0###############", new CultureInfo("en-US")).Replace(",", ".") + "\t" + frmte.txtbox_h.Text.Replace(",", ".") + "\t" + frmte.cmbbox_fea.SelectedItem.ToString().Split(' ')[0] +  
       "\t" + frmte.txt_profile_comment.Text + "\t" + frmte.txt_plan_comment.Text + "\r\n";  
});  

Espero que o artigo tenha sido útil escrevi em tempo recorde.


Comentários

  1. Software mal projetado mesmo o AutoCAD. Fiquei muito triste ao ver meu Q9550 travando com uma planta de 17MB sendo que o processamento está travado em 25%.

    Até quando a Autodesk vai deixar o software subutilizar nossos processadores?

    ResponderExcluir
    Respostas
    1. Olá Raul,
      Na verdade software é bem projetado só que ele ficou para trás com a evolução da tecnologia multi core. Eu acredito que já tenha funções que a Autodesk tenha implementado a função multi core, isso é um processo demorado porque são muitas funções e assim como eu mostro neste exemplo não é uma tarefa fácil e passa pela estrutura de acesso de cada camada de código.

      Mas vamos seguir na expectativa que a coisa vai melhorar!

      Abraço,
      Victor

      Excluir

Postar um comentário

Postagens mais visitadas deste blog

Numeração Automática no AutoCAD

Sem dúvida esse é uma ferramenta única que fazia falta a tempos e foi introduzida recentemente no AutoCAD, na última década… Em 2013 tinha feito um programa para criar uma bolinhas de numeração automática usando o conceito de Leader eu fazia um controle dos números dos itens na mão mesmo, depois de um tempo resolvi fazer um menuzinho para controlar o conteúdo do número dos bullets, hoje isso não é mais necessário, existe um comando no próprio AutoCAD para isso e faz parte das Ferramentas Expressas do AutoCAD. Gosto de compartilhar ferramentas e dicas que tendem a ser menos conhecidas ou subutilizadas, mas ainda podem fornecer um impulso para sua produtividade e fluxo de trabalho e que eu mesmo me surpreendi quando as encontrei Hoje, quero apresentar outra ferramenta, a numeração automática. Como o nome indica, o comando Numeração automática permite que você adicione automaticamente um número às etiquetas ou texto em um desenho. Vou mostrar como a numeração automática pode ser usada par...

Alterar a cor de fundo do AutoCAD

Eu sou um adepto do fundo preto, tudo que eu posso mudo a cor do fundo para preto. Nas ultimas versões do AutoCAD tenho notado que não é mais o padrão o fundo preto absoluto, é um cinza escuro. As vezes gosto também de mudar a cor do Layout de branco para preto afinal de contas quando trabalhamos com penas e contraste no model space preto, ver um desenho no layout é impossível.  Você pode mudar sua cor de fundo do AutoCAD para a cor que quiser. Definindo cores personalizadas Primeiro, vá para o menu Opções (botão Aplicativo> Opções), selecione a guia Exibição e clique no botão Cores. Agora você deve ver a caixa de diálogo Cores da janela de desenho: Agora você pode atualizar as cores de qualquer elemento da interface. Isso incluí o plano de fundo em qualquer contexto (selecione Model e Layout). Você pode escolher qualquer uma das predefinições, ou você pode escolher a partir de todo o espectro (basta escolher a opção Selecionar Cor ...). Se você não gost...

Unindo blocos em AutoCAD utilizando .NET

Hoje me deparai com uma necessidade de unir blocos com propriedades então resolvi fazer um comando para realizar esses procedimento. A principio esse procedimento não é tão complexo de ser executado individualmente mas por outro lado gostaria também de analisar um pouco melhor as possibilidades de se manipular blocos utilizando o AutoCAD. Abaixo o código que utilizei: using Autodesk.AutoCAD.ApplicationServices ; using Autodesk.AutoCAD.DatabaseServices ; using Autodesk.AutoCAD.EditorInput ; using Autodesk.AutoCAD.Runtime ; using System.Linq ; // This line is not mandatory, but improves loading performances [assembly: CommandClass(typeof(Merging_AutoCAD_blocks.Comandos))] namespace Merging_AutoCAD_blocks { public class Comandos { /// <summary> /// Uni dois ou mais blocos em um unico bloco. /// </summary> [CommandMethod("MERGBLKS")] public static void UnindoBlocos () {...