Filter Performance Conclusion
Originally Published inContinuing the topic of the Revit API filter performance, Guy responds to Ralf:
Ralf raises some good points.
A good point on the order in filters, I did not think about that and I did think the speed improvement was larger than normal. I’ve found in past testing using the TypeFilter in most cases 2-10% slower than using some form of .NET filtering for real world projects and where other filters are not appropriate.
I agree that an anonymous method is not as elegant; I haven’t used this method in any production code. It just gave you a list as per your original code. Almost without exception, all my commands use the ElementIterator getElements() rather than populating a list. And for that reason the point I should have made in the previous post was that IMO I still think the TypeFilter is redundant. This is how I would have processed the elements in the command:
Filter f1 = cf.NewCategoryFilter(
BuiltInCategory.OST_Doors );
Filter f2 = cf.NewCategoryFilter(
BuiltInCategory.OST_Windows );
Filter f3 = cf.NewLogicOrFilter( f1, f2 );
ElementIterator itor = doc.get_Elements( f3 );
int n = 0;
while( itor.MoveNext() )
{
if( itor.Current is FamilyInstance )
{
n++;
FamilyInstance elem = itor.Current
as FamilyInstance;
}
}
Against the 100 MB project this is still 1-3% faster than including the type filter, even including the casting to a FamilyInstance.
The other point Ralf raises is that x >> y where x is the total element number and y is the number of elements of interest, e.g. of the desired category. I would go a step further and say for most commands x >> y >> z, where z is the number of particular instances of a family you are interested in processing. For that reason a vast majority of production commands I’ve written use parameter filtering, yet I have none with type filtering.
I’ve also found in testing that type filter performance is very dependent on whether a project has been recently purged of unused objects. As users will know, purging on a regular basis helps project performance. The difference in performance between a purged versus a non purged project for API commands can be very dramatic, as the total object count often drops significantly.
As you can tell, I’m not a big fan of the type filter but every project is different ;-)
For completeness sake, here is how I would use the ElementIterator in the original code:
private Dictionary<ElementId, List<ElementId>>
getElementIds( ElementIterator itor)
{
Dictionary<ElementId, List<ElementId>> dict
= new Dictionary<ElementId, List<ElementId>>();
string fmt = "{0} is hosted by {1}";
while( itor.MoveNext() )
{
object elem = itor.Current;
if( elem is FamilyInstance )
{
FamilyInstance fi = elem as FamilyInstance;
ElementId id = fi.Id;
ElementId idHost = fi.Host.Id;
Debug.WriteLine( string.Format( fmt,
ElementDescription( fi ),
ElementDescription( idHost ) ) );
if( !dict.ContainsKey( idHost ) )
{
dict.Add( idHost, new List<ElementId>() );
}
dict[idHost].Add( id );
}
}
return dict;
}