Author Topic: Speed Improvement  (Read 13873 times)

0 Members and 1 Guest are viewing this topic.

DBARANAS

  • Guest
Speed Improvement
« on: July 25, 2007, 01:42:57 PM »
My apps design is split into 3 major sections.

1] The top section gets persisted and holds simple parameters that define the sub assemblies in a Building Model.

2] The middle section creates, moves, deletes, etc, my objects based on project parameters, These objects are contained in generic lists. It boils down to just lists of solids, polylines, lines, tables, and mtexts.

3] The last section is just a few methods to open up transactions and iterate the generic lists and have Acad do work.

This scheme works quite well until the model gets bigger, On a 2 year old box with 1G Ram and a $100 NVidia card it takes about 7-10 seconds to create ~500 solids.

When I profile I see that 95% of the time is spent in just 1 method [3] ...example below

What I am thinking about doing is getting few these methods [3] that interface to Acad in their own native class and wrap it and call it from my managed code in the hopes that Native Arx is quicker than Net Arx.

This would be a big learning curve for unkown benefit if any, Any thoughts are appreciated



Code: [Select]
   

Friend Shared Sub DrawEntities()

        Dim db As AcDb.Database = AcDb.HostApplicationServices.WorkingDatabase
        Dim tr As AcDb.Transaction = db.TransactionManager.StartTransaction()
        Dim dl As AcAs.DocumentLock = AcAp.DocumentManager.MdiActiveDocument.LockDocument()
        Dim bt As AcDb.BlockTable = DirectCast(tr.GetObject(db.BlockTableId, _
        AcDb.OpenMode.ForWrite, True), AcDb.BlockTable)
        Dim br As AcDb.BlockTableRecord = DirectCast(tr.GetObject(bt(AcDb.BlockTableRecord.ModelSpace), _
        AcDb.OpenMode.ForWrite, False), AcDb.BlockTableRecord)

        For Each Line As AcDbLine In Lines
            If Not Line.ID.Exists Then
                'Create AcDb Entity
                Dim en As New AcDb.Line(New AcGe.Point3d(0, 0, 0), New AcGe.Point3d(5, 0, 0))
                en.Color = AcCm.Color.FromColor(Line.Color)
                'Rotate Around Z Axis
                en.TransformBy(AcGe.Matrix3d.Rotation(Rad(Line.Rotation), _
                New AcGe.Vector3d(0, 0, 1), New AcGe.Point3d(0, 0, 0)))
                'Move in ModelSpace
                Dim _Matrix3d As AcGe.Matrix3d = AcGe.Matrix3d.Displacement(New AcGe.Vector3d( _
                Line.Origin.X + (Math.Cos(Rad(Line.Angle)) * Line.Position.X), _
                Line.Origin.Y - (Math.Sin(Rad(Line.Angle)) * Line.Position.X), _
                Line.Position.Z))
                en.TransformBy(en.Ecs.PostMultiplyBy(_Matrix3d))
                'Add Entity to AcDb
                br.AppendEntity(en)
                tr.AddNewlyCreatedDBObject(en, True)
                'Update FwHr Entity
                Line.ID.AcDbHandle = en.ObjectId.Handle.Value.ToString()
                Line.ID.Exists = True
            End If
        Next

        For i As Integer = 0 To PolyLines.Count - 1
            If Not PolyLines(i).ID.Exists Then
                'Create AcDb Entity
                Dim PointCol As New AcGe.Point3dCollection
                For j As Integer = 0 To PolyLines(i).Points.Count - 1
                    PointCol.Add(New AcGe.Point3d(PolyLines(i).Points(j).XYZ))
                Next
                Dim en As New AcDb.Polyline3d(AcDb.Poly3dType.SimplePoly, PointCol, True)
                en.Visible = PolyLines(i).IsVisible
                en.Color = AcCm.Color.FromColor(PolyLines(i).Color)
                Select Case PolyLines(i).IsOnWCS
                    'Baselines are drawn in the WCS so they don't move and rotate
                    Case False
                        'Rotate Around Z Axis
                        en.TransformBy(AcGe.Matrix3d.Rotation(Rad(PolyLines(i).Rotation), _
                        New AcGe.Vector3d(0, 0, 1), New AcGe.Point3d(0, 0, 0)))
                        'Move in ModelSpace
                        Dim _Matrix3d As AcGe.Matrix3d = AcGe.Matrix3d.Displacement(New AcGe.Vector3d( _
                        PolyLines(i).Origin.X + (Cos(Rad(PolyLines(i).Angle)) * PolyLines(i).Position.X), _
                        PolyLines(i).Origin.Y - (Sin(Rad(PolyLines(i).Angle)) * PolyLines(i).Position.X), _
                        PolyLines(i).Position.Z))
                        en.TransformBy(en.Ecs.PostMultiplyBy(_Matrix3d))
                        en.List()
                End Select
                'Add Entity to AcDb
                br.AppendEntity(en)
                tr.AddNewlyCreatedDBObject(en, True)
                'Update FwHr Entity
                PolyLines(i).ID.AcDbHandle = en.ObjectId.Handle.Value.ToString()
                PolyLines(i).ID.Exists = True
            End If
        Next

        For i As Integer = 0 To Solids.Count - 1
            If Not Solids(i).ID.Exists Then
                Dim en As AcDb.Solid3d = Nothing
                Dim BasePointCol As New AcGe.Point3dCollection
                For j As Integer = 0 To Solids(i).Points.Count - 1
                    BasePointCol.Add(New AcGe.Point3d(Solids(i).Points(j).XYZ))
                Next
                Dim Polyline3d As AcDb.Polyline3d = New AcDb.Polyline3d(AcDb.Poly3dType.SimplePoly, BasePointCol, True)
                'Create Region
                Dim ColPoly As AcDb.DBObjectCollection = New AcDb.DBObjectCollection()
                Dim ColRegion As AcDb.DBObjectCollection = New AcDb.DBObjectCollection()
                ColPoly.Add(Polyline3d)
                ColRegion = AcDb.Region.CreateFromCurves(ColPoly)
                Dim Region As AcDb.Region = DirectCast(ColRegion(0), AcDb.Region)
                ColPoly.Dispose()
                ColRegion.Dispose()
                Polyline3d.Dispose()
                'Create AcDb Entity
                en = New AcDb.Solid3d()
                en.Extrude(Region, Solids(i).Extrude, 0)
                Region.Dispose()
                en.Color = AcCm.Color.FromColor(Solids(i).Color)

                'Union Solids if needed
                If Solids(i).AddRegions.Count > 0 Then
                    Dim AddPointCol As New AcGe.Point3dCollection
                    For j As Integer = 0 To Solids(i).AddRegions.Count - 1
                        'Get Union Points
                        For k As Integer = 0 To Solids(i).AddRegions.Item(j).Points.Count - 1
                            AddPointCol.Add(New AcGe.Point3d(Solids(i).AddRegions(j).Points(k).XYZ))
                        Next
                        'Create Union Region
                        Dim AddPolyline3d As AcDb.Polyline3d = New AcDb.Polyline3d(AcDb.Poly3dType.SimplePoly, AddPointCol, True)
                        Dim AddPoly As AcDb.DBObjectCollection = New AcDb.DBObjectCollection()
                        Dim AddRegion As AcDb.DBObjectCollection = New AcDb.DBObjectCollection()
                        AddPoly.Add(AddPolyline3d)
                        AddRegion = AcDb.Region.CreateFromCurves(AddPoly)
                        Dim AcDbAddRegion As AcDb.Region = DirectCast(AddRegion(0), AcDb.Region)
                        'Create Union Solid
                        Dim AddSolid As New AcDb.Solid3d
                        AddSolid.Extrude(AcDbAddRegion, Solids(i).AddRegions(j).Extrude, 0)
                        AddSolid.Color = AcCm.Color.FromColor(Solids(i).AddRegions(j).Color)
                        'Union Solid
                        en.BooleanOperation(AcDb.BooleanOperationType.BoolUnite, AddSolid)
                        AcDbAddRegion.Dispose()
                        AddRegion.Dispose()
                        AddPointCol.Clear()
                    Next
                    AddPointCol.Dispose()
                End If

                'Subtract Solids if needed
                If Solids(i).SubtractRegions.Count > 0 Then
                    Dim MinusPointCol As New AcGe.Point3dCollection
                    For j As Integer = 0 To Solids(i).SubtractRegions.Count - 1
                        'Get Minus Points
                        For k As Integer = 0 To Solids(i).SubtractRegions.Item(j).Points.Count - 1
                            MinusPointCol.Add(New AcGe.Point3d(Solids(i).SubtractRegions(j).Points(k).XYZ))
                        Next
                        'Create Minus Region
                        Dim MinusPolyline3d As AcDb.Polyline3d = New  AcDb.Polyline3d(AcDb.Poly3dType.SimplePoly, MinusPointCol, True)
                        Dim MinusPoly As AcDb.DBObjectCollection = New AcDb.DBObjectCollection()
                        Dim MinusRegion As AcDb.DBObjectCollection = New AcDb.DBObjectCollection()
                        MinusPoly.Add(MinusPolyline3d)
                        MinusRegion = AcDb.Region.CreateFromCurves(MinusPoly)
                        Dim AcDbSubRegion As AcDb.Region = DirectCast(MinusRegion(0), AcDb.Region)
                        'Create Minus Solid
                        Dim MinusSolid As New AcDb.Solid3d
                        MinusSolid.Extrude(AcDbSubRegion, Solids(i).SubtractRegions(j).Extrude, 0)
                        MinusSolid.Color = AcCm.Color.FromColor(Solids(i).SubtractRegions(j).Color)
                        'Subtract Solid
                        en.BooleanOperation(AcDb.BooleanOperationType.BoolSubtract, MinusSolid)
                        AcDbSubRegion.Dispose()
                        MinusRegion.Dispose()
                        MinusPointCol.Clear()
                    Next
                    MinusPointCol.Dispose()
                End If

                'Color Faces
                If Solids(i).ID.Description = g.c.Wall.PLATE_TOP_TOP Then
                    Dim ids As AcDb.ObjectId() = New AcDb.ObjectId(0) {}
                    Select Case Solids(i).Angle
                        Case g.c.FRONT, g.c.MINUS_FRONT
                            Dim Outside As New AcDb.SubentityId(AcDb.SubentityType.Face, 4)
                            Dim OutsidePath As New AcDb.FullSubentityPath(ids, Outside)
                            en.SetSubentityColor(Outside, AcCm.Color.FromColor(System.Drawing.Color.Green))
                            Dim Inside As New AcDb.SubentityId(AcDb.SubentityType.Face, 2)
                            Dim InsidePath As New AcDb.FullSubentityPath(ids, Inside)
                            en.SetSubentityColor(Inside, AcCm.Color.FromColor(System.Drawing.Color.Red))
                        Case g.c.BACK
                            Dim Outside As New AcDb.SubentityId(AcDb.SubentityType.Face, 2)
                            Dim OutsidePath As New AcDb.FullSubentityPath(ids, Outside)
                            en.SetSubentityColor(Outside, AcCm.Color.FromColor(System.Drawing.Color.Green))
                            Dim Inside As New AcDb.SubentityId(AcDb.SubentityType.Face, 4)
                            Dim InsidePath As New AcDb.FullSubentityPath(ids, Inside)
                            en.SetSubentityColor(Inside, AcCm.Color.FromColor(System.Drawing.Color.Red))
                        Case g.c.LEFT
                            Dim Outside As New AcDb.SubentityId(AcDb.SubentityType.Face, 4)
                            Dim OutsidePath As New AcDb.FullSubentityPath(ids, Outside)
                            en.SetSubentityColor(Outside, AcCm.Color.FromColor(System.Drawing.Color.Red))
                            Dim Inside As New AcDb.SubentityId(AcDb.SubentityType.Face, 2)
                            Dim InsidePath As New AcDb.FullSubentityPath(ids, Inside)
                            en.SetSubentityColor(Inside, AcCm.Color.FromColor(System.Drawing.Color.Green))
                        Case g.c.RIGHT, g.c.MINUS_RIGHT
                            Dim Outside As New AcDb.SubentityId(AcDb.SubentityType.Face, 2)
                            Dim OutsidePath As New AcDb.FullSubentityPath(ids, Outside)
                            en.SetSubentityColor(Outside, AcCm.Color.FromColor(System.Drawing.Color.Red))
                            Dim Inside As New AcDb.SubentityId(AcDb.SubentityType.Face, 4)
                            Dim InsidePath As New AcDb.FullSubentityPath(ids, Inside)
                            en.SetSubentityColor(Inside, AcCm.Color.FromColor(System.Drawing.Color.Green))
                    End Select
                End If

                Select Case Solids(i).IsOnWCS
                    'Baselines are drawn in the WCS so they don't move and rotate
                    Case False
                        'Rotate Around Z Axis
                        en.TransformBy(AcGe.Matrix3d.Rotation(Rad(Solids(i).Rotation), New AcGe.Vector3d(0, 0, 1), New AcGe.Point3d(0, 0, 0)))
                        'Move in ModelSpace
                        Dim _Matrix3d As AcGe.Matrix3d = AcGe.Matrix3d.Displacement(New AcGe.Vector3d(Solids(i).Origin.XYZ))
                        en.TransformBy(en.Ecs.PostMultiplyBy(_Matrix3d))
                End Select

                'Add Entity to AcDb
                br.AppendEntity(en)
                tr.AddNewlyCreatedDBObject(en, True)
                'Update FwHr Entity
                Solids(i).ID.AcDbHandle = en.ObjectId.Handle.Value.ToString
                Solids(i).ID.Exists = True
                Solids(i).Centroid = en.MassProperties.Centroid.ToArray
                Solids(i).Weight = en.MassProperties.Volume * g.c.Weight.SPF

                If Solids(i).IsSelectable Then
                    'Add xData to AcDb Entity
                    Dim xRec As New AcDb.Xrecord()
                    xRec.Data = New AcDb.ResultBuffer( _
                    New AcDb.TypedValue(AcDb.DxfCode.Text, Solids(i).ID.Beam), _
                    New AcDb.TypedValue(AcDb.DxfCode.Text, Solids(i).ID.BeamPocket), _
                    New AcDb.TypedValue(AcDb.DxfCode.Text, Solids(i).ID.Blocking), _
                    New AcDb.TypedValue(AcDb.DxfCode.Text, Solids(i).ID.Building), _
                    New AcDb.TypedValue(AcDb.DxfCode.Text, Solids(i).ID.Description), _
                    New AcDb.TypedValue(AcDb.DxfCode.Text, Solids(i).ID.Door), _
                    New AcDb.TypedValue(AcDb.DxfCode.Text, Solids(i).ID.DoubleStud), _
                    New AcDb.TypedValue(AcDb.DxfCode.Text, Solids(i).ID.FireStopping), _
                    New AcDb.TypedValue(AcDb.DxfCode.Text, Solids(i).ID.FloorSheeting), _
                    New AcDb.TypedValue(AcDb.DxfCode.Text, Solids(i).ID.Item), _
                    New AcDb.TypedValue(AcDb.DxfCode.Text, Solids(i).ID.Joist), _
                    New AcDb.TypedValue(AcDb.DxfCode.Text, Solids(i).ID.Panel), _
                    New AcDb.TypedValue(AcDb.DxfCode.Text, Solids(i).ID.Partition), _
                    New AcDb.TypedValue(AcDb.DxfCode.Text, Solids(i).ID.Post), _
                    New AcDb.TypedValue(AcDb.DxfCode.Text, Solids(i).ID.Project), _
                    New AcDb.TypedValue(AcDb.DxfCode.Text, Solids(i).ID.RimJoist), _
                    New AcDb.TypedValue(AcDb.DxfCode.Text, Solids(i).ID.Slab), _
                    New AcDb.TypedValue(AcDb.DxfCode.Text, Solids(i).ID.Stair), _
                    New AcDb.TypedValue(AcDb.DxfCode.Text, Solids(i).ID.Storey), _
                    New AcDb.TypedValue(AcDb.DxfCode.Text, Solids(i).ID.Strapping), _
                    New AcDb.TypedValue(AcDb.DxfCode.Text, Solids(i).ID.Wall), _
                    New AcDb.TypedValue(AcDb.DxfCode.Text, Solids(i).ID.Window))
                    en.CreateExtensionDictionary()
                    Dim SolidExtDict As AcDb.DBDictionary = DirectCast(tr.GetObject(en.ExtensionDictionary(), _
                    AcDb.OpenMode.ForWrite, False), AcDb.DBDictionary)
                    SolidExtDict.SetAt("EntityData", xRec)

                    'Add Entity to AcDb
                    tr.AddNewlyCreatedDBObject(xRec, True)
                End If
            End If
        Next

        For i As Integer = 0 To MTexts.Count - 1
            If Not MTexts(i).ID.Exists Then
                'Create AcDb Entity
                Dim en As New AcDb.MText()
                en.Contents = MTexts(i).Value
                en.TextHeight = g.c.TEXT_HEGHT
                en.Color = AcCm.Color.FromColor(MTexts(i).Color)

                'Rotate Around Z Axis
                en.TransformBy(AcGe.Matrix3d.Rotation(Rad(MTexts(i).Angle), New AcGe.Vector3d(0, 0, 1), New AcGe.Point3d(0, 0, 0)))
                'Move in ModelSpace
                Dim MatrixA As AcGe.Matrix3d = AcGe.Matrix3d.Displacement(New AcGe.Vector3d( _
                MTexts(i).Origin.X + (Cos(Rad(MTexts(i).Angle)) * MTexts(i).Position.X), _
                MTexts(i).Origin.Y + (Sin(Rad(MTexts(i).Angle)) * MTexts(i).Position.X), _
                MTexts(i).Position.Z))
                en.TransformBy(en.Ecs.PostMultiplyBy(MatrixA))

                Dim MatrixB As AcGe.Matrix3d = AcGe.Matrix3d.Displacement(New AcGe.Vector3d( _
                -Sin(Rad(MTexts(i).Angle)) * MTexts(i).Position.Y, _
                Cos(Rad(MTexts(i).Angle)) * MTexts(i).Position.Y, _
                0))
                en.TransformBy(en.Ecs.PostMultiplyBy(MatrixB))

                'Add Entity to AcDb
                br.AppendEntity(en)
                tr.AddNewlyCreatedDBObject(en, True)
                'Update FwHr Entity
                MTexts(i).ID.AcDbHandle = en.ObjectId.Handle.Value.ToString()
            End If
        Next

        dl.Dispose()
        tr.Commit()
        tr.Dispose()

        Project.CommandLineTimeStamp("DrawingAids.DrawEntities" & " -Count [" & Solids.Count.ToString & "]")

    End Sub




