Speed issues when creating a lot of dynamic blocks

Speed issues when creating a lot of dynamic blocks

MGO-Norsyn
Advocate Advocate
1,256 Views
10 Replies
Message 1 of 11

Speed issues when creating a lot of dynamic blocks

MGO-Norsyn
Advocate
Advocate

Hi all

I have a command where I create many dynamic blocks, let's say 1800. The first 200 go fairly fast, but the following blocks go slower and slower. So the whole operation takes around 40 minutes for this amount of blocks.

Does anyone know why this happens?

0 Likes
1,257 Views
10 Replies
Replies (10)
Message 2 of 11

kerry_w_brown
Mentor
Mentor

@MGO-Norsyn 

Could it be that you're choking the UNDO mechanism and Transaction(s).

You may need to provide sample code.

Stay well,

 


// Called Kerry or kdub in my other life.

Everything will work just as you expect it to, unless your expectations are incorrect. ~ kdub
Sometimes the question is more important than the answer. ~ kdub

NZST UTC+12 : class keyThumper<T> : Lazy<T>;      another  Swamper
0 Likes
Message 3 of 11

MGO-Norsyn
Advocate
Advocate

Thank you for your reply.

At first I was creating all blocks inside one transaction. Then I thought maybe adding so many objects to one transaction is slowing it down, so I've split the creation of blocks into chunks of 25 and each chunk has its own transaction. And that didn't have any effect on execution time.

In my undo history only the name of my command displays after the command is finished and not individual 1800 block additions.

I have tried to find the chokepoint and as far as I can say, it is the line var var br = new BlockReference(pt, btrId); that is responsible for the slow down.

My code is kinda long and is not very friendly to look at, but the basic stuff as I've described.

 

Maybe I should not use dynamic blocks to do this? Right now I am using dynamic user parameters to store some data, but maybe I should just use vanilla blocks with attributes? I might think that dynamic blocks maybe have some hidden performance deficiency?

0 Likes
Message 4 of 11

kerry_w_brown
Mentor
Mentor

>>>  I might think that dynamic blocks maybe have some hidden performance deficiency?

They do. each block is different, and has unique name, even if they look the same.
Conventional block inserts use less database space. 

The UNDO I mentioned is associated with the transaction.

 

wild assed guess: Perhaps try wrapping the  new BlockReference in a using statement so each one auto-disposes.

I assume you're assigning each one in a loop inside a transaction . . . just add each to the transaction in the loop.
I think that will work . . . I'm a little brain dead at the moment.
May even be worth trying  having a new  transaction. That will give you a baseine to compare against.

 

Tony or Gilles or Norman Et al may have a suitable idea.

Stay well,

 


// Called Kerry or kdub in my other life.

Everything will work just as you expect it to, unless your expectations are incorrect. ~ kdub
Sometimes the question is more important than the answer. ~ kdub

NZST UTC+12 : class keyThumper<T> : Lazy<T>;      another  Swamper
0 Likes
Message 5 of 11

MGO-Norsyn
Advocate
Advocate

Thank you for your reply.

My creation of blocks happen in this way:

using (Transaction tx = etc...)
{
    try
    {
        //Create blocks in a loop.
        //So I don't have one transaction per block.
        //I just didn't write the nested loop here.

        //As shown in developer guide using usings.
        using (var br = new BlockReference(etc...))
        {
            //assign rotation and layer
            modelSpace.AppendEntity(br);
            tx.AddNewlyCreatedDBObject(br, true);

            foreach (var attDef in attDefs)
            {
                using (var attRef = new AttributeReference())
                {
                    attRef.SetAttributeFromBlock(attDef, wpBr.BlockTransform);
                    attRef.Position = attDef.Position.TransformBy(wpBr.BlockTransform);
                    attRef.TextString = attDef.getTextWithFieldCodes();
                    attRef.Layer = textLayerName;
                    wpBr.AttributeCollection.AppendAttribute(attRef);
                    tx.AddNewlyCreatedDBObject(attRef, true);
                }
            }

            //Do some more stuff

            //Here I set the dynamic block properties
        }
    }
    catch
    {
        tx.Abort();
        //exit or continue
    }
    tx.Commit();
}

 

I tried adding all blocks to one transaction, then splitting the creation in chunks so I have 25 blocks per transaction --> same result.

 

When creating many blocks the time to create each next block is linearly rising. See the graph in the picture.

MGONorsyn_0-1737633528518.png

 

0 Likes
Message 6 of 11

ActivistInvestor
Mentor
Mentor

@MGO-Norsyn wrote:

Thank you for your reply.

At first I was creating all blocks inside one transaction. Then I thought maybe adding so many objects to one transaction is slowing it down, so I've split the creation of blocks into chunks of 25 and each chunk has its own transaction. And that didn't have any effect on execution time.

If all of the insertions you're creating are graphically-distinct (meaning that a separate anonymous block must be defined for each), that might explain why the operation is bogging down.

 

I couldn't tell from your code if each transaction that processes a chunk is a nested transaction, or if they are all outer-most transactions, but that definitely matters. Nested transactions aren't really transactions at all. in reality there is only one transaction (the 'outer-most' one), and nested transactions are just undo markers. So, if you are nesting the transactions, then try not doing that to see if there is any difference (although that might result in a separate UNDO group for each insertion, which may not be acceptable).

 

