Getting Metadata performance

Which is faster:

  1. Getting object by piece of it’s name
  2. Getting object by UserObject attribute

I’m going to hazard a guess that it will be about the same - both are object attributes at the same level, so searching for either will probably take about the same time…

OK, a simple test:
I created a box, give it a name “box_one” and also set a user text “box_one” under a key “searchtest”

I then ran the following script -
First it times one million searches of the object name to see if it contains “one”
Then it does the same to see if the user string key “searchtest” contains “one”

import rhinoscriptsyntax as rs
import scriptcontext as sc
import Rhino, time

objID=rs.GetObject()
#object has object name 'box_one'
#object has user text 'box_one' stored under key searchtest
#will search if object name or user string contains "one"

obj=rs.coercerhinoobject(objID)
fragment="one"

st1=time.time()
name_search_count=0
for i in range(1000000):
    name=obj.Attributes.Name
    if fragment in name: name_search_count+=1
print "Name search time={}, Found {}".format(time.time()-st1,name_search_count)
    
st2=time.time()
string_search_count=0
for i in range(1000000):
    ustr=obj.Attributes.GetUserString("searchtest")
    if fragment in ustr: string_search_count+=1
print "String search time={}, Found {}".format(time.time()-st2,string_search_count)

The results are:
Name search time=1.56773376465, Found 1000000
String search time=1.72177001953, Found 1000000

A difference of less than 0.2 seconds in a million searches is not extremely significant IMO…

2 Likes

You probably need to run that test on a 3dm that has a several thousand objects where only the very last object has the searched for data.

If you want to search really fast and object name is a good key for you then you should look into ObjectEnumeratorSettings().

Test:

  • add box, array 100x100x10
  • run randomnames.py
  • run test_search_speed.py

I get as output:

Name search time=0.227386474609, Found 1
String search time=0.262298583984, Found 1
Enumerator settings time=0.0179595947266, Found 1

The scripts:

randomnames.py

import random, string
import scriptcontext as sc

def randomword(length):
   letters = string.ascii_lowercase
   return ''.join(random.choice(letters) for i in range(length))
   
obs = [o for o in sc.doc.Objects]
for o in obs[:-1]:
	o.Attributes.Name = randomword(15)
	o.Attributes.SetUserString("somelongkey", randomword(15))
	o.CommitChanges()

o = obs[-1]
o.Attributes.Name = "longnametosearchfor"
o.Attributes.SetUserString("somelongkey", "longnametosearchfor")
o.CommitChanges()

test_search_speed.py:

_count=0
for i in range(1):
    for obj in sc.doc.Objects:
        name=obj.Attributes.Name
        if fragment in name: name_search_count+=1
print "Name search time={}, Found {}".format(time.time()-st1,name_search_count)
    
st2=time.time()
string_search_count=0
for i in range(1):
    for obj in sc.doc.Objects:
        ustr=obj.Attributes.GetUserString(key)
        if fragment in ustr: string_search_count+=1
print "String search time={}, Found {}".format(time.time()-st2,string_search_count)

st3=time.time()
enumeratorsettings = Rhino.DocObjects.ObjectEnumeratorSettings()
enumeratorsettings.NameFilter=fragment
obs = [o for o in sc.doc.Objects.GetObjectList(enumeratorsettings)]
print "Enumerator settings time={}, Found {}".format(time.time()-st3, len(obs))
2 Likes

Thanks Nathan,
Does this enumeratorsettings look into usertext attributes as well? Can I make it look there?

My models where I need this have over 20k objects inside. Also I need to check for name of the object and 5 usertext attributes.

in the second case I have less objects between 5k and 10k, but I have over 10 usertext attributes on each object.

ObjectEnumeratorSettings doesn’t have an entry for user text that I could find.

The test I posted above has you create 10k objects, and creates the worst case scenario by having the last object be the one you need.

Searching name through Attributes is 0.22s, string from user data text is 0.26s.

It isn’t so much the accessing of those bits in Attributes, but rather the traversing of the objects in the document.

1 Like

There is a method which allows to FindByUserString without iterating all objects in python. Maybe you could use it to search for one of the required user strings and filter the results using the other required attributes.

_
c.

1 Like

:smiley:

image

image

Apparently there it is also in scriptcontext

sc.doc.Objects.FindByUserString()

I think the developer pages always quote the latest version number (i.e. does it work now), not the first version from which the method started working.

1 Like

yes, it works.