It's Alive!

  • Retired
  • Needs a day job
  • Posts: 8698
  • AKA Daniel
Re: Speed Improvement
« Reply #1 on: July 26, 2007, 09:33:44 PM »
Hi Dave,

Your code looks very well written. I don’t how much of a performance gain you would get by porting this method to C++, It’s kind of the “don’t know until you try” thing. I would suspect it would be only a few percent. It certainly is possible to stick this in a mixed managed / unmanaged assembly, but probably isn’t a trivial task. What version of AutoCAD it this intended for?

MickD

  • King Gator
  • Posts: 3636
  • (x-in)->[process]->(y-out) ... simples!
Re: Speed Improvement
« Reply #2 on: July 26, 2007, 10:07:16 PM »
besides, 7-10 sec's for 500 solids with mod's/xform's isn't that bad, I'd hate to do it by hand ;)
I'm guessing the objects (such as lines) you are getting the data from your own object classes, if that is the case, passing your lists of objects to arx to do the xform and db stuff 'may' save you considerable time, but as Daniel pointed out I'm not sure it's worth it.
Nice job!
« Last Edit: July 26, 2007, 10:08:39 PM by MickD »
"Programming is really just the mundane aspect of expressing a solution to a problem."
- John Carmack

"Short cuts make long delays,' argued Pippin.”
- J.R.R. Tolkien

