Slow run time using FilteredElementCollector

Slow run time using FilteredElementCollector

Anonymous
Not applicable
1,915 Views
9 Replies
Message 1 of 10

Slow run time using FilteredElementCollector

Anonymous
Not applicable

Hi,

 

So I am using a query to search filteredelementcollector of all the groups in my Revit file.

 

This is the code I am using:

 

for (int i =0; i < elementNames.Count(); i++)

{

      var selectedItems = from element in groupCollector where element.Name == elementNames[i] select element;

}

As you can see it is being run in a for loop, however it is horribly slowing down my addin. Is there anyway to replace the query and search through a filteredelementcollector.

 

Thanks.

0 Likes
Accepted solutions (2)
1,916 Views
9 Replies
Replies (9)
Message 2 of 10

Anonymous
Not applicable

If I understand correctly you have a list of elements and you want to find groups of a certain name ?

 

You could make an list of groups with the filteredcollection method filtering on the category "Model Groups". 

 

Once you have that list, use a loop or a linq expressing to find the name you need.

0 Likes
Message 3 of 10

jeremytammik
Autodesk
Autodesk
Accepted solution

Dear Filip,

 

Your query has nothing whatsoever to do with the filtered element collector and its performance.

 

Your query as it stands, with the sample code you show, is about post-processing the results obtained from the filtered element collector.

 

You can improve performance and cut the processing speed by at least half, quite possibly 90% or even significantly more by making use of the built-in power of the filtered element collector.

 

To do so, add quick filters up front, possibly followed by slow filters, before retrieving the results into the .NET environment and possibly post-processing them further.

 

http://thebuildingcoder.typepad.com/blog/2015/12/quick-slow-and-linq-element-filtering.html

 

Look at the discussions and analysis of filtered element collector performance dating back five years ago and more:

 

http://thebuildingcoder.typepad.com/blog/about-the-author.html#5.9

 

You can use a parameter filter to filter for element names much more effectively:

 

 

Cheers,

 

Jeremy



Jeremy Tammik
Developer Technical Services
Autodesk Developer Network, ADN Open
The Building Coder

Message 4 of 10

Anonymous
Not applicable
Accepted solution

What value are you trying to retrieve from the FilteredElementCollector?  And based off of that answer, how are you initializing your FilteredElementCollector?  There are many different ways of doing it.  For example, you can initialize your FilteredElementCollector (FEC) to grab everything in a drawing (which could be A LOT!).  However, if you're only looking for specific types of Elements, you can set your FEC to only pull those types.  For further example, if you only want FamilyInstances, you can set up your FEC like this:

var fecFittings = new FilteredElementCollector(pDoc)
                .OfClass(typeof(FamilyInstance))
                .OfType<FamilyInstance>()
                .ToList();

which will return a List<FamilyInstance>.  Furthermore, if you only wanted to return specific types of FamilyInstance's you can use lambda expressions like this in your FEC:

 

var fecFittings = new FilteredElementCollector(m_pDoc)
                .OfClass(typeof(FamilyInstance))
                .Where(pElt =>
                {
                    int lCatId = pElt.Category.Id.IntegerValue;
                    return lCatId == (int)BuiltInCategory.OST_PipeAccessory ||
                           lCatId == (int)BuiltInCategory.OST_PipeFitting ||
                           lCatId == (int)BuiltInCategory.OST_MechanicalEquipment;
                })
               .OfType<FamilyInstance>()
               .ToList();

From there, you can simply iterate through your List<FamilyInstance> with a foreach loop to find the specific elements you want:

 

foreach (FamilyInstance pFam in fecFittings)
{
    /*Do what we need with each Element*/
}

I do this A LOT with my plug-in and I have little to no lag time.  Finding out the best way to utilize your FECs is what will make the world of difference.  Also, try to use foreach loops whenever possible.  It saves you from having to worry about "Index Out of Bounds" errors. 

 

Hope that helps!!

0 Likes
Message 5 of 10

Anonymous
Not applicable

Hi Jeremy,

 

Thanks for your answer before. It was quite helpful. I have an additional query linked to this.

 

I am searching through my filteredElementCollector for groups which I know the name of.

