Why are Model#find_entity_by_persistent_id and Model#find_entity_by_id so SLOW
As I have some doubt in my plugins, I wrote a quick test to measure the performance of these 2 methods, at least on containers:
Model#find_entity_by_persistent_idModel#find_entity_by_id
The code for the test is below. It profiles the time taken to retrieve a container from its ID by
- the native API method
- versus a brute force method exploring all definitions and trying to find a match of ID among their instances
To run the code, just select an object in the model.
def test_pid_comp
#Take the selected container in the model
model = Sketchup.active_model
comp = model.selection[0]
return unless comp
pid_comp = comp.persistent_id
eid_comp = comp.entityID
nb_iter = 200
c = nil
#Method: find_entity_by_persistent_id
t0 = Time.now
for i in 0..nb_iter
c = model.find_entity_by_persistent_id(pid_comp)
end
puts "\nfind_entity_by_persistent_id = #{Time.now - t0} s - #{c.persistent_id}"
#Method: Custom method by exploring definition instances for persistence id
t0 = Time.now
for i in 0..nb_iter
model.definitions.each do |cdef|
c = cdef.instances.find { |c| c.persistent_id == pid_comp }
break if c
end
end
puts "Custom method PID = #{Time.now - t0} s - #{c.persistent_id}"
#Method: find_entity_by_id
t0 = Time.now
for i in 0..nb_iter
c = model.find_entity_by_id(eid_comp)
end
puts "\nfind_entity_by_id = #{Time.now - t0} s - #{c.entityID}"
#Method: Custom method by exploring definition instances for EntityID
t0 = Time.now
for i in 0..nb_iter
model.definitions.each do |cdef|
c = cdef.instances.find { |c| c.entityID == eid_comp }
break if c
end
end
puts "Custom method EntityID = #{Time.now - t0} s - #{c.entityID}"
end
The result is (tried 2 times, to show this is consistent) in SU2025:
test_pid_comp
find_entity_by_persistent_id = 2.563366 s - 4734942
Custom method PID = 1.2880996 s - 4734942
find_entity_by_id = 2.6136978 s - 808890
Custom method EntityID = 1.2985735 s - 808890
=> nil
test_pid_comp
find_entity_by_persistent_id = 2.5356326 s - 4734942
Custom method PID = 1.1624917 s - 4734942
find_entity_by_id = 2.5597183 s - 808890
Custom method EntityID = 1.1454812 s - 808890
=> nil
So the native API methods are MORE THAN TWICE SLOWER than a brute force method written in Ruby.
I am surprised, because I thought the API methods were just doing a lookup in an internal table....
To run the test, I took a model in the 3D Warehouse which is not extremely complex (270 containers)
There is no internal lookup table for IDs or PIDs. It's doing a full model search.
find_entity_by_persistent_id does however have a scope argument that narrow downs what is searched. If you are using PIDs for definitions, have you tested that?
Unfortunately, there is no filter such as container (or Group and ComponentInstance).
Using the filter entities give the same time:
find_entity_by_persistent_id = 1.19062 s - 3306586
find_entity_by_persistent_id (entities: true) = 1.2159846 s - 3306586
Custom method PID = 0.9147268 s - 3306586
To search a container would need a new option - and it sounds like a very reasonable option to me. It just haven't come up before until now.
I guess it would make sense to add the scope options to find_entities_by_id as well.
Logged as: SKEXT-4828