DBARANAS

  • Guest
Re: Speed Improvement
« Reply #3 on: July 27, 2007, 01:05:56 AM »
Hi Daniel, Mick

Yes everything is done in my classes and I end up with generic lists of my entities which have enough stuff in them to hand over to Acad so it can make everything blindly and only in 1 or 2 transactions. I found they each eat up time so that's why I did it that way.

Every time something is changed by the user the assembly in question is completely destroyed and rebuilt. This is because I am doing some weird things like adjusting everything based on weekly lumber prices, truck sizes, bad foundations, people changing their minds, etc, etc. I never know exactly how many or what size things will end up being until the model builds/rebuilds itself.

Being 100% parametric and with no drafting going on I sometimes have to destroy and rebuild a building which can contain >3000 entities.

Seeing as most everything between me and Acad goes on in that last method, it should be worth learning to write a C++ dll that does what that method does and see what happens. I have 3 similar methods like that one and thats it.

I hope that between http://arxdummies.blogspot.com/2006/03/autocad-2007.html, C++-CLI Standard.pdf, and the Adesk Object Arx group I can figure something out and speed things up

I am still in 2007, in 2008 something broke with the PropertyGrid on a Palette not showing items in dropdowns when using type converters....another battle for later.




MickD

  • King Gator
  • Posts: 3636
  • (x-in)->[process]->(y-out) ... simples!