The code I am currently using is via filtering but it is still terribly slow. I currently don't have more than 30 groups which consist of a basic detail item and a TextNote each. It is taking approximately 35 seconds to execute the loop in which I have the filtering. This is only approximately only 3 seconds quicker than the same process using a LINQ query. I am searching for each group via the below code in a for loop:

 

FilteredElementCollector groupCollector = new FilteredElementCollector(document).OfClass(typeof(Group));

ParameterValueProvider provider = new ParameterValue Provider(new ElementId(BuiltInParameter.ELEM_TYPE_PARAM);

FilterStringRileEvaluator evaluator = new FilterStringEvaluator();

FilterRule rule = new FilterStringRule(provider, new FilterStringContains(), name, true); //where name = "Ductwork0" to "Ductwork30

 

ElementParameterFilter epf = new ElementParameterFilter(rule);

return groupCollector.WherePasses(epf).FirstElement();

 

Is there anything that I could do to improve the above code to speed up the process? I am also having the issue that this code works as expected in Revit 2017, but crashes with index errors if I search the filteredElementCollector using above filters instead of a LINQ query.

 

Cheers,

Filip Karisik.

0 Likes
Message 6 of 10

Anonymous
Not applicable

Apologies the error in Revit 2016 is actually "object reference not set to an instance of an object".

 

Cheers,

Filip Karisik.

0 Likes
Message 7 of 10

jeremytammik
Autodesk
Autodesk

Dear Filip,

 

Thank you for your query.

 

Your code looks perfectly fine to me.

 

You can make your code more readable by formatting it as code, using the </> 'Insert Code' icon.

 

The code you show cannot be compiled, because the Revit API does not define any class named `FilterStringRileEvaluator`.

 

Does that mean that you typed all that code by hand instead of using copy and paste? Tapfer, tapfer. But error-prone.

 

I cannot imagine any filtered element collector taking 35 seconds to retrieve 30 groups.

 

What does 'where name = "Ductwork0" to "Ductwork30' mean?

 

Are you running this in a loop with different values for `name`?

 

Is your project file unbelievably huge?

 

That is all I can say just looking at your code.

 

For anything further, you had best provide a minimal reproducible case:

 

http://thebuildingcoder.typepad.com/blog/about-the-author.html#1b

 

I hope this helps.

 

Best regards,

 

Jeremy



Jeremy Tammik
Developer Technical Services
Autodesk Developer Network, ADN Open
The Building Coder

0 Likes
Message 8 of 10

FAIR59
Advisor
Advisor

You are now filtering for placed groups , I think its faster to filter for GroupTypes, and if you are interested in placed groups, you can use the property GroupType.Groups to get them.

 

                StringBuilder sb = new StringBuilder();
                _now = DateTime.Now;
                sb.AppendLine(string.Format("{0}:{1}", _now, _now.Millisecond));
                FilteredElementCollector groupTypeCollector = new FilteredElementCollector(document).OfClass(typeof(GroupType));
                ParameterValueProvider provider1 = new ParameterValueProvider(new ElementId(BuiltInParameter.ALL_MODEL_TYPE_NAME));
                FilterRule rule1 = new FilterStringRule(provider1, new FilterStringContains(), "Ductwork", true); 
                ElementParameterFilter epf1 = new ElementParameterFilter(rule1);
                foreach (GroupType _gt in groupTypeCollector.WherePasses(epf1))
                {
                    foreach (Group _group in _gt.Groups)
                    {
                        sb.AppendLine(string.Format("     {0}  <{1}>", _group.Name, _group.Id));
                        break;
                    }
                }
                _now = DateTime.Now;
                sb.AppendLine(string.Format("{0}:{1}", _now, _now.Millisecond));
                TaskDialog.Show("debug", sb.ToString());

 

 

0 Likes
Message 9 of 10

Anonymous
Not applicable

Hi Jeremy,

 

It was an issue with my file. Once I copied all the elements from that file into a new one I was getting very quick speeds, less than a few seconds.

 

Cheers,

Filip Karisik.

0 Likes
Message 10 of 10

jeremytammik
Autodesk
Autodesk

Dear Filip,

Congratulations on solving your issue and thank you for letting us know.

Cheers,

Jeremy



Jeremy Tammik
Developer Technical Services
Autodesk Developer Network, ADN Open
The Building Coder

0 Likes