AutoCAD .NET provides two ways to open objects, the modern transaction way and the legacy direct opening. Quite some people love the latter and advocate it here and there stating that the transaction overheads could be avoided so the performance will be improved. Another fact attracting people to use it may be that the ‘for advanced use only’ intellisense tool tip for the ObjectId.Open method.
The question is: Does the obsolete ‘advanced’ ObjectId.Open really beat the more .NET compliant and friendly Transaction.GetObject()?
Let’s do a small experiment with VB.NET and AutoCAD .NET to answer it in this post.
In terms of time taken by a single ObjectId.Open call or a single Transaction.GetObject against a single ObjectId, we don’t bother to measure here since both would take almost no time. Let us focus on some more meaningful cases such as processing a single object repeatedly and batch processing many different objects at a single moment.
The following VB.NET and AutoCAD .NET code is to open the same object for 10 thousand times, using the two approaches.
<CommandMethod("TransactionOrOpenDirect")> _
Public Shared Sub TransactionOrOpenDirect_Method()
Dim ed As Editor = MgdAcApplication.DocumentManager.MdiActiveDocument.Editor
Dim db As Database = HostApplicationServices.WorkingDatabase
Try
Dim dbTM As Autodesk.AutoCAD.DatabaseServices.TransactionManager = db.TransactionManager
Dim id As ObjectId = Autodesk.AutoCAD.Internal.Utils.EntLast()
If id.IsValid Then
Dim watch As New Stopwatch()
watch.Restart()
For i As Integer = 0 To 9999
Using ent As Entity = DirectCast(id.Open(OpenMode.ForWrite), Entity)
ent.ColorIndex = i Mod 256
End Using
Next
watch.Stop()
ed.WriteMessage("Time elapsed in opening direct: {0}" & vbLf, watch.Elapsed.TotalMilliseconds)
watch.Restart()
Using tr As Transaction = dbTM.StartTransaction()
For i As Integer = 0 To 9999
Dim ent As Entity = DirectCast(tr.GetObject(id, OpenMode.ForWrite), Entity)
ent.ColorIndex = i Mod 256
Next
tr.Commit()
End Using
watch.Stop()
ed.WriteMessage("Time elapsed in transation: {0}" & vbLf, watch.Elapsed.TotalMilliseconds)
Else
ed.WriteMessage(vbLf & "There is no entity at all in the drawing.")
End If
Catch ex As System.Exception
MgdAcApplication.DocumentManager.MdiActiveDocument.Editor.WriteMessage(ex.ToString())
End Try
End Sub
Here is the output:
Command: TransactionOrOpenDirect
Time elapsed in opening direct: 236.2197
Time elapsed in transation: 116.7412
As can be seen, the ObjectId.Open took more than twice as the Transaction.GetObject in this scenario.
Now let’s look at another scenario, iterating through huge amount of entities of the current drawing (15 thousand circles in our test case) using the two approaches. Here is the test code and command.
<CommandMethod("TransactionOrOpenDirect2")> _
Public Shared Sub TransactionOrOpenDirect2_Method()
Dim ed As Editor = MgdAcApplication.DocumentManager.MdiActiveDocument.Editor
Dim db As Database = HostApplicationServices.WorkingDatabase
Try
Dim watch As New Stopwatch()
Dim index As Integer = 0
watch.Restart()
Using btr As BlockTableRecord = DirectCast(SymbolUtilityServices.GetBlockModelSpaceId(db).Open(OpenMode.ForRead), BlockTableRecord)
For Each id As ObjectId In btr
Using ent As Entity = DirectCast(id.Open(OpenMode.ForWrite), Entity)
ent.ColorIndex = index Mod 256
End Using
index += 1
Next
End Using
watch.Stop()
ed.WriteMessage("Time elapsed in ObjectId.Open {0} entites: {1}" & vbLf, index, watch.Elapsed.TotalMilliseconds)
index = 0
watch.Restart()
Using tr As Transaction = db.TransactionManager.StartTransaction()
Dim btr As BlockTableRecord = DirectCast(tr.GetObject(SymbolUtilityServices.GetBlockModelSpaceId(db), OpenMode.ForRead), BlockTableRecord)
For Each id As ObjectId In btr
Dim ent As Entity = DirectCast(tr.GetObject(id, OpenMode.ForWrite), Entity)
ent.ColorIndex = index Mod 256
index += 1
Next
End Using
watch.Stop()
ed.WriteMessage("Time elapsed in Transaction.GetObject {0} entites: {1}" & vbLf, index, watch.Elapsed.TotalMilliseconds)
Catch ex As System.Exception
MgdAcApplication.DocumentManager.MdiActiveDocument.Editor.WriteMessage(ex.ToString())
End Try
End Sub
Here is the output again.
Command: TransactionOrOpenDirect2
Time elapsed in ObjectId.Open 15000 entites: 26181.1386
Time elapsed in Transaction.GetObject 15000 entites: 19114.8809
The Transaction.GetObject won again! Things are clear now, aren’t they?
That is, the Transaction.GetObject has its very reason to replace the legacy ObjectId.Open in the AutoCAD .NET API and the ObjectId.Open has its very reason to be marked as deprecated. In addition, using something that is marked as ‘for advanced use only’ does not necessarily mean that the use is advanced!
The leading edge AutoCAD .NET Addin Wizard (AcadNetAddinWizard) provides various project wizards, item wizards, coders and widgets to help program AutoCAD .NET addins.
Hey Siderinnet1,
The little testing I did I never saw a gain using open method unless opening a small amount of objects, but as you mentioned the time is so small it is negabile. Its great to see you confirm this. Also if you create a local variable for TransactionManager in TransactionOrOpenDirect2 and then in both methods use TransactionManager.GetObject() it should speed it up even more.
Thanks again for this blog and all the great information.
Posted by: JeffH | 02/11/2015 at 07:21 PM
Jeff, thanks for the confirmation and information. We changed the code accordingly and saw the extra performance gain for the GetObject call, as you said. You are welcome as always.
Posted by: Spiderinnet1 | 02/11/2015 at 08:55 PM