Re: Speed Improvement
« Reply #4 on: July 27, 2007, 01:37:40 AM »
Just a thought, could you create a matrix and store it on the object that contains the values and just pass these values to the acad matrix method for a one time xform. If I remember correctly you can build a matrix by passing in an array of values to initialise the matrix, pass it in and 'doit'. This may save you reading, sorting and manipulating you objects as much as a matrix can hold both rotation and translation in the one matrix. It will involve a bit of math on your part but the drawing/rebuilding may be quicker??
hth.
"Programming is really just the mundane aspect of expressing a solution to a problem."
- John Carmack

"Short cuts make long delays,' argued Pippin.”
- J.R.R. Tolkien

DBARANAS

  • Guest
Re: Speed Improvement
« Reply #5 on: July 27, 2007, 04:24:43 AM »
Just a thought, could you create a matrix and store it on the object that contains the values and just pass these values to the acad matrix method for a one time xform. If I remember correctly you can build a matrix by passing in an array of values to initialise the matrix, pass it in and 'doit'. This may save you reading, sorting and manipulating you objects as much as a matrix can hold both rotation and translation in the one matrix. It will involve a bit of math on your part but the drawing/rebuilding may be quicker??
hth.

I did a quick test by stripping out xforms, subtracting, unions. Just get everything into modelspace at 0,0,0.

On ~1000 items all unstripped I average 13 to 16 secs. On the stripped version it is a very consistant 
10.500 sec (+/- 50mSec). So it seems that overhead is costing ~25% more....not too much for all the work it's doing.

That was a good idea and it shows that most of the overhead is spent just creating things without doing anything else.






LE

  • Guest
Re: Speed Improvement
« Reply #6 on: July 27, 2007, 01:04:19 PM »
When I profile I see that 95% of the time is spent in just 1 method [3] ...example below

