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:
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:
- 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!
- 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:
- 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.
- 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.
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%.
ResponderExcluirAté quando a Autodesk vai deixar o software subutilizar nossos processadores?
Olá Raul,
ExcluirNa 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