-
Notifications
You must be signed in to change notification settings - Fork 49
Optimize Maya host.ls() method #456
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
|
Think assuming objectsSets as containers is fair enough. If this break anyone's pipeline, it will encourage a good discussion about it. |
|
@davidlatwe could you give this a test run on your end, including testing whether "nested" containers still work as expected for you? I've tested these cases including with scrambled namespaces as I knew that could be a use case on your end (as such I kept the other optimization tests with |
|
Neat.
|
I believe so. I think
Good point. If ever needed we can add a fallback to
Sure did. But somehow it still was slower in heavier scenes. As if it still tries to filter some garbage stuff. Will do another check on Monday. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Cool, looks good to me.
|
Just tested it on my end with a big set dressing scene, there were Speed comparison
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great work !!
Interesting that it's still that slow @davidlatwe. I wonder what the next bottleneck would be in that case. It must be the parsing of the container that uses Or, is your slowdown because of a custom config call within Should I look into optimizing this further? |
|
Sorry for the delay, had an extra day off due to the weather here ⛈️
I have tested that if I remove the
No, I have changed the code here so that custom call was actually returning an empty |
Haha. So in your case it's beneficial that it includes additional data not defined in the schema. What would be the verdict? Would we want Option 1) Leave it as is. I'm thinking of maybe leaving it as is for now as grabbing only the relevant attributes might be slow too with the backwards compatibility to Option 2) First get the schema version, then get only those relevant attributes. 👍 We could maybe do a check on attribute def parse_container(node):
# psuedocode
fn = om.MFnDependencyNode(node)
container = dict()
# Get container attributes
plug = fn.findPlug("schema", True)
if not plug:
# assume container-1.0
container["schema"] = "container-1.0"
attributes = [
"id",
"name",
"author",
"loader",
"families",
"time",
"subset",
"asset",
"representation",
"version",
"silo",
"path",
"source"
]
else:
schema = plug.asString()
if schema == "container-2.0":
attributes = [
"schema",
"id",
"name",
"namespace",
"loader",
"representation"
]
else:
# Unknown container schema
return None
# Collect the values
for key in attributes:
plug = fn.findPlug(key, True)
if plug:
container[key] = plug.asString()
# `objectName` is special, it doesn't refer to an
# attribute but refers to the node name.
container["objectName"] = fn.name()
if validate:
schema.validate(container)
return containerOption 3) Read the schema version from container and directly get the required attributes from the actual schema. For example: import avalon.schema
schema = "container-2.0"
attributes = avalon.schema._cache[schema + ".json"]["required"]
print attributesWhich if we add a import avalon.schema
schema = "container-2.0"
attributes = avalon.schema.get(schema)["required"]
print attributes |
|
So I did a quick speed comparison with the above Option 3 implemented with this code: def parse_container(container, validate=True):
import avalon.schema
sel = om.MSelectionList()
sel.add(container)
dep_node = sel.getDependNode(0)
fn = om.MFnDependencyNode(dep_node)
data = dict()
# Get container schema version
if fn.hasAttribute("schema"):
plug = fn.findPlug("schema", True)
schema = plug.asString()
else:
# Backwards compatibility
schema = "container-1.0"
# Get attributes from schema
schema_doc = avalon.schema._cache.get(schema.rsplit(":", 1)[-1] + ".json",
None)
if not schema_doc:
# Unknown schema version. Ignore..
return {}
attributes = schema_doc["required"]
# Collect the values
for key in attributes:
if key == "objectName":
# `objectName` is special, it doesn't refer to an
# attribute but refers to the node name.
data["objectName"] = fn.name()
continue
plug = fn.findPlug(key, True)
if plug:
data[key] = plug.asString()
if validate:
schema.validate(data)
return dataI timed it in a scene with 12 containers, and did 100 iterations of import time
s = time.time()
for x in range(100):
list(pip.ls())
e = time.time()
print e-sThe difference between old and new For a small scene it seems the speed difference is neglible. @davidlatwe could you try giving this a go with your heavy scene? With these current stats I must admit the more complex code here definitely is not worth it. It actually seems that for many containers most time is spent on the Another speed comparison in a heavier scene with 24 containers and many nodes! running 5 iterations of Parsing the container through the API doesn't seem to be worth it for these scenes with under a hundred containers. I wonder if it helps more in your scene with |
|
Thanks ! So I have tested with my heavy scene, and :
Looks like disabling validation is the best option here ! |
@mottosso technically we could, however it's also exposed in |
|
This PR is ready to be merged. 👍 |
|
Actually, could we add |
|
What, there's validation somewhere? :O I can't spot it. What kind of validation is happening? |
It's only the schema validation not a Pyblish validation. ;)
Sure. Should I just make it |
|
|
- Also corrected some docstrings
Exposed the |
|
Of course it's not Pyblish validation? Would prefer removing the validation altogether... oh, and you already made the change. Please heavily discuss any change to the API. API changes are forever, ever ever. In this case, there must be a clear reason to add
Don't change the API, unless you discuss discuss discuss. |
|
If you introduce an optional validation and it's enabled by default, does that mean we won't get the speed up benefit by using the GUI tools in Avalon? |
Sure. Agree. ;) Maybe pushing ahead to rapidly with the commit. :) The change wasn't directly meant to be merged but to leave it in a state for the PR discussion to continue, but I guess it does create a bit of "noise" in the commits.
@mottosso This could be dangerous if by any chance someone accidentally created a set with the same
@tokejepsen Correct. The UI would still use the slower version that is mostly noticeable with a very high amount of containers. The original optimization at the beginning of this PR however is there nonetheless. This validation is only another optimization. The reason for removing the validation altogether currently is purely an optimization for heavy scenes. I guess we can have a little "vote" for removing it altogether? Is there anyone who can not live with the validation being removed from |
|
Maybe revert the previous commit (f588483), merge, and open another issue ? An issue to discuss whether to validate container data entries by default, or remove the validation entirely from |
I'll just revert the change either way, because initial consensus seems to be opposing the idea of it anyway. ;) Then whether we use this PR or another for removing the validation altogether I don't really mind with either. Feel free to merge once I reverted it and open a clear issue for the "validations". :) |
Thanks. It makes me very nervous that you're willing to make changes to the API so lightly. Any change to the API better have a darn good reason for happening. Anything else is for your own configs, not core.
;) |
|
Merging this ! |
Issue
In large scenes with many nodes the
host.ls()query for host Maya is slower than it needs to be. I've seen cases where a single query took 3.7 seconds.What's changed?
The
_ls()method that was used to just list all the relevant avalon containers in maya by node name was relatively slow in the way it queried it when used in large scenes. It previously did a full query on all nodes by an ".id" attribute whereas we should do it only overobjectSetsas those are the container nodes in the Maya pipeline. This has now been changed to using the Maya Python API 2.0 to iterate over all dependency nodes of typemaya.api.OpenMaya.MFn.kSetAlso, it did that full iteration twice, because it also had to look up for the backwards compatibility id
pyblish.mindbender.container. This has now been changed to just detect whether the id value is any of the two directly as opposed to iterating the scene twice.Speed comparison
As I was developing an optimized method I've built some prototype functions as a replacement and compared timings in smaller and big scenes. In all cases I've found that this new implementation was much faster.
In my large test scene the difference was huge:
The newer method being faster also confirmed in a small test scene by @tokejepsen here:
The implementation in this PR is
New 2in those tests.Fact: Containers are objectSets?
In the Maya pipeline in Avalon containers are created with the
containerisefunction that is exposed in avalon core, this creates theobjectSetthat encapsulates the nodes that belong to specific loaded data. As such this implementation assumes that all nodes that we allow to be acontaineractually is of typesetbecause that's what Avalon core provides, plus it's what I've seen (logically) used throughout all available configs.However, do note, that if someone ever relied on something having the
.idon a random node in the scene and get detected as a container that with this change it will not be detected if it's not anobjectSet.