Type Filter Benchmark Update

We have come to trust the Revit 2011 API filters to really be the most performant approach to retrieve sets of database elements fulfilling a complex combination of criteria. To reinforce that message, here is another benchmark by Piotr Zurek, who says:

While searching for some answers in your blog I have stumbled across an old post where Guy Robinson compared the performance of the Revit 2010 API class filter vs an anonymous method.

I found the result really intriguing since I always assumed that the filter would be quicker. Since the post was a bit old I decided to check how those results would look in the current version. I also decided to add a third element to the comparison – filtering using LINQ. You know, just for the kicks... :-)

I'm happy to report that the situation has improved, by which I mean the speed of the "elegant" Revit filter is on par with both other methods. I probably should have tried to test it on a bigger model with more elements to filter to get a bit measurable result but what I got clearly shows that the 3x difference is gone. Here are my results from five runs:

Method      Time in ms to retrieve 860 elements
Class filter 10840363737
Anonymous method 19236393837
LINQ 12937373939

Here is the source code used, reproduced here below for your convenience:

[Transaction( TransactionMode.Manual )]
[Regeneration( RegenerationOption.Manual )]
public class Commands : IExternalCommand
{
  public Result Execute(
      ExternalCommandData commandData,
      ref string message,
      ElementSet elements )
  {
    try
    {
      UIApplication uiApp = commandData.Application;
      UIDocument uiDoc = uiApp.ActiveUIDocument;
      Application app = uiApp.Application;
      Document doc = uiDoc.Document;
 
      Stopwatch sw = Stopwatch.StartNew();
 
      // f5 = f1 && f4
      // = f1 && (f2 || f3)
      // = family instance and (door or window)
 
      #region Filters and collector definitions
 
      ElementClassFilter f1 
        = new ElementClassFilter( 
          typeof( FamilyInstance ) );
 
      ElementCategoryFilter f2 
        = new ElementCategoryFilter( 
          BuiltInCategory.OST_Doors );
 
      ElementCategoryFilter f3 
        = new ElementCategoryFilter( 
          BuiltInCategory.OST_Windows );
 
      LogicalOrFilter f4 
        = new LogicalOrFilter( f2, f3 );
 
      LogicalAndFilter f5 
        = new LogicalAndFilter( f1, f4 );
 
      FilteredElementCollector collector 
        = new FilteredElementCollector( doc );
 
      #endregion
 
      //#region Filtering with a class filter
      //List<Element> openingInstances = 
      //  collector.WherePasses(f5).ToElements() 
      //    as List<Element>;
      //#endregion
 
      //#region Filtering with an anonymous method
      //List<Element> openings = collector
      //  .WherePasses(f4)
      //  .ToElements() as List<Element>;
      //List<Element> openingInstances
      //  = openings.FindAll(
      //    e => e is FamilyInstance );
      //#endregion
 
      #region Filtering with LINQ
      List<Element> openings = collector
        .WherePasses( f4 )
        .ToElements() as List<Element>;
 
      List<Element> openingInstances 
        = ( from instances in openings
          where instances is FamilyInstance
          select instances ).ToList<Element>();
      #endregion
 
      int n = openingInstances.Count;
      sw.Stop();
 
      Debug.WriteLine( string.Format( 
        "Time to get {0} elements: {1}ms", 
        n, sw.ElapsedMilliseconds ) );
 
      return Result.Succeeded;
    }
    catch( Exception ex )
    {
      message = ex.Message + ex.StackTrace;
      return Result.Failed;
    }
  }
}

Many thanks to Piotr for testing and sharing these results!

By the way, this comparison and the results are quite comparable to the level filter benchmark included in Kevin Vandecar's filtered element collector overview and benchmarking suite.