by Guy Robinson
17. April 2010 16:35
API performance has always been of interest, even more so as large projects regularly hit >300Mb now. With the new API’s such as updaters and the DocumentChanged event performance testing has got a lot more complicated. Throw in transaction and regeneration issues and it soon became obvious a different approach was required. I’m not ready to talk about this approach but I thought a little test on transactions speaks volumes about some of the additional design decisions API authors need now consider.
The test comprises 3 commands. One doing a single loop with a single transaction. The second uses a subtransaction per loop. The third one full transaction per loop. each command was run 10 times to average out variation per run. Here’s the results:
| Run | Single Transaction (ms) | SubTransaction (ms) | Full Transaction (ms) |
| 1 | 795 | 1544 | 73085 |
| 2 | 852 | 1553 | 72935 |
| 3 | 778 | 1556 | 72797 |
| 4 | 784 | 1554 | 73170 |
| 5 | 781 | 1536 | 73837 |
| 6 | 789 | 1564 | 72334 |
| 7 | 787 | 1533 | 71912 |
| 8 | 778 | 1541 | 71782 |
| 9 | 793 | 1550 | 73472 |
| 10 | 777 | 1524 | 73290 |
| Average | 791.4 | 1545.5 | 72861.4 |
So as you can see subtransactions were twice as slow , this is an excellent result considering 10000 were committed against only one. However as expected the majority of the overhead in the single transaction test was the committing of the single transaction, as can be seen by the Full transaction test being ~9000% slower for 10000 transactions.
So in conclusion I feel we can say:
“Utilise subtransactions as required without fear of serious performance degradation. Be very careful when considering multiple transactions within a single command cycle.”
Update: Arnošt who is part of the API team at Autodesk left a thorough comment below. So I've added his conclusion here:
“Do not use neither sub-transactions nor transactions unless you need them, but use them when the end-user benefits from having them..”
And here’s the code:
Single Transaction:
try
{ var doc = commandData.Application.ActiveUIDocument.Document;
const int LOOPS = 10000;
Stopwatch sw = Stopwatch.StartNew();
var trans = new Transaction(doc, "transtest");
trans.Start();
for (int i = 0; i < LOOPS; i++)
{ doc.ProjectInformation.Name = "transtest" + i;
}
trans.Commit();
sw.Stop();
message = string.Format("Elapsed time for {0}== {1}", LOOPS, sw.ElapsedMilliseconds); return Result.Failed;
}
catch (Exception ex)
{ message = ex.Message + ex.StackTrace;
return Result.Failed;
}
Subtransaction:
try
{ var doc = commandData.Application.ActiveUIDocument.Document;
const int LOOPS = 10000;
Stopwatch sw = Stopwatch.StartNew();
var trans = new Transaction(doc, "transtest");
trans.Start();
for (int i = 0; i < LOOPS; i++)
{ var subTrans = new SubTransaction(doc);
subTrans.Start();
doc.ProjectInformation.Name = "transtest" + i;
subTrans.Commit();
}
trans.Commit();
sw.Stop();
message = string.Format("Elapsed time for {0}== {1}", LOOPS, sw.ElapsedMilliseconds); return Result.Failed;
}
catch (Exception ex)
{ message = ex.Message + ex.StackTrace;
return Result.Failed;
}
Full Transaction:
try
{ var doc = commandData.Application.ActiveUIDocument.Document;
const int LOOPS = 10000;
Stopwatch sw = Stopwatch.StartNew();
for (int i = 0; i < LOOPS; i++)
{ var trans = new Transaction(doc, "transtest");
trans.Start();
doc.ProjectInformation.Name = "transtest" + i;
trans.Commit();
}
sw.Stop();
message = string.Format("Elapsed time for {0}== {1}", LOOPS, sw.ElapsedMilliseconds); return Result.Failed;
}
catch (Exception ex)
{ message = ex.Message + ex.StackTrace;
return Result.Failed;
}
Cheers,
Guy