You can also try disabling undo recording for the Database, although undo information is filed out to a file in the temp folder.

 

Another thing you can try is creating the dynamic block references in a temporary in-memory Database and then inserting them into the active document.

0 Likes
Message 7 of 11

MGO-Norsyn
Advocate
Advocate

I've just realized that the transaction is nested, as I have a transaction when the command is called...

0 Likes
Message 8 of 11

MGO-Norsyn
Advocate
Advocate

No, it doesn't help splitting the transaction (top, outer transaction) into chunks. The running time is the same.

 

And I couldn't create blocks in a in-memory database, it was giving me eWrongDatabase error. Very strange, I don't usually have problems creating blocks in in-memory databases.

 

using (Transaction tTx = tempDb.TransactionManager.StartTransaction())
{
    try
    {
        #region Prepare block table record and attributes
        Oid modelspaceId = SymbolUtilityServices.GetBlockModelSpaceId(tempDb);
        BlockTableRecord modelSpace = modelspaceId.Go<BlockTableRecord>(tTx, OpenMode.ForWrite);

        //Prepare block table record
        BlockTable bt = tempDb.BlockTableId.Go<BlockTable>(tTx);
        if (!bt.Has(blockName)) throw new System.Exception("Block is missing!");
        Oid btrId = bt[blockName];
        BlockTableRecord btrWp = btrId.Go<BlockTableRecord>(tTx);
        List<AttributeDefinition> attDefs = new List<AttributeDefinition>();
        foreach (Oid arOid in btrWp)
        {
            if (!arOid.IsDerivedFrom<AttributeDefinition>()) continue;
            AttributeDefinition at = arOid.Go<AttributeDefinition>(tTx);
            if (!at.Constant) attDefs.Add(at);
        }
        #endregion

        foreach (var cluster in chunk)
        {
            //some code

            using (var wpBr = new BlockReference(wpt, btrId))
            {
                wpBr.Rotation = cluster.First().Rotation;
                wpBr.Layer = blockLayerName;

                Oid id = modelSpace.AppendEntity(wpBr); <-- eWrongDatabase here. What? why?
                tTx.AddNewlyCreatedDBObject(wpBr, true);

                foreach (var attDef in attDefs)
                {
                    using (var attRef = new AttributeReference())
                    {
                        attRef.SetAttributeFromBlock(attDef, wpBr.BlockTransform);
                        attRef.Position = attDef.Position.TransformBy(wpBr.BlockTransform);
                        attRef.TextString = attDef.getTextWithFieldCodes();
                        attRef.Layer = textLayerName;
                        wpBr.AttributeCollection.AppendAttribute(attRef);
                        tTx.AddNewlyCreatedDBObject(attRef, true);
                    }
                }
0 Likes
Message 9 of 11

Norman_Yuan
Mentor
Mentor

@MGO-Norsyn 

You did not say: are these dynamic block references of thousand-ish from the same dynamic block definition, or very limited number of the dynamic block definitions (i.e. each dynamic block definition is used for hundreds or thousands references with variations of property values)? I assume this is your case.

 

I had the same experience of slowness: I worked with a drawing model that has a very long "alignment" type facilities and a quite complicated dynamic block is used as some sort of label with many dynamic properties. By concept from user's point view, it seems great: user only deal with 1 single block. But AutoCAD has to deal with thousands of anonymous block definitions and references every time a block reference is created and one of its property is set.

 

I have no idea of the details of how AutoCAD creates the anonymous block definitions behind scene. But the rapidly increased slowness when the count of references to the same dynamic block definition increases suggests that whenever a dynamic property value of a block reference changes, AutoCAD may scan ALL anonymous block definitions related to the dynamic block definition to find out if there is an existing anonymous block definition suits the changed property value; if yes, the existing anonymous block definition is used; otherwise, a new one is created. The intention could be that to minimize the number of anonymous block definition being created, thus save a bit of drawing size. However, if the count of the block reference is large, the constant searching/comparing would be excessively costly. Obviously, if too many properties are defined, it only makes thing worse.

 

Again, it is just my speculation, but seems explaining what I (and you) experienced the slowness. IMO, using excessive number of dynamic block reference from the same dynamic block definition with all different property values would not be a good AutoCAD practice.

 

You might want to verify if the slowness situation be much better when you insert the block reference without setting their dynamic properties (i.e. keep the default dynamic property values, this way all the block references are the references of the original block definition, no anonymous block definitions are created).

 

 

Norman Yuan

Drive CAD With Code

EESignature

Message 10 of 11

MGO-Norsyn
Advocate
Advocate

Thank you for your answer, I will try to switch to ordinary blocks then...

 

It seems to be the best way to use Autocad is to not draw anything at all...

0 Likes
Message 11 of 11

MGO-Norsyn
Advocate
Advocate

Sorry, I forgot to report that using ordinary blocks with attributes fixes the problem.

The creation of 1800+ dynamic blocks that would take 40 mins., now takes a couple of seconds with ordinary blocks.

0 Likes