Entities Observer Slows Down Dynamic Components Dramatically

Please see the videos below. Is this to be expected?

In the attached project, there is a Dynamic Component. In the Ruby Console, you can see how long it takes to update this component. When we attach observers with the following code, the updating of the component takes considerably longer. This difference is much worse in an actual project where it often takes 10x as long or doesnt even finish, as can be seen in the attached video.

class MyEntitiesObserver < Sketchup::EntitiesObserver
  def onElementModified(entities, entity)
    puts "onElementModified: #{entity}"
  end
end

Sketchup.active_model.definitions.each {|e| e.entities.add_observer(MyEntitiesObserver.new)}

Do you have any hints if this is due to the way the dynamic component is authored or if this is some characteristic of the project? The project this component is embedded in is over 100mb large.

Please let me know if I can help with any further information.

DynamicComponentOnly.skp (291.3 KB)

Cheers
David

I don’t have much experience with the observers, but I’ve moved the topic to Ruby API… to see if you get more attention here… :blush: :innocent:

1 Like

Printing text to the console takes a little time. This observer may also fire many times during a redrawn, especially if added to all component definitions.

You could try limiting the observer only to the things you really need to listen to.

1 Like

This code was only used for demonstration purposes. You can actually declare an empty onElementModified method with the same result.

Unfortunately, I am sure that we need to listen to all changes in the project.

So is it to be expected that the mere presence of listeners slows down the evaluation of the dynamic components?

What is the higher level purpose of listening to these changes? Maybe you could listen to model transactions instead, depending on the use case. In that case the observer should only fire once for everything wrapped in a single start_operation/commit_operation. Currently I suspect it can fire tens if not hundrades of times for a DC redraw.

It also may be more practical to instantiate one Entities observer, and attach that to all definitions, rather than one for every definition. Might save some memory.

One thing to keep in mind is that the DC extension itself attaches it’s own observers to every definition that has a dynamic dictionary. So there is a whole lot of watchin’ and reactin’ goin’ on.

Thank you for your replies!

What is the higher level purpose of listening to these changes?

We send live updates to our rendering window. We need to know which instances have changed. If we were to only listen to start/commit operations, we would have to figure that out for ourselves (as far as I understand).

It also may be more practical to instantiate one Entities observer, and attach that to all definitions, rather than one for every definition. Might save some memory.

In our real plugin, we use one observer instance. Again, this was just for demonstration purposes to show that the decreased performance has nothing to do with our plugin per se.

The ComponentDefinition class is a subclass of Entity, so might also fire for an EntityObserver's #onChangeEntity callback. Perhaps that would be faster ?

The other alternative is to attach the above to each instance that you wish to watch.

You could try to cache the GUID (revision identifier) for all definitions. Then you can use a model observer to listen to transactions, and filter out the definitions that have gotten new GUIDs. For changes at the model root you’d probably still need one single entities observer.

Just to provide a bit more context: I work for Enscape (https://enscape3d.com/) and the project stems from a customer support ticket.

If I understand correctly, your suggestion is that we should check for the changes in the component definitions ourselves instead of attaching an observer to them. I cannot imagine that this is the way the API is meant to be used since add_observer exists.

You can add completely empty observers and the same performance issues arise. So writing the following into the ruby console would have the same effect:

class MyEntitiesObserver < Sketchup::EntitiesObserver
end

compDefObserver = MyEntitiesObserver.new

Sketchup.active_model.definitions.each {|e| e.entities.add_observer(compDefObserver)}

We are wondering if this might point to a problem of the API that could be solved on your end or if it is due to the way the customer defined their dynamic component.

If I understand this correctly, the problem with entities observer here is that it fires once for each change to entities. If 30 component definitions have 10 entity changes each, that would equal 300 changes, even if it to the user looks like one single big change. I don’t think you need to track each individual change to an entities collection if you just want to track the component as a whole changing.

Have you made the observer count how many times it is being called?

The function is called 36 times. In the empty project I attached in the OP, the performance hit is negligible. In the full project that was sent to us, it takes more than a minute to update with the same amount of calls whereas without observers it takes half a second.