Community
Navisworks API
Welcome to Autodesk’s Navisworks API Forums. Share your knowledge, ask questions, and explore popular Navisworks API topics.
cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

ModelItemCollection performance issues

41 REPLIES 41
Reply
Message 1 of 42
luca_vezza
5551 Views, 41 Replies

ModelItemCollection performance issues

Hi all,

I'm hitting serious performance issues with the ModelItemCollection class. I have a collection of about 270k items (so, big but not tremendously huge...). Just looping over the collection (with foreach) takes forever (several minutes!). I have tried converting the collection to a hashedSet and looping just flies (~1 millisec). Of course, converting takes ages too, so that is not a real solution...

Is there something big that I am missing? Or is it really the kind of performance I should expect from that type of collection?

Thanks,

 

   Luca

41 REPLIES 41
Message 2 of 42
dgorsman
in reply to: luca_vezza

I don't think you're missing anything, it just takes longer for larger sets.  This could be due to the tree-based structure Navisworks uses for its data.

 

Rather than processing one massive generic set and test/process for every object, you'll likely find it faster to first create smaller, selectively created sets first.

----------------------------------
If you are going to fly by the seat of your pants, expect friction burns.
"I don't know" is the beginning of knowledge, not the end.


Message 3 of 42
luca_vezza
in reply to: dgorsman


@dgorsman wrote:

I don't think you're missing anything, it just takes longer for larger sets.  This could be due to the tree-based structure Navisworks uses for its data.

 

Rather than processing one massive generic set and test/process for every object, you'll likely find it faster to first create smaller, selectively created sets first.


Thanks for the reply, really. But, still, I need to understand a bit more... I don't see why the tree-based structure could impact the collection performance; in my case, the collection contains items and when I loop on them I do not use at all the parent-child relationship. I'm just looping on the list (the simplest test was just increasing a counter, so not even using the single item...).

About your suggestion, I could split it, but I need a reference figure then... What size should I use as the upper limit for my batch operations then? 1k?

 

Note however that I still believe something is wrong here, anyway... 270k is not so huge in a list and this is confirmed by the fact that as HashedSet with the same items just flies. Any idea of what could explain this huge difference?

 

   Luca

Message 4 of 42
rade_tomovic
in reply to: luca_vezza

Hi,

Can you be a little bit more specific? It's not just looping, I suppose that you are taking those elements and doing some operations over. If you are additionaly looping through categories and properties, this is really slow, I asked the same questions on this forum without any answer
Message 5 of 42
luca_vezza
in reply to: rade_tomovic


@rade_tomovic wrote:
Hi,

Can you be a little bit more specific? It's not just looping, I suppose that you are taking those elements and doing some operations over. If you are additionaly looping through categories and properties, this is really slow, I asked the same questions on this forum without any answer

Well, of course in my orginal code I was doing several things; but then I focused on finding the main culprit of the performance issues so I started stripping down the code. The simplest test was really reduced to the following:

int tmpCounter = 0;
foreach (ModelItem currItem in itemList)
 tmpCounter++;

And this was taking an absurd amount of time... The same loop on a HashedSet was instantaneous.

I will try to simplify my code and see if it can be shared, so maybe you can run the same tests if you care.

 

    Luca

Message 6 of 42
rade_tomovic
in reply to: luca_vezza

Ok, I haven't noticed issues like that while simple looping through model items. But maybe this hint will help you - you need (very probably) just items which have GUIDs (which means, at least for Revit based models - instances) so add one IF clause just after loop where you'll check that item GUID is different from empty GUID.
Message 7 of 42
luca_vezza
in reply to: rade_tomovic


@rade_tomovic wrote:
Ok, I haven't noticed issues like that while simple looping through model items. But maybe this hint will help you - you need (very probably) just items which have GUIDs (which means, at least for Revit based models - instances) so add one IF clause just after loop where you'll check that item GUID is different from empty GUID.

This is interesting... the lack of a general notion of GUID is exactly why I'm writing my code... I do not have Revit projects, but data coming from several other CAD packages, GUIDs are always empty; and there is no field that can serve as a Unique ID. So, I need to create one myself (by adding custom props).

 

However, I've just simplified my code and reduced to a Debug button that calls this:

// get the selected items
Document oDoc = Autodesk.Navisworks.Api.Application.ActiveDocument;
ModelItemCollection itemList = oDoc.CurrentSelection.ToSelection().GetSelectedItems();

MessageBox.Show("Total selected items: " + itemList.Count.ToString());
System.Diagnostics.Stopwatch timer = new System.Diagnostics.Stopwatch();
timer.Start();

