Filter Performance Conclusion

Continuing 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 )
      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;