... Here's mine:Code - Text: [Select]
Command: rndpoints Number of points: 500000 Command: tt1 AcTrTransaction: 2004 Command: tt2 OpenCloseTransaction: 2858 Command: tt1 AcTrTransaction: 1881 Command: tt2 OpenCloseTransaction: 2849 Command: tt1 AcTrTransaction: 1933 Command: tt2 OpenCloseTransaction: 2853
Using Dell Precision M6600, [Quad-]Core i7 @ 2.3 Ghz, 16GB RAM, Win7 Enterprise 64-Bit, tested in Civil 3D 2011.
... Here's mine:Code - Text: [Select]
Command: rndpoints Number of points: 500000 Command: tt1 AcTrTransaction: 2004 Command: tt2 OpenCloseTransaction: 2858 Command: tt1 AcTrTransaction: 1881 Command: tt2 OpenCloseTransaction: 2849 Command: tt1 AcTrTransaction: 1933 Command: tt2 OpenCloseTransaction: 2853
Using Dell Precision M6600, [Quad-]Core i7 @ 2.3 Ghz, 16GB RAM, Win7 Enterprise 64-Bit, tested in Civil 3D 2011.
BlackBox, thanks. If I'm not mistaken, in earlier releases of AutoCAD, the OpenCloseTransaction was implemented entirely in managed code, which would explain the lop-sided result you're getting. I'm not sure what release it was, that they moved it to native code, but as of 2012 it is.
... I have yet to see the OpenCloseTransaction do anything any faster than a regular transaction. Perhaps with write-operations it may be different but I've yet to do any testing.
[OpenCloseTransaction] may be used instead of the transaction class in certain scenarios.. It wraps the ObjectId.Open/Close functions, but makes it easier to correctly pair these functions by storing references to every object opened and automatically closing them.
... I have yet to see the OpenCloseTransaction do anything any faster than a regular transaction. Perhaps with write-operations it may be different but I've yet to do any testing.
Forgive my asking... If there is no veritable speed difference, then what benefit is there to:Quote from: ArxMgd.chm, OpenCloseTransaction Class[OpenCloseTransaction] may be used instead of the transaction class in certain scenarios.. It wraps the ObjectId.Open/Close functions, but makes it easier to correctly pair these functions by storing references to every object opened and automatically closing them.
Is there any 'best practice' as to when one should use one Transaction over the other?
I vaguely recall you and either Kean, or Stephen discussing something about this, perhaps I should find and re-read that as well.
Cheers
Many thanks for taking the time to entertain my curiosity about Transactions, Tony... I'll definitely be referring to that post moving forward.
Cheers
Hi Tony,
This is not any news to you as I have seen you mention this and cache the TransactionManager property of Transaction class, but doing so does change the results.
At bottom of post for anyone interested I added code to show how caching TransactionManager aligns functions calls similar to OpenCloseTrasaction.
For a small number of objects could not get them to register any time, but looks like OpenCloseTransaction on my system will give you whopping 1ms edge(500 objects) before a regular transaction begins to catch up(1000 objects)
As the number of objects increases the Transaction starts biatch slapping the OpenCloseTransaction where no timer is needed to tell the transaction performs much better.
Here are the only changes I made to original code which was changing "tr.GetObject" to "acTm .GetObject"Code - C#: [Select]Here are the results and these were consistently producing same results.
using AcApTransactionManager = Autodesk.AutoCAD.ApplicationServices.TransactionManager; ////// ////.... ///// [CommandMethod("TT1")] public static void TestAcTrTransaction() { Document doc = Application.DocumentManager.MdiActiveDocument; Database db = doc.Database; Editor ed = doc.Editor; AcApTransactionManager acTm = doc.TransactionManager; using (Transaction tr = acTm.StartTransaction()) { BlockTableRecord btr = (BlockTableRecord) acTm.GetObject(db.CurrentSpaceId, OpenMode.ForRead); int i = 0; Stopwatch sw = Stopwatch.StartNew(); foreach (ObjectId id in btr) { if (id.ObjectClass == dbPointClass) { DBPoint p = (DBPoint)acTm.GetObject(id, OpenMode.ForRead); ++i; } } sw.Stop(); ed.WriteMessage("\nAcTrTransaction: {0}", sw.ElapsedMilliseconds); tr.Commit(); } } Code - Text: [Select]
Command: RNDPOINTS Number of points: 100 Command: TT1 AcTrTransaction: 0 Command: TT2 OpenCloseTransaction: 0 Command: RNDPOINTS Number of points: 500 Command: TT1 AcTrTransaction: 1 Command: TT2 OpenCloseTransaction: 0 Command: RNDPOINTS Number of points: 1000 Command: TT1 AcTrTransaction: 2 Command: TT2 OpenCloseTransaction: 2 Command: RNDPOINTS Number of points: 100000 Command: TT1 AcTrTransaction: 209 Command: TT2 OpenCloseTransaction: 260 Command: TT1 AcTrTransaction: 210 Command: TT2 OpenCloseTransaction: 259 Command: RNDPOINTS Number of points: 500000 Command: TT1 AcTrTransaction: 1111 Command: TT2 OpenCloseTransaction: 2298 Command: TT1 AcTrTransaction: 1094 Command: TT2 OpenCloseTransaction: 2285
To see why here is a OpenCloseTransactionCode - C#: [Select]With a transaction notice how in GetObject it makes a call to CheckTopTransaction() then uses its TransactionManager property which creates new wrapper to call GetObjectInternal() that is more comparable to OpenCloseTransaction GetObject().
public override unsafe DBObject GetObject(ObjectId id, OpenMode mode, [MarshalAs(UnmanagedType.U1)] bool openErased, [MarshalAs(UnmanagedType.U1)] bool forceOpenOnLockedLayer) { AcDbObject* objPtr; int num = AcMgOpenCloseTransaction.getObject(this.GetImpObj(), &objPtr, (AcDbObjectId) ((long) &id), (AcDb.OpenMode) mode, openErased, forceOpenOnLockedLayer); if (num != 0) { } return (DBObject) RXObject.Create((IntPtr) objPtr, false); }
I am not sure why they create a new wrapper for each use of the TransactionManager property.Code - C#: [Select]I never really understood the different results for large number of objects but it would now make sense if it was caused from GC from all new wrappers created.
public virtual unsafe DBObject GetObject(ObjectId id, OpenMode mode) { this.CheckTopTransaction(); return TransactionManager.GetObjectInternal(AcDbImpTransaction.transactionManager((AcDbImpTransaction* modopt(IsConst) modopt(IsConst)) this.GetImpObj()), id, mode, false, false); } public virtual TransactionManager TransactionManager { get { IntPtr unmanagedPointer = new IntPtr(AcDbImpTransaction.transactionManager((AcDbImpTransaction* modopt(IsConst) modopt(IsConst)) this.GetImpObj())); return (TransactionManager) RXObject.Create(unmanagedPointer, false); } } internal static unsafe DBObject GetObjectInternal(AcDbTransactionManager* pTM, ObjectId id, OpenMode mode, [MarshalAs(UnmanagedType.U1)] bool openErased, [MarshalAs(UnmanagedType.U1)] bool forceOpenOnLockedLayer) { AcDbObject* objPtr; int num = GetObjectInTransaction(&objPtr, pTM, (AcDbObjectId) ((long) &id), (AcDb.OpenMode) mode, openErased, forceOpenOnLockedLayer); if (num != 0) { } return (DBObject) RXObject.Create(unmanagedPointer, false); }
Tony and Jeff,
So does that mean you guys are going to start using:
TransactionManager.GetObject
Instead of the old standard:
var tr=TransactionManager.StartTransaction
tr.GetObject
Are we talking about transactions inside an event handler, or event handlers that call methods which start transactions, or both?
Specifically Editor.EnteringQuiescentState. Inside the handler I have code that may or may not call methods that start traditional transactions. Is that a nono?
EDIT:
The first thing I do in the handler is remove the handler.
Ah, thanks for the insight. So far my transactions are being undone along with the command that ended which is desired in my case but it's good to know that it's something to watch out for.