Jump to content
Sign in to follow this  

VectorScript performance profiling

Recommended Posts

I have some fairly hefty scripts which operate on thousands of objects and I would obviously like to optimise the performance as much as possible.

I take care with my dynamic arrays and things like that but are some operations typically slower than others?

Is it better to create and destroy an object where you need it or move it around? Are WHILE loops faster than ForEachObjectInList?

Do criteria searches slow things down or not?

Are there any consistent methods for profiling to see where your script spends a lot of time?



Share this post

Link to post

Hi Joel,

"Good performance" is going to require something different in every circumstance, and without seeing the code, there's no way of knowing where the optimization opportunities might be. Nevertheless, there are some general guidelines.

You're right about dynamic arrays. Re-allocating a dynamic array forces the creation of a new array of different dimensions, copying the contents of the old array into the new one, and then cleaning up the old one. If this is happening thousands of times in a script, you can see a hit because of it. So try to re-allocate as little as possible. If you know you'll need 1000 elements, allocate the array for 1200, and later, if you find you need more, add another 200 elements. Don't re-allocate the array every time you add an element.

In a script that operates on PIOs in the drawing, don't reset the other objects if you don't have to. You might be doing SetRFields to poke new data in, but does that really require also calling ResetObject? (Sometimes it's just data that will appear in a worksheet somewhere, and there is actually no reason to reset the object.) Also, sometimes it makes a lot of sense to check the existing values before poking the new values in, and if the values aren't actually changing, then there's no need for a reset. You can save a lot of execution time this way.

As a general rule, math is much faster (at run-time) than construct geometry (though it's a tad slower at design-time). :) Creating and destroying objects involves a substantial amount of overhead, as VW has to do many things to preserve the integrity of the drawing list as objects are created and destroyed. If you can do everything that you need to do within VectorScript, you won't kick off all of those routines under the hood. And if you do need to create a construct object for some reason, it's better to re-use it than to delete it and create a new one next time. Editing is cheaper than creating and destroying.

SetSelect can be pretty expensive, because (last I heard) it triggers the Object Info palette code, and that is very expensive. Last time I checked, 75% of the regen time for a normal object was spent in building the OI palette for that object. If SetSelect is triggering this code, you could be spending a lot of time making the OI palette flicker, instead of getting the job done. So always use handle-based calls if they are available, rather than using SetSelect and DoMenuTextByName.

ForEachObjectInList is very fast, and ForEachObject isn't bad, though you shouldn't use these more than once in a script if it can be avoided. These will step through every object (in the specified list, or in the entire drawing, respectively). Always run something like this once, to build up an array of handles, and then process the array of handles as many times as you want, rather than re-running the same search. There is more that could be said on this topic, and there are cases where ForEachObject can actually be faster than stepping through an array in VectorScript looking for a specific element. It all depends on how many objects are in the drawing, and how much data you might have to collect to support your searching. If you're really having performance problems, you might have to try it a couple different ways.

You can use GetTickCount to capture the number of 60ths of a second since VW was launched. The basic strategy is simply like this:

tick1 := GetTickCount;

{do some stuff}

tick2 := GetTickCount;

{do some stuff}

tick3 := GetTickCount;

WriteLn('tick1: ', tick1 / 60);

WriteLn('tick2: ', (tick2 - tick1) / 60);

WriteLn('tick3: ', (tick3 - tick2) / 60);

Don't dump your performance logging to the text file during the script operation, or you'll pick up a performance hit just because of the file i/o (which by the way is kinda slow too).


Share this post

Link to post


I have also observed that when you delete objects within a plug-in object script, it regenerates the plug-in twice. I try to use 2D locus (points) for geometrical operations and then reuse them as reference points like origin, mid line, etc.

Share this post

Link to post


Thank you for these very useful pointers, I'll revisit my code and see if I can make improvements on this basis.

I was wondering if GetTickCount would work and I'll see how I can best use it.

Interestingly, I have always found file I/O operations to be blisteringly fast. Saving the data from 3.5k objects takes about 3 seconds on my PPC G4 PowerBook!

I use ForEachObject a great deal because many of my scripts depend on the LOC criterion and I suspect this is costing me. I'll try to minimise my usage but I don't think there will be a lot I can do.

Using selections is often the only way to prepare objects for processing amongst numerous others - if the OIP is closed, does it still have the same effect?

I would say script performance is generally pretty good, it's just when having to process very large documents, every second saved is a bonus.

Thanks again,


Share this post

Link to post

Here are some findings that might be of interest.

(All measurements are averages of multiple loops through the same objects)

(Compiler mode ON, Stop on VS Warnings ON, OIP displayed)

(PowerBook G4 1.67GHz, VW2008 SP3)

Of the scripts I have profiled that select and deselect objects, all are around 10 ticks _faster_ overall with the OIP displayed than with it closed.

My OIP never flickers during script execution or shows any activity until the script terminates. (I don't think it ever has done).

Selection and deselection, even of thousands of objects, has been very fast for me with the OIP displayed or not. Mostly I see 2-3 ticks to select, process and deselect around 10 objects within a loop which is doing a fair amount of work - plenty fast enough.

The single biggest problem by far is the use of ForEachObject and the LOC criterion.

It costs 46-50 ticks (0.8 secs) just to make the call even when the callback is empty, and therefore using it to save the handles of the objects meeting those criteria is not recommended either.

In comparison, Count or SelectObj with the same criteria are < 1 tick and using both and performing the same callback operations in a WHILE loop for the selected objects is 4-5 ticks. The entire script loop of 400 lines of code now reduces to 8 ticks (0.13sec)!

Where ForEachObject is not used with LOC, there is no significant performance hit in taking the handle array route then calling the subroutine within a FOR loop. A benefit of this is that Message() will reliably update during script execution which is certainly not always the case when it is in the callback of ForEachObject.

I'll keep collating the measurements I make to see if there are other bottlenecks that can be avoided.


Share this post

Link to post

Instead of using the LOC criterion, you could build an array of the objects that you need to operate on at the beginning of the script. Instead of just storing the handles, you would also store the insertion points of the PIOs. (They're PIOs that you're operating on, right?) Anyway, use GetSymLoc to find the insertion points, and store the x/y in additional elements within the TYPE structure. Then, to find an object that you want, instead of using the LOC criterion, you use the Norm function to test the distance of the object from a test point.

Let me know if this doesn't make sense.

Share this post

Link to post

Thinking that there is actually a lot that could be said on this topic, I went ahead and created a VectorLab article:


This way, as more information gets created, it can be added to a central article on the topic, rather than tacked onto the end of a discussion. (Some stuff just works better as articles, and not as discussions.)

Share this post

Link to post

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Sign in to follow this  


7150 Riverwood Drive, Columbia, Maryland 21046, USA   |   Contact Us:   410-290-5114


© 2018 Vectorworks, Inc. All Rights Reserved. Vectorworks, Inc. is part of the Nemetschek Group.

  • Create New...