(I am new to C#)

What did you use for that?

Have you seen or tried ANTS Profiler? by red-gate - I have seen the ads in the codeproject.com web site.

DBARANAS

  • Guest
Re: Speed Improvement
« Reply #7 on: July 28, 2007, 12:39:33 AM »
When I profile I see that 95% of the time is spent in just 1 method [3] ...example below

(I am new to C#)

What did you use for that?

Have you seen or tried ANTS Profiler? by red-gate - I have seen the ads in the codeproject.com web site.


This is a simple counter I think I might have found on codeproject.com and changed a bit ...can't remember. I tried ANTS before, I use this because it's quick and dirty and all I really need.

Running the code below produced the following results. It averages around 40 uSecs just to make the counter write to the command line compared with a few mSecs for most commands and functions to execute, so I get a pretty good idea how long things are really taking.


Code: [Select]
       Project.CommandLineTimeStamp("DrawingAids.DrawEntities" & " -Count [" & Solids.Count.ToString & "]")
        Project.CommandLineTimeStamp("Test Counter")
        Project.CommandLineTimeStamp("Test Counter")
        Project.CommandLineTimeStamp("Test Counter")
        Project.CommandLineTimeStamp("Test Counter")
        Project.CommandLineTimeStamp("Test Counter")
        Project.CommandLineTimeStamp("Test Counter")
        Project.CommandLineTimeStamp("Test Counter")
        Project.CommandLineTimeStamp("Test Counter")
        Project.CommandLineTimeStamp("Test Counter")
        Project.CommandLineTimeStamp("Test Counter")
        Project.CommandLineTimeStamp("Test Counter")
        Project.CommandLineTimeStamp("Test Counter")
        Project.CommandLineTimeStamp("Test Counter")
        Project.CommandLineTimeStamp("Test Counter")

Code: [Select]
    Public Function CommandLineTimeStamp( _
    ByVal Description As String) _
    As Boolean

        Dim ed As AcEd.Editor = AcAp.DocumentManager.MdiActiveDocument.Editor

        ed.WriteMessage( _
        MsVb.Chr(10) & _
        Description _
        & " [" & (TIMER.Peek() / CType(1000, Single)).ToString() & "]" & MsVb.Chr(10))

        TIMER.Reset()

        Return True

    End Function

Code: [Select]
Public NotInheritable Class StopWatch

    Private Declare Ansi Function QueryPerformanceCounter Lib "kernel32.dll" (ByRef x As Long) As Integer
    Private Declare Ansi Function QueryPerformanceFrequency Lib "kernel32.dll" (ByRef x As Long) As Integer
    Private _Frequency As Long
    Private _Increment As Long
    Private _StartTime As Long
    Public Sub New()

        Frequency = GetFrequency()
        Reset()

    End Sub
    Public Function Duration() _
    As Long

        Return CType((((GetValue() - StartTime) / CType(Frequency, Double)) * 1000000), Long)

    End Function
    Private Function GetFrequency() As Long

        Dim ret As Long = 0
        If QueryPerformanceFrequency(ret) = 0 Then
            Throw New NotSupportedException("Error while querying the performance counter frequency.")
        End If
        Return ret

    End Function
    Private Function GetValue() As Long

        Dim ret As Long = 0
        If QueryPerformanceCounter(ret) = 0 Then
            Throw New NotSupportedException("Error while querying the high-resolution performance counter.")
        End If
        Return ret

    End Function
    Public Function Peek() _
    As Long

        Return CType((((GetValue() - StartTime) / CType(Frequency, Double)) * 1000000), Long)

    End Function
    Public Sub Reset()

        StartTime = GetValue()

    End Sub
    Private Property Frequency() As Long

        Get
            Return _Frequency
        End Get
        Set(ByVal Value As Long)
            _Frequency = Value
        End Set

    End Property
    Private Property Increment() As Long

        Get
            Return _Increment
        End Get
        Set(ByVal Value As Long)
            _Increment = Value
        End Set

    End Property
    Private Property StartTime() As Long

        Get
            Return _StartTime
        End Get
        Set(ByVal Value As Long)
            _StartTime = Value
        End Set

    End Property

End Class

« Last Edit: August 02, 2007, 10:42:09 AM by Dave Baranas »

Glenn R

  • Guest
Re: Speed Improvement
« Reply #8 on: July 31, 2007, 05:21:37 AM »
Have you tried NOT using the transaction mechanism/paradigm and use the traditional open/close ARX method?

ie. BlockTableRecord btr = ModelSpaceBlockTableRecordObjectId.Open(yada, yada);

// Dome some funky mojo here

btr.Close();

Using the open/close model can gain you significant speed improvements, however, you have to REALLY REALLY carefull to make sure you close everything you open...as you do in ARX.

Just some thoughts.

Cheers,
Glenn.


DBARANAS

  • Guest
Re: Speed Improvement
« Reply #9 on: August 01, 2007, 12:19:15 AM »
Have you tried NOT using the transaction mechanism/paradigm and use the traditional open/close ARX method?

Using the open/close model can gain you significant speed improvements, however, you have to REALLY REALLY carefull to make sure you close everything you open...as you do in ARX.


Thanks Glenn

I was not able to get at the bt/btr without using tr.GetObject.........
I cannot find a br.open method just a close method and Acad warns it is obsoleted and to use a tr instead.

Do you have any C# code of doing this you could show?....
This is .Net and not COM you mean with the open/close way right......???

After a weekend in the Arx Samples it seems the "F:\ObjectARX 2007\samples\entity\polysamp\" is a good starting place if I have go the .Net/Arx way.

I am thinking about having the Arx project reference the Net project so I can just pass over the Lists and have the DrawEntities method done (C++) over there.....and return the list back to the managed side when the work is done.

This brings up concerns about what kind overhead can occur when passing big lists across net/native boundaries .. I am getting the feeling that this might be really getting done via COM... :-(




It's Alive!

  • Retired
  • Needs a day job
  • Posts: 8698
  • AKA Daniel
Re: Speed Improvement
« Reply #10 on: August 01, 2007, 02:31:32 AM »
Dave, if you think you may need the functionality of both .NET and Native C/C++, you should consider using a mixed managed/unmanaged dll. Of course this would mean using C++ & C++/CLI. The good thing is that you can use a translator to move your existing VB code to C++/CLI. To move your “List” between the managed and unmanaged worlds, you may be able to use a ResultBuffer / *resbuf since it already does the memory pinning and type conversions.

Just a thought

Glenn R

  • Guest
Re: Speed Improvement
« Reply #11 on: August 01, 2007, 06:00:57 AM »
Dave,
I'm actually out of the country at the moment, so I don't have a my laptop handy.

You don't open off an object - you open off an ObjectId, so for instance:

Database db = HostApplicationServices.WorkingDatabase;

BlockTable bt = db.BlockTableId.Open(OpenMode.ForRead, false) as BlockTable;
// Do some mojo
// Don't forget to close
bt.Close();

Then go for a particular blocktablerecord by using the blocktable's indexer. I would show it, but the keyboard I'm using at the moment, is faling to recognise the right square bracket.

This is all off the top of my head, however I think you get the idea.

Cheers,
Glenn.

DBARANAS

  • Guest
Re: Speed Improvement
« Reply #12 on: August 02, 2007, 06:41:15 AM »
Dave,

You don't open off an object - you open off an ObjectId, so for instance:


I was able to get these this way.

Code: [Select]
        Dim bt As AcDb.BlockTable = DirectCast(db.BlockTableId.GetObject(OpenMode.ForWrite), BlockTable)
        Dim br As AcDb.BlockTableRecord = DirectCast(bt.Item(AcDb.BlockTableRecord.ModelSpace.ToString).GetObject( _
        OpenMode.ForWrite, True), BlockTableRecord)


Then I used the bt.Add(br)

This only half works if a transaction has not been commented out even though I am trying to go around using it. Unless I use tr.AddNewlyCreatedDBObject it won't show up in modelspace.

If I don't have a transaction going bt is null and I can't get to br through it.

Another idea is to find something in the Arx samples...maybe ..samples\entity\polysamp\ and put it in a loop and see how much Arx is faster.....and if so then going C++ mixed Arx/Net might be the only way left.

LE

  • Guest
Re: Speed Improvement
« Reply #13 on: August 02, 2007, 10:52:38 AM »
Another idea is to find something in the Arx samples...maybe ..samples\entity\polysamp\ and put it in a loop and see how much Arx is faster.....and if so then going C++ mixed Arx/Net might be the only way left.

Dave;

Most of my functions now are in C++ and with ARX, never had done any salad mixing of managed-unmanaged code... but going to that place just to improve a 7-10 secs? do not know...  :roll:

Here is attached a function in C++ that does a summation of list of lists and a sample list, maybe will provide an idea of the average timing that takes to process a long list inside of a C++ function and in AutoCAD... (the source code of summation() is in the ARX forum)

Load the list from the VLIDE and then in the command line:

Command: (summation lst)

HTH

Glenn R

  • Guest
Re: Speed Improvement
« Reply #14 on: August 04, 2007, 05:41:59 AM »
Dave,

DO NOT mix transactions and the open/close paradigm - you will get unpredictable results. Use one or the other, but not both.

Code: [Select]
// get the current dbase
Database db = HostApplicationServices.WorkingDatabase;

// get the block table
BlockTable bt = db.BlockTableId.Open(OpenMode.ForRead, false) as BlockTable;
if (bt == null)
    return;

// get the modelspace block as an example
ObjectId modelBtrId = bt[BlockTableRecord.ModelSpace];
BlockTableRecord modelBtr = modelBtrId.Open(OpenMode.ForWrite, false) as BlockTableRecord;
if (modelBtr == null)
    return;

// Close blocktable as we don't need it anymore
bt.Close();

// Create a line
Line pLine = new Line();
// Set some properties here...
// blah blah

// Add it to modelspace
modelBtr.AddNewlyCreatedDBObject(pLine);      //can't off the top of my head remember the method name but pretty sure it's AddNewlyCreatedDBObject...

// VERY VERY important - always close what you open
modelBtr.Close();


That should give you the idea. It's essentailly the same thing I posted previously.
Remeber this is all off the top of my head, but I'm sure somebody will correct me if I'm wrong.

Glenn.
« Last Edit: August 04, 2007, 05:43:31 AM by Glenn R »

DBARANAS

  • Guest
Re: Speed Improvement
« Reply #15 on: August 07, 2007, 12:08:39 AM »
Dave,

DO NOT mix transactions and the open/close paradigm - you will get unpredictable results. Use one or the other, but not both.


Thanks Glenn

I was trying to just use ONLY open/close before and now I can this part working without any use of transactions

Code: [Select]
       
Dim bt As BlockTable = DirectCast(db.BlockTableId.Open(OpenMode.ForRead, False), BlockTable)
        Dim id As ObjectId = bt(BlockTableRecord.ModelSpace)
        Dim br As BlockTableRecord = DirectCast(id.Open(OpenMode.ForWrite, False), BlockTableRecord)

'Then make stuff
'then

bt.close


I get 3 warnings....2 saying use "GetObject" instead and the last one on bt.close "Use Transaction Instead"

"AddNewlyCreatedDBObject" is now a transaction method "tr.AddNewly...........

I am wondering with those obsolete warnings ..... they did not break this open/close way to force you to use transactions

So it works except that without having AddNewlyCreatedDBObject nothing shows up in modelspace

This is using 2007
« Last Edit: August 07, 2007, 06:18:55 AM by Dave Baranas »

DBARANAS

  • Guest
Re: Speed Improvement
« Reply #16 on: August 07, 2007, 01:40:41 AM »
Dave;

Most of my functions now are in C++ and with ARX, never had done any salad mixing of managed-unmanaged code... but going to that place just to improve a 7-10 secs? do not know...  :roll:

Here is attached a function in C++ that does a summation of list of lists and a sample list, maybe will provide an idea of the average timing that takes to process a long list inside of a C++ function and in AutoCAD... (the source code of summation() is in the ARX forum)

Load the list from the VLIDE and then in the command line:

Command: (summation lst)

HTH

Thanks Luis, I tried it and it takes less than 2 seconds. I am trying to understand your algorithm to get an idea of how much work is being done.


Glenn R

  • Guest
Re: Speed Improvement
« Reply #17 on: August 07, 2007, 05:30:50 AM »
Like I said, I was doing this off the top of my head. You're right, AddNewlyCreatedDBObject is a transaction method. What you do is add your entities to the appropriate block table record (Model for example), which you would do normally when using a transaction and that's it.

You may have to do a regenall at the end to see things, but I don't think so.

So, something like this:

BlockTableRecord btr = ......
btr.AddEntity(pSomeEntity);  // or whatever the method is called...

Look up the method in the blocktablerecord class.

LE

  • Guest
Re: Speed Improvement
« Reply #18 on: August 07, 2007, 12:03:56 PM »
Thanks Luis, I tried it and it takes less than 2 seconds. I am trying to understand your algorithm to get an idea of how much work is being done.

Dave;

I being asked to port some routines, that were written in Auto/Visual Lisp and some in VB/A to C++/ARX/MFC, many I have to rethink and redo from scratch, but so far the speed for the new routines or commands exceeds all the expectations....

I'm also, doing some of the same functions in C# just for testing - but they do not want their source in this language, (good for me).

Do not know if you might want to rewrite your (whole) application in C++/ARX/MFC - but it will depend on the time you have for that, might be a lot of fun!.... :)

DBARANAS

  • Guest
Re: Speed Improvement
« Reply #19 on: August 08, 2007, 07:30:50 AM »
Do not know if you might want to rewrite your (whole) application in C++/ARX/MFC - but it will depend on the time you have for that, might be a lot of fun!.... :)

If fun is seeing multiple coronary attacks happen simultaneously when I pitch that one :lmao:

I expanded your summation list x10 and it still comes in under ~10 seconds. Super fast but I wonder how lightweight those objects are compared to Solids.
I am going to modify that simple myline Arx Daniel posted in the ARX group so that it loops making solids and compare those results too.   

If that works OK then I will try go mixed/managed so I can keep the UI in .Net and do the AcDb stuff in ARX where I need the speed gain.

Code: [Select]
static void CRPArxProject1_MyLine(void)
{
AcGePoint3d startPt(1.0, 1.0, 0.0);
AcGePoint3d endPt(10.0, 10.0, 0.0);
AcDbLine *pLine = new AcDbLine(startPt, endPt);

        AcDbBlockTable *pBlockTable = NULL;
AcDbDatabase* pDB = acdbHostApplicationServices()->workingDatabase();
pDB->getSymbolTable(pBlockTable, AcDb::kForRead);
AcDbBlockTableRecord* pBlockTableRecord = NULL;
pBlockTable->getAt(ACDB_MODEL_SPACE, pBlockTableRecord, AcDb::kForWrite);
pBlockTable->close();

        AcDbObjectId lineId = AcDbObjectId::kNull;
pBlockTableRecord->appendAcDbEntity(lineId, pLine);
pBlockTableRecord->close();
pLine->close();
}


« Last Edit: August 08, 2007, 08:15:01 AM by Dave Baranas »

LE

  • Guest
Re: Speed Improvement
« Reply #20 on: August 08, 2007, 10:22:36 AM »
I expanded your summation list x10 and it still comes in under ~10 seconds. Super fast but I wonder how lightweight those objects are compared to Solids.
I am going to modify that simple myline Arx Daniel posted in the ARX group so that it loops making solids and compare those results too.   

If that works OK then I will try go mixed/managed so I can keep the UI in .Net and do the AcDb stuff in ARX where I need the speed gain.

I wrote a very simple function to draw three different types of solids, included is the arx with the command: SOLIDS

It will draw 3000 items, see if helps, to me they are drawn fast enough (and they are a very basic solids) :)

