[SOLVED]after .net code executed, visio page become slow and high latency

Started by KarryGood, March 02, 2018, 06:43:00 AM

Previous topic - Next topic

0 Members and 1 Guest are viewing this topic.

KarryGood

Hello, everybody.

I have a visio page with 8000 simple rectangles. I programmed a  simple .net Add-in that generated a button in visio ribbion. The button's function is to modify 1000 rectangles angle, like below:

public void btn_click()
{
   Page pg = Globals.ThisAddin.Application.ActivePage;
   for(int i = 0; i < 1000; i++)
   {
      pg.Shapes[i + 1].CellSRC[(short)visSectionObject, (short)visRowXFormOut, (short)visXFormAngle].Formula = "30 deg";
   }
}

After running the button's code, the page became slow when I zoomin or open one shape's ShapeSheet window.

Then I release my .net addin(Developer -> COM addins -> uncheck my addin -> OK),  the page became fast again.

I want to know why visio became slow after run that code. Is there any methods to solute the problem?

KarryGood

And if I execute same function with vba, the page's zooming speed is same with that before executing vba code.

Sub test()
    Dim pg As Page
    Dim sp As Shape
    Set pg = ActivePage
    Dim i As Long
    For i = 1 To pg.Shapes.Count
        pg.Shapes(i).CellsSRC(VisSectionIndices.visSectionObject, VisRowIndices.visRowXFormOut, VisCellIndices.visXFormAngle).Formula = "30 deg"
    Next
End Sub


I'm confused.
Anybody know this problems?
Do I have to give up using .net framework to develop visio?

Surrogate

Strange !
IMHO No matter what development environment you use.
Please describe how you draw these over9000 rectangles ?
Best way - drop rectangles from stencil. Because in this case these
rectangles are a array (set) of similar shapes and Visio engine works with
these rectangles more quickly then if you draw each rectangle with drawing tools.

KarryGood

Quote from: Surrogate on March 03, 2018, 04:02:24 AM
Strange !
IMHO No matter what development environment you use.
Please describe how you draw these over9000 rectangles ?
Best way - drop rectangles from stencil. Because in this case these
rectangles are a array (set) of similar shapes and Visio engine works with
these rectangles more quickly then if you draw each rectangle with drawing tools.

I draw a rectangle, then COPY PASTE COPY PASTE COPY PASTE... :-)

Now I create a new visio document with stencil. Also I COPY PASTE about 16000 rectangles.

The file becomes a little smaller. But the problem remains. That is: After executing vba code, the page efficiency was the same as before. But after executing .net code, the page became slow when zoom in or out.

Surrogate

Hi, Karry!
Why you draw so many rectangles? These rectangle contain any text?
If no, may be best way create fill pattern?

Nikolay

Maybe .NET code leaves a lot of Visio shape objects in memory (i.e. not garbage collected). Or subscribes to some events.
Check the memory consumed by Visio after generating.

If memory is the problem, try executing after generating the page the "ultimate workaround" (twice):

GC.Collect();
GC.Collect();


Other than that you may consider releasing underlying COM objects explicitly when generating that many things. (Marshal.ReleaseComObject). Like this:


private void button1_Click(object sender, RibbonControlEventArgs e)
{
    var pg = Globals.ThisAddIn.Application.ActivePage;
    var shapes = pg.Shapes;
    for(var i = 0; i < 8000; i++)
    {
        var shape = shapes[i + 1];

        var cell = shape.CellsSRC[
            (short) Visio.VisSectionIndices.visSectionObject,
            (short) Visio.VisRowIndices.visRowXFormOut,
            (short) Visio.VisCellIndices.visXFormAngle];

        cell.Formula = "30 deg";

        Marshal.ReleaseComObject(cell);
        Marshal.ReleaseComObject(shape);
    }
}


The difference to VBA is that VBA releases objects when they go out of scope (it relies on COM reference counting).
The .NET uses garbage collection (starts freeing memory when needed only)

Alternatively, you could try to use a SINGLE call instead of multiple calls (use .SetFormulas):