int tmpCounter = 0;
foreach (ModelItem currItem in itemList)
   tmpCounter++;

long t1 = timer.ElapsedMilliseconds;
MessageBox.Show("Time to loop list of all items:  " + t1.ToString() + " milliseconds");

As long as I have a few thousands selected items, this eats up seconds. Which, to me, seems really too much.

 

    Luca

Message 8 of 42
rade_tomovic
in reply to: luca_vezza

There must be some GUID - try to change selection resolution in the interface. If those elements came from CAD, try to set selection resolution to geometry and see if there are GUIDs under Item/GUID. If not, try to cycle through different selection resolutions until you see GUID
Message 9 of 42
luca_vezza
in reply to: rade_tomovic

GUIDs in these large industrial projects are only at the file level. Quite useless as any file has thousands of items...

 

Message 10 of 42
xiaodong_liang
in reply to: luca_vezza

Hi @luca_vezza,

 

it is weird behavior as you did nothing in foreach on a small tree. And it seems you only dumped a selected item, which means the items are much less. Is such code working well with some other small files (such as SDK sample gatehouse.nwd, clashtest.nwd etc)? I'd think it will need a reproducible sample file to diagnose further.

 

As to guid, I guess you talked about the guid property with ModelItem API object. It is auto-assigned when Navisworks imports the file, but not all types of file have such id value. While as  @rade_tomovic mentioned, no matter any file format, it should be a kind of unique id in the object properties, e.g.Entity Handle of AutoCAD, unique id/ element id of Revit etc.

Message 11 of 42
luca_vezza
in reply to: xiaodong_liang

Hi Xiaodong,

 


@@xiaodong_liang wrote:

it is weird behavior as you did nothing in foreach on a small tree. And it seems you only dumped a selected item, which means the items are much less. Is such code working well with some other small files (such as SDK sample gatehouse.nwd, clashtest.nwd etc)? I'd think it will need a reproducible sample file to diagnose further.


 It seems to work fine (meaning the perfomance is not an issue) for small collections in general, but this is simply because the issue is there but smaller, harder to perceive... So try it on any file and you should get the problem.

 


As to guid, I guess you talked about the guid property with ModelItem API object. It is auto-assigned when Navisworks imports the file, but not all types of file have such id value. While as  @rade_tomovic mentioned, no matter any file format, it should be a kind of unique id in the object properties, e.g.Entity Handle of AutoCAD, unique id/ element id of Revit etc.


The files we work with come from CAD systems such as Catia, AVEVA PDMS and Intergraph. All these files do not show usable GUIDs on the items, unfortunately.

 

   Luca

Message 12 of 42
devtechchina
in reply to: luca_vezza

Hi @luca_vezza,

 

regarding with guid per object, I checked with our engineer team and got the comments below:

 

What IDs are available depend on the file format you’re loading into Navisworks. Some file formats don’t have any per objects ids, some have ids but they aren’t stable (edit the file in some unrelated way and the ids change). Most of these ids are only unique within a file. Whatever ids are there show up as object properties which are different depending on format – Entity Handle for Autocad, Element Id for Revit, etc.

 

In addition Navisworks has support for a per object GUID which is a stable, globally unique id for an object. GUIDs exist for file formats which directly support GUIDs (e.g. IFC) and for some file formats where we can generate stable, globally unique ids based on the file format specific ids. This includes AutoCAD and Revit but not Catia, PDMS or Integraph. So, for such file formats:  you would have to rely on custom properties that the creator of the design file has setup.

Message 13 of 42
luca_vezza
in reply to: luca_vezza


@luca_vezza wrote:

Hi Xiaodong,

 


@@xiaodong_liang wrote:

it is weird behavior as you did nothing in foreach on a small tree. And it seems you only dumped a selected item, which means the items are much less. Is such code working well with some other small files (such as SDK sample gatehouse.nwd, clashtest.nwd etc)? I'd think it will need a reproducible sample file to diagnose further.


 It seems to work fine (meaning the perfomance is not an issue) for small collections in general, but this is simply because the issue is there but smaller, harder to perceive... So try it on any file and you should get the problem.

 


Still on the ModelItem collection performance issue...

I see that the Task manager shows a high number of page faults when the code loops on the collection. And the app's private working set keeps on reducing, as if the system (or the Garbage Collector) decided to move the items' details away from the main memory. If that is the case, accessing them would cause the page fault and, to retrieve the data, extra processor cycles would certainly be needed. This could potentially explain the very poor performance.