Code: [Select]
bool postToDatabase (AcDbObjectId blockId, AcDbEntity *pEntity)
{
assert( pEntity != NULL );
Acad::ErrorStatus es;
AcDbBlockTableRecordPointer pBlk (blockId, AcDb::kForWrite);
if ( (es = pBlk.openStatus ()) != Acad::eOk )
{
acutPrintf(_T("\nError= %s"), acadErrorStatusText(es));
return (false);
}
es = pBlk->appendAcDbEntity (pEntity);
if (es != Acad::eOk)
{
acutPrintf(_T("\nError= %s"), acadErrorStatusText(es));
}
return (es == Acad::eOk);
}

Code: [Select]
static void makeSomeSolids(void)
{
AcDb3dSolid *pBox = new AcDb3dSolid;
if ( pBox->createBox(10, 10, 10) == Acad::eOk )
{
postToDatabase( acdbCurDwg()->currentSpaceId(), pBox );
pBox->close();
}
AcDb3dSolid *pSphere = new AcDb3dSolid;
if ( pSphere->createSphere(20) == Acad::eOk )
{
postToDatabase( acdbCurDwg()->currentSpaceId(), pSphere );
pSphere->close();
}
AcDb3dSolid *pPyramid = new AcDb3dSolid;
if ( pPyramid->createPyramid(10, 4, 10) == Acad::eOk )
{
postToDatabase( acdbCurDwg()->currentSpaceId(), pPyramid );
pPyramid->close();
}
}