private void button2_Click(object sender, RibbonControlEventArgs e)
{
    var pg = Globals.ThisAddIn.Application.ActivePage;

    var src = Array.CreateInstance(typeof(short), 8000 * 4);
    var val = Array.CreateInstance(typeof(object), 8000);

    var sel = pg.CreateSelection(Visio.VisSelectionTypes.visSelTypeAll);

    Array ids;
    sel.GetIDs(out ids);

    for (int i = 0; i < 8000; ++i)
    {
        short id = Convert.ToInt16(ids.GetValue(i));

        src.SetValue(id, i * 4 + 0);
        src.SetValue((short) Visio.VisSectionIndices.visSectionObject, i * 4 + 1);
        src.SetValue((short) Visio.VisRowIndices.visRowXFormOut, i * 4 + 2);
        src.SetValue((short) Visio.VisCellIndices.visXFormAngle, i * 4 + 3);

        val.SetValue("30 deg", i);
    }

    pg.SetFormulas(ref src, ref val, 0);
}

Nikolay

Please note that if what you are actually trying to do is to create a "tile" fill pattern,
Probably it's better to just use the fill pattern instead of thousands of rectangles.

Croc

I tried to reproduce the situation. To increase the effect, I changed not 1000, but 7000 shapes.
After pressing the button (turning 7000 shapes) the operating time of Zoom increases more than 10 times.
Event Monitor does not register additional events.
The Task Manager does not register changes in physical memory.
If I add GK.Collect (); (twice) at the end of the cycle, the deceleration disappears.

KarryGood

Thanks, everybody!

GC.Collect() twice can solve this problem. After I ran the .net code with GC.Collect() statement, visio's page could zoom in/out as quickly as before.
It seems .net memory is the problem.

In addition, to improving efficiency, I add GC.Collect() even in for loop! And it works. As below:

        private void button3_Click(object sender, RibbonControlEventArgs e)
        {
            Page pg = Globals.ThisAddIn.Application.ActivePage;
            int undoId = pg.Document.BeginUndoScope("set angle");
            Array src = Array.CreateInstance(typeof(short), pg.Shapes.Count * 4);
            Array val = Array.CreateInstance(typeof(object), pg.Shapes.Count);
            for (int i = 0; i < pg.Shapes.Count; i++)
            {
                src.SetValue((short)pg.Shapes[i + 1].ID, i * 4);
                src.SetValue((short)VisSectionIndices.visSectionObject, i * 4 + 1);
                src.SetValue((short)VisRowIndices.visRowXFormOut, i * 4 + 2);
                src.SetValue((short)VisCellIndices.visXFormAngle, i * 4 + 3);
                val.SetValue("30 deg", i);
            }
            pg.SetFormulas(ref src, ref val, (short)VisGetSetArgs.visSetUniversalSyntax);

            pg.Document.EndUndoScope(undoId, true);

            GC.Collect();
            GC.Collect();
        }

        private void button4_Click(object sender, RibbonControlEventArgs e)
        {
            Page pg = Globals.ThisAddIn.Application.ActivePage;
            int undoId = pg.Document.BeginUndoScope("set angle");
            Array src = Array.CreateInstance(typeof(short), pg.Shapes.Count * 4);
            Array val = Array.CreateInstance(typeof(object), pg.Shapes.Count);
            int operationCounter = 0;
            for (int i = 0; i < pg.Shapes.Count; i++)
            {
                src.SetValue((short)pg.Shapes[i + 1].ID, i * 4);
                src.SetValue((short)VisSectionIndices.visSectionObject, i * 4 + 1);
                src.SetValue((short)VisRowIndices.visRowXFormOut, i * 4 + 2);
                src.SetValue((short)VisCellIndices.visXFormAngle, i * 4 + 3);
                val.SetValue("30 deg", i);

                //run GC.Collect() after every 1000 loops
                if (operationCounter++ > 1000)
                {
                    GC.Collect();
                    GC.Collect();
                    operationCounter = 0;
                }
            }
            pg.SetFormulas(ref src, ref val, (short)VisGetSetArgs.visSetUniversalSyntax);

            pg.Document.EndUndoScope(undoId, true);

            GC.Collect();
            GC.Collect();
        }
    }

But button3_Click is much slower than button4_Click. It is strange.

Croc

I also noticed that 8000 single operations ".Formula =" are faster than the "SetFormulas" batch operation. About two times.

Nikolay


KarryGood

Quote from: Nikolay on March 05, 2018, 12:27:00 PM
BTW, how much memory do you have installed on the PC?

Big enough. Memory in one PC is 16G, in another PC is 64G.

Visio Guy

Consider turning off undo before crazy operations like this.

visApp.UndoEnabled = false;
For articles, tips and free content, see the Visio Guy Website at http://www.visguy.com
Get my Visio Book! Using Microsoft Visio 2010

KarryGood

Quote from: Visio Guy on March 06, 2018, 07:03:47 AM
Consider turning off undo before crazy operations like this.

visApp.UndoEnabled = false;

Good trick. Thank you :)