Slow, Slower Still and Faster Filtering

Today I discuss (once again) an important performance aspect of Revit element filtering, a Python script for tagging JPEG images with EXIF data, prompted by a recent ski tour, and three other interesting topics that caught my eye:

Slow, Slower Still and Faster Filtering

A question was raised in the Revit API discussion forum thread asking, is it slow filter?

Meseems we need to point out yet again the difference between slow and fast filters.

Much more importantly, though, the difference between using Revit filters or post-processing results using .NET and or LINQ.

The latter will always be at least twice as slow as using Revit filters, regardless of whether they are fast or slow.

The process of marshalling the Revit element data out of the Revit memory space into the .NET environment always costs much more time than retrieving the data inside of Revit in the first place.

Therefore, if you care about performance, use as many Revit filters as possible, regardless whether they are fast or slow.

In the following example, .NET post-processing is used to test a parameter value.

That can be speeded up by an order of magnitude by implementing a Revit parameter filter instead, which avoids the marshalling, post-processing, and is fast to boot:

Question: I have used the following code:

  string section_name = "XXXXXXX";

  IEnumerable elems = collect
    .OfClass(typeof(FamilyInstance))
    .Where(x => x.get_Parameter(
      BuiltInParameter.ELEM_FAMILY_PARAM)
        .AsValueString() == section_name );

Is it a slow filter?

Answer: No, it is not. It is even slower than a slow filter.

Your code retrieves the element data from Revit to the .NET add-in memory space, and then uses .NET and LINQ to post-process it.

The difference is explained in the discussion of quick, slow and LINQ element filtering.

You could convert it to a fast filter by implementing a parameter filter to compare the family name.

That discussion does not show how to actually implement the parameter filter.

I therefore cleaned up The Building Coder sample code demonstrating retrieving named family symbols using either LINQ or a parameter filter for you to illustrate how to do that:

  #region Retrieve named family symbols using either LINQ or a parameter filter
  static FilteredElementCollector
    GetStructuralColumnSymbolCollector(
      Document doc )
  {
    return new FilteredElementCollector( doc )
      .OfCategory( BuiltInCategory.OST_StructuralColumns )
      .OfClass( typeofFamilySymbol ) );
  }

  static IEnumerable<Element> Linq( 
    Document doc, 
    string familySymbolName )
  {
    return GetStructuralColumnSymbolCollector( doc )
      .Where( x => x.Name == familySymbolName );
  }

  static IEnumerable<Element> Linq2( 
    Document doc, 
    string familySymbolName )
  {
    return GetStructuralColumnSymbolCollector( doc )
      .Where( x => x.get_Parameter( 
        BuiltInParameter.SYMBOL_NAME_PARAM )
          .AsString() == familySymbolName );
  }

  static IEnumerable<Element> FilterRule( 
    Document doc, 
    string familySymbolName )
  {
    return GetStructuralColumnSymbolCollector( doc )
      .WherePasses(
        new ElementParameterFilter(
          new FilterStringRule(
            new ParameterValueProvider(
              new ElementIdBuiltInParameter.SYMBOL_NAME_PARAM ) ),
            new FilterStringEquals(), familySymbolName, true ) ) );
  }

  static IEnumerable<Element> Factory( 
    Document doc, 
    string familySymbolName )
  {
    return GetStructuralColumnSymbolCollector( doc )
      .WherePasses(
        new ElementParameterFilter(
          ParameterFilterRuleFactory.CreateEqualsRule(
            new ElementIdBuiltInParameter.SYMBOL_NAME_PARAM ),
            familySymbolName, true ) ) );
  }
  #endregion // Retrieve named family symbols using either LINQ or a parameter filter

It demonstrates two ways to implement the highly efficient parameter filter.

First, the explicit solution, implementing the separate ElementParameterFilter, FilterStringRule, and ParameterValueProvider components one by one.

Second, a much more succinct solution using the ParameterFilterRuleFactory CreateEqualsRule method.

Here are some other examples of using the ParameterFilterRuleFactory:

I hope this clarifies.

Now for some non-Revit-related topics:

Python JPEG EXIT Filename Tagging

I went on a ski tour in the Silvretta area with some friends two weeks ago, climbing Haagspitze, Hintere Jamspitze, Ochsenkopf, Sattelkopf and above all Piz Buin.

Here I am crossing the Piz Buin summit ridge:

Jeremy on the Piz Buin summit ridge

That excursion prompted me to implement a Python script to tag the photos shared by the participants after the tour.

How to easily sort all the different pictures chronologically?

Well, most cameras embed a timestamp in EXIF data when they generate a JPEG image.

This information can be easily accessed and used to rename each image file, e.g., prepending the timestamp to the filename.

In case of multiple photographers, it also makes sense to add the photographer initials to the filename.

I implemented a really minute little Python script ren2timestamp.py that does this for me, using a hint from the StackOverflow discussion on getting date and time when photo was taken from EXIF data using PIL and the exif-py easy-to-use Python module to extract Exif metadata from tiff and jpeg files:

#!/usr/bin/env python
# ren2timestamp.py - rename files to add their exif timestamp as a prefix
# Copyright (C) 2018 by Jeremy Tammik, Autodesk Inc.

import exifread, datetime, os, sys, time

files = sys.argv[1:]

exif_ts_key = 'EXIF DateTimeOriginal'

def exif_timestamp(filename):
  with open(filename, 'rb') as fh:
    tags = exifread.process_file(fh, stop_tag=exif_ts_key)
    if tags.has_key(exif_ts_key):
      return tags[exif_ts_key]
    else:
      return ''

photographer_initials = 'jt'

for g in files:
  ts3 = exif_timestamp(g)
  ts3 = str(ts3).replace(':','').replace(' ','_')
  newname = ts3 + '_' + photographer_initials + '_' + g
  print("%s --> '%s'" % (g,newname))
  os.rename(g, newname)

Here is the closing picture of the entire group on the Sattelkopf summit:

Group photo on Sattelkopf

TED Talks and Population Growth

TED is a non-profit organisation devoted to spreading ideas, usually in the form of short, powerful talks (18 minutes or less). TED is also a global community, welcoming people from every discipline and culture who seek a deeper understanding of the world. TED believes passionately in the power of ideas to change attitudes, lives and, ultimately, the world. TED.com is a clearinghouse of free knowledge from the world's most inspired thinkers.

I have listened to many TED talks, enjoyed and appreciated them very much, and highlighted several of them here in the past.

Here is an illuminating one with a paradoxical answer by Hans Rosling on Global population growth, box by box:

The world's population will grow to 9 billion over the next 50 years – and only by raising the living standards of the poorest can we check population growth.

Objective Reality Does Not Exist

A quantum experiment suggests there’s no such thing as objective reality.

Artificial Intelligence Judge

Can AI be a fair judge in court? Estonia thinks so.