Code: [Select]
static void LESQSummation17_SOLIDS(void)
{
int num = 0;
while (num < 1000)
{
makeSomeSolids();
num++;
}
}
« Last Edit: August 08, 2007, 10:24:46 AM by LE »

DBARANAS

  • Guest
Re: Speed Improvement
« Reply #21 on: August 08, 2007, 06:27:30 PM »
Thanks Luis! for that source, and Daniel, Glenn, and Mick too.

I added an xform and stripped out the spheres and Pyramids which greatly sped things up a lot....lucky for me I just use a few of those.

I played with it and now can say for sure that Arx is way[5X] faster. On average it's 15 sec for 5,000 rectangular solids, 45 sec for 10,000 and 150 sec for 20,000. I did 50,000 before I ran out of memory[1G]. I am starting to see the possibilities hardware coming into the model at the 5,000 level for extra detail when needed with this kind of performance

After 25% into them the hourglass pointer starts flickering until finished which I think is the cheap video card choking to keep up.

Being very green at this I am wondering about if you can get an entry point to an Arx file like lot's of the way's it's done with Dll's and Exe's. Maybe it's as easy as doing a DllImport and passing lists back and forth.

Anyway another Pandora's box to open on the way to the end. Thanks again everyone.




MickD

  • King Gator
  • Posts: 3636
  • (x-in)->[process]->(y-out) ... simples!
Re: Speed Improvement
« Reply #22 on: August 08, 2007, 07:19:05 PM »
PInvoke my be a way, just write your arx functions as C style functions(ie. not part of a class) and you can PInvoke them easily enough. It's actually not that hard to write a managed wrapper either, though passing your entity data back and forth will take a bit of work (type marshaling).

I think the quickest way to do it though would be to pass your list of data to ARX and let it create all the entities without crossing from .net to native with every entity creation, that's the bottle neck. You may want a list of entity ID's that are created, in that case a wrapper would be the way to go I think.
"Programming is really just the mundane aspect of expressing a solution to a problem."
- John Carmack

"Short cuts make long delays,' argued Pippin.”
- J.R.R. Tolkien

It's Alive!

  • Retired
  • Needs a day job
  • Posts: 8698
  • AKA Daniel
Re: Speed Improvement
« Reply #23 on: August 08, 2007, 11:50:22 PM »
IMO, you need to make a managed wrapper. Once you have done this you can either reference your new .NET assembly as you would any other, or start adding your UI code using C++/CLI. Either way you would need to wrap your code. I am not sure the format of your list, but there already may be methods you can use from the built-in managed wrappers. For example if you can make your native code except a resbuf as an argument, you may be able to use that as your communication link. It’s already wrapped.