Is there a way to force Navisworks to keep the items info in memory until we're done looping on the collection? (Note that physical RAM is not an issue, there is a lot available...).

Thanks,

 

     Luca

 

Message 14 of 42
Anonymous
in reply to: luca_vezza

I would suggest to use the ComAPI, converting your ModelItemCollection once.

Using this method I'm able to parse depth first a model with around half million objects in less than a second on a typical laptop, including accessing each object name.

 

Something like that:

 

var nwSel = ComApiBridge.ToInwOpSelection(items); // items is your original ModelItemCollection
var nwItems = nwSel.Paths();
int c = nwItems.Count;
for (int i=1;i<=c;i++) { var path = (InwOaPath3)nwItems[i]; var node = (InwOaNode)path.Nodes().Last(); // do your stuff here using node
// examples: node.UserName, node.IsLayer, etc... }

 

 

 

Message 15 of 42
luca_vezza
in reply to: Anonymous

Wow, that sounds interesting. I will give it a try asap and get back to you.

By the way, any idea if/where one could find a decent doc for the COM API?

 

Thanks a bunch,

 

   Luca

Message 16 of 42
rade_tomovic
in reply to: luca_vezza

Just iterating through ModelItemCollection is not performance issue (simple O(n) for enough items). Performance starting to be a huge problem when you try to get properties, because time complexity is going to O(n^3) one loop for items, one for categories and one for properties.

Message 17 of 42
luca_vezza
in reply to: rade_tomovic


@rade_tomovic wrote:

Just iterating through ModelItemCollection is not performance issue (simple O(n) for enough items). Performance starting to be a huge problem when you try to get properties, because time complexity is going to O(n^3) one loop for items, one for categories and one for properties.


I wish that was true... but apparently in my case even just looping eats up a lot of time. See posts above.

 

     Luca

Message 18 of 42
Anonymous
in reply to: rade_tomovic

It's true that iterating over properties for each items greatly increase running time.

However theoretically it's not O(n^3)  but O(n x categories x properties) provided the access to the categories for one item below the hood is direct.

 

As real case example, in my plugin, I can iterate through over a quarter million items in 0.6s but it take around 7s to get all their "static" [aka not the GUI custom one] SP3D properties using ComApi. Still acceptable but not so "yahoo" !

In my case almost all items are from SP3D so have lots of SP3D properties as strings, this explaining that...

 

For reference simplified code extract on how I access these whi "node" being the ComApi node of concern:

			InwOpState11 state = (InwOpState11)ComApiBridge.State;
			InwNodeAttributesColl attrs = node.Attributes();
			InwOaAttribute attr = null;

			var cc = attrs.Count;
			for (int i = 1; i <= cc; i++)
			{
				attr = attrs[i] as InwOaAttribute;

				if (attr.ClassName != "SP3D")
					continue;

				InwOaPropertyVec propVec = state.GetAttributeProperties(attr);
				InwOaPropertyColl props = propVec.Properties();
				int pc = props.Count;

				for (int j = 1; j <= pc; j++)
				{
					InwOaProperty prop = (InwOaProperty)props[j];
					PropertiesIndexes idx;

					// do your stuff here using prop.value;
				}

				GC.KeepAlive(props);
				GC.KeepAlive(propVec);
			}

 

Message 19 of 42
rade_tomovic
in reply to: Anonymous

Yeah, techically it is O(nxmxp), however it is really slow. I developed plugin for grouping model into tree, based on custom properties and for that purposes I have to iterate through every item/category/property/value and for models larger than 100k it can take a few minutes (especially for complex BIM projects where you have a hundreds of properties). I’m not sure that COM API is faster than .NET in that case, but please advise if you have different experience.

Message 20 of 42
Anonymous
in reply to: rade_tomovic

I have not instrumented the specific performance difference to access categories and properties between ComApi and .NET, however my gut feeling is a 3x speed-up

Depending on your use case, you can speed up things by skeeping categories you don't want or have already listed

Same for the properties.

 

There is one critical semantic difference between .NET and ComAPI categories:

- .NET API merge the "static" and the "custom GUI" properties

- ComAPI has 2 different APIs to access these

 

While this would seems .NET API is at an advantage here in terms of writing simpler code, I would argue this is actually the opposite !

 

Using ComAPI you have some control of what you parse and this can give you a up to 10x performance improvement if you know which type of category [Custom GUI or "static") you are looking at.

Can't find what you're looking for? Ask the community or share your knowledge.

Post to forums  

Rail Community


 

Autodesk Design & Make Report