ToElementIds Performance

Conversion of a filtered element collector to an explicit .NET collection of elements or element ids is always costly and mostly avoidable and unnecessary.

Before getting to the details of that issue, here is another quick snapshot from our travels to the West European developer conferences.
As you either know or might guess, one of the main topics of these is cloud and mobile development, e.g. looking at topics such as the

BIM 360 Glue REST API and SDK
and
my hands-on exploration of how to

access and use it via Python
.

Funnily enough, the lighting in the Paris Charles de Gaulle airport is designed along a cloud scheme, so here is very low quality picture of us almost touching the clouds at the airport gate preparing to board the flight to Milano:

Jeremy touching the clouds in Charles de Gaulle in Paris

Ora siamo arrivati in Milano per un’altra conferenza di sviluppatori.

Returning to the Revit API, I recently mentioned a couple of

FindElement and collector optimisations
.

One of them is the fact that the conversion from a filtered element collector to a .NET collection is costly and should be avoided if possible.

Here is another observation related to that, brought up by Guy Robinson and answered by Scott Conover of the Revit API development team:

Question: I sometimes use the ToElementIds method, e.g. like this:

">
  var fc = new FilteredElementCollector( doc )
    .OfSomething()
    .ToElementIds();
 
  foreach( var elemId in fc )
  {
    var element = doc.GetElement( elemId );
  }

I noticed that this is about 25% slower than accessing the elements directly as follows:

">
  var fc = new FilteredElementCollector( doc )
    .OfSomething();
 
  foreach (var elem in fc)
  {
      var element = elem;
  }

I am wondering why the ToElementIds approach is so much slower?

Shouldn’t the two approaches be equivalent?

Is there any good reason to use ToElementIds at all?

Not surprisingly, this ~25% slower figure has been the same on my tests ever since I started comparing in Revit 2012.

Answer: This is not unexpected.

ToElementIds and ToElements allocate a new concrete .NET collection containing the elements passing the filter.
Then it returns this allocated collection to you, which involves a conversion from a native level collection to a managed object.
This will take extra time as compared to a direct iteration.

Why might you use the ToElementIds variants?
Well, you might need a concrete collection to store in memory.
Or you might need a collection to pass as input to an API method like Delete – which you should definitely not call from the middle of a foreach iteration unless you like unexpected behaviour.
This provides a shortcut to building this allocation.

For any sort of operation of the type “I want to read properties of each passing element” you can skip ToElements and the extra collection allocation.

Thank you Scott for these insights!

For one example of how this can be achieved, look at the aforementioned

FindElement and collector optimisations
.
There are lots of other examples in many of The Building Coder discussions, since I consciously and consistently avoid this conversion whenever possible, i.e. almost always.


Comments

5 responses to “ToElementIds Performance”

  1. Hi Jeremy,
    I am a newer to Revit, and now have some problem need your help.
    It’s about Mep connector, as I dont’t understand the difference between Architecture、Structure and Mep clearly, there are some problems I can’t solve, please help me ,the problems are list below:
    1: I’m now developing an add-in program to export data from revit project(Architecture or Structure or Mep) to other format file,Is it possible to develop a common program to deal with revit projects for these three types. if that’s possible do I need have all these type products.
    2: When I use this program to export the project created by Architecture it seems OK, but when I export project created by Mep, some problems came out, almost all element has no material(in project these elements have their own surface color), in code it’s materialId always -1,and some element like connector ,the faces of solid always 0, the relevant codes are list here:
    public string GetElementGeometryInfo(Element elem)
    {
    string geometryInfo = “”;
    Options opt = new Options();
    opt.ComputeReferences = true;
    GeometryElement geomElem = elem.get_Geometry(opt);
    if (null != geomElem)
    {
    GetStructPoints(geomElem, 0);
    }
    return geometryInfo;
    }
    public void GetStructPoints(GeometryElement geomElem, Int32 transIndex)
    {
    foreach (GeometryObject geomObj in geomElem.Objects)
    {
    string geoType = geomObj.GetType().Name;
    switch (geoType)
    {
    case “Solid”:
    procSolid(geomObj, transIndex);
    break;
    case “Face”:
    procFace(geomObj, transIndex);
    break;
    case “Mesh”:
    procMesh(geomObj, transIndex);
    break;
    case “Curve”:
    case “Line”:
    case “Arc”:
    procCurve(geomObj, transIndex);
    break;
    case “Profile”:
    procProfile(geomObj, transIndex);
    break;
    case “Element”:
    procGeoElement(geomObj, transIndex);
    break;
    case “GeometryInstance”:
    procInstance(geomObj, transIndex);
    break;
    case “Edge”:
    procEdge(geomObj, transIndex);
    break;
    default:
    break;
    }
    }
    }
    public void procSolid(GeometryObject obj, Int32 transIndex)
    {
    Solid solid = obj as Solid;
    if (null == solid)
    {
    return;
    }
    //a solid has many faces
    FaceArray faces = solid.Faces;
    if (faces.Size == 0)
    {
    return;
    }
    foreach (Face face in faces)
    {
    procFace(face, transIndex);
    RecordNormal_VN(face, transIndex);
    RecordMaterial(face);
    }
    protected void procInstance(GeometryObject obj, Int32 transIndex)
    {
    GeometryInstance instance = obj as GeometryInstance;
    if (null == instance)
    {
    return;
    }
    Transform allTransform = CsMathUtil.AddTransform( m_trans[transIndex], instance.Transform );
    m_trans.Add(allTransform);
    Int32 newTransIndex = m_trans.Count-1;
    GeometryElement instanceGeometry = instance.GetSymbolGeometry();
    if (null == instanceGeometry)
    {
    return;
    }
    //get all geometric primitives contained in the GeometryElement
    GetStructPoints(instanceGeometry, newTransIndex);
    }

  2. Dear Iriving,
    1. Yes, of course it is possible to support Architecture, MEP and Structure with one single identical add-in. Please read the Getting started material, which describes that.
    2. Connectors are virtual objects which have no geometry and therefore no solid.
    Cheers, Jeremy.

  3. Thanks Jeremy
    I’m appreciate your reply,but my problem still exist and must to solve, my work is to develop an add-in that can read data from Revit project and export them to my company’s own format files,so that all visble objects can be reproduced in the 3D Editor Program of my company.
    Just as you said ” Connectors are virtual objects”,is that means I can’t reproduced them whatever? and I can report to my boss that these things just can’t export?
    addition problem:
    If I plan to export a Revit project that it’s document contains Architecture,Structure and Mep objects,is there anything I need to pay attention to?
    Anything information will be appreciated,waiting for your reply >_<

  4. Dear Iriving,
    You can easily access the MEP connectors and export any data you like for them. Look at the TraverseSystem SDK sample to see how the data can be accessed and used. They just have no geometry.
    The only thing you should be aware of when exporting architectural, structural and MEP data is that you need to have the appropriate flavour of Revit enabled for the data to be accessible at all. The easiest way to achieve this is obviously to run your add-in in a Revit one-box installation encompassing all three flavours. For example, the MEPModel property will return null when run inside of Revit Architecture or Structure.
    Cheers, Jeremy.

  5. hum..I got that.
    Thanks again Jeremy,you are so kind of man

Leave a Reply to IrivingCancel reply

Discover more from Autodesk Developer Blog

Subscribe now to keep reading and get access to the full archive.

Continue reading