PS. If your going to be doing a lot of this type of work in the future, you might want to take the time to learn C++/CLI and making managed wrappers

LE

  • Guest
Re: Speed Improvement
« Reply #24 on: August 09, 2007, 09:59:56 AM »
the two previous comments by the masters, make it sound, that it is very easy.... :)

I started reading this document - among some others - might help you:

http://www.gotw.ca/publications/C++CLIRationale.pdf

MickD

  • King Gator
  • Posts: 3636
  • (x-in)->[process]->(y-out) ... simples!
Re: Speed Improvement
« Reply #25 on: August 09, 2007, 06:03:09 PM »
the two previous comments by the masters, make it sound, that it is very easy.... :)
...

It's not that easy at first, you still need to learn how it works but once you've worked that out away you go..
The best approach would be to use the arx app wizard to create a basic managed dll for you, to handle marshaling the different acad types there are some functions in the 'mgdinterop.h' that comes with the sdk, this is the key!

It's been a while since I have used a wrapper but the sdk will have some samples by now I'd imagine, if you need a hand working it out just holler.
"Programming is really just the mundane aspect of expressing a solution to a problem."
- John Carmack

"Short cuts make long delays,' argued Pippin.”
- J.R.R. Tolkien

Glenn R

  • Guest
Re: Speed Improvement
« Reply #26 on: August 13, 2007, 09:08:39 PM »
2.8 GHz P4
1.0 Gb ram

To create 10000 3dsolid boxes takes 3 seconds using the open/close paradigm as demonstrated below:

Code: [Select]
[CommandMethod("MakeSolids")]
static public void tcgsMakeSolidsCommand( )
{
// We're simply going to add solids to modelspace

Document doc = acadApp.DocumentManager.MdiActiveDocument;
Database db = doc.Database;
Editor ed = doc.Editor;

BlockTable bt = db.BlockTableId.Open(OpenMode.ForRead, false) as BlockTable;
if(bt == null)
return;

// Get modelspace btr
BlockTableRecord modelspace = bt[BlockTableRecord.ModelSpace].Open(OpenMode.ForWrite, false) as BlockTableRecord;
if(modelspace == null)
return;

// Close blocktable as we don't need it anymore
bt.Close();

DateTime start = DateTime.Now;

for(int i = 0; i < 10000; i++)
{
using(Solid3d sol = new Solid3d())
{
sol.CreateBox(100.0, 50.0, 10.0);
modelspace.AppendEntity(sol);
}
}

// Close modelspace
modelspace.Close();

DateTime finish = DateTime.Now;

TimeSpan elapsed = finish - start;

ed.WriteMessage("\nElapsed time: {0} hours, {1} minutes, {2} seconds.", elapsed.Hours, elapsed.Minutes, elapsed.Seconds);

}

Glenn R

  • Guest
Re: Speed Improvement
« Reply #27 on: August 14, 2007, 12:26:48 AM »
...and the equivalent transactional code (below) to the above, takes 6.4 seconds...double the time.

Code: [Select]
Document doc = acadApp.DocumentManager.MdiActiveDocument;
Database db = doc.Database;
Editor ed = doc.Editor;

DateTime start = DateTime.Now;

Transaction tr = db.TransactionManager.StartTransaction();

using(tr)
{
BlockTable bt = tr.GetObject(db.BlockTableId, OpenMode.ForRead, false) as BlockTable;
if(bt == null)
return;

BlockTableRecord modelspace = tr.GetObject(bt[BlockTableRecord.ModelSpace], OpenMode.ForWrite, false) as BlockTableRecord;
if(modelspace == null)
return;

for(int i = 0; i < 10000; i++)
{
using(Solid3d sol = new Solid3d())
{
sol.CreateBox(100.0, 50.0, 10.0);
modelspace.AppendEntity(sol);
tr.AddNewlyCreatedDBObject(sol, true);
}
}

tr.Commit();
}

DateTime finish = DateTime.Now;

TimeSpan elapsed = finish - start;

ed.WriteMessage("\nElapsed time: {0} hours, {1} minutes, {2}.{3} seconds.", elapsed.Hours, elapsed.Minutes, elapsed.Seconds, elapsed.Milliseconds);

Glenn R

  • Guest
Re: Speed Improvement
« Reply #28 on: August 14, 2007, 12:39:07 AM »
Just for kicks, I added 2 more solids; a wedge and a sphere, into the 10000 iteration loop, thereby effectively creating 30000 3d solids.

Using a transaction, the elapsed time was 36.702 seconds.
Using open/close, the elapsed time was 23.843 seconds.

MickD

  • King Gator
  • Posts: 3636
  • (x-in)->[process]->(y-out) ... simples!
Re: Speed Improvement
« Reply #29 on: August 14, 2007, 12:57:24 AM »
~30% saving, not bad!

I think his biggest hurdle though could be all the transformations, maybe these take even longer than a simple solid creation perhaps??
"Programming is really just the mundane aspect of expressing a solution to a problem."
- John Carmack

"Short cuts make long delays,' argued Pippin.”
- J.R.R. Tolkien

Glenn R

  • Guest
Re: Speed Improvement
« Reply #30 on: August 14, 2007, 01:22:37 AM »
We won't know until he tests it Mick - be interesting to see.

I just posted the code as I thought he was not getting what I was on about...

DBARANAS

  • Guest
Re: Speed Improvement
« Reply #31 on: August 15, 2007, 06:20:49 PM »
We won't know until he tests it Mick - be interesting to see.

I just posted the code as I thought he was not getting what I was on about...

I plugged it in and found ~30% gain over transactions. Timing is +/- 50mSec instead of +/- 3 seconds

I could not get it to work before because I was Dim'ing instead of Using, once I changed that then everything showed up in modelspace.

Thanks Glenn

MickD

  • King Gator
  • Posts: 3636
  • (x-in)->[process]->(y-out) ... simples!
Re: Speed Improvement
« Reply #32 on: August 15, 2007, 06:35:39 PM »
We won't know until he tests it Mick - be interesting to see.
...

Seems like that's the ticket Glenn, nice one!
"Programming is really just the mundane aspect of expressing a solution to a problem."
- John Carmack

"Short cuts make long delays,' argued Pippin.”
- J.R.R. Tolkien