Improving NodeGraph::getNodeDef performance#2933
Conversation
…h NodeGrap::getNodeDef
…t performance improvement.
| std::unordered_map<string, std::vector<PortElementPtr>> _portElementMap; | ||
| std::unordered_map<string, std::vector<NodeDefPtr>> _nodeDefMap; | ||
| std::unordered_map<string, std::vector<InterfaceElementPtr>> _implementationMap; | ||
| std::unordered_map<string, ImplementationPtr> _nodeGraphImplMap; |
There was a problem hiding this comment.
I think its possible for more than one Implementation element to use the same NodeGraph - so this might need to be
std::unordered_map<string, std::vector>
For standard surface (here) we have two different Implementation elements that have the same NodeGraph
There was a problem hiding this comment.
Curious if there are any metrics for the additional update cost especially if the cache updates frequently ? At most currently 1 or a few items will ever be found and it's mostly static during the lifetime of a document.
| std::replace_if(name.begin(), name.end(), isInvalidChar, '_'); | ||
| name = replaceSubstrings(name, _invalidTokens); | ||
| if (std::find(_reservedWords.begin(), _reservedWords.end(), name) != _reservedWords.end()) | ||
| if (_reservedWords.find(name) != _reservedWords.end()) |
There was a problem hiding this comment.
This seems like a nice improvement :) good catch
| if (!nodedef) | ||
| { | ||
| for (auto impl : getDocument()->getImplementations()) | ||
| ImplementationPtr impl = getDocument()->getImplementationForNodeGraph(getQualifiedName(getName())); |
There was a problem hiding this comment.
Would it make sense that instead of caching the implementation reference and then getting the nodedef here, just store the nodedef reference as the lookup ?
| std::unordered_map<string, std::vector<PortElementPtr>> _portElementMap; | ||
| std::unordered_map<string, std::vector<NodeDefPtr>> _nodeDefMap; | ||
| std::unordered_map<string, std::vector<InterfaceElementPtr>> _implementationMap; | ||
| std::unordered_map<string, ImplementationPtr> _nodeGraphImplMap; |
There was a problem hiding this comment.
Curious if there are any metrics for the additional update cost especially if the cache updates frequently ? At most currently 1 or a few items will ever be found and it's mostly static during the lifetime of a document.
Fuzz testing was consistently findings some very slow inputs. Looking at this inputs through
perf.I've added a performance regression case that builds example trees that have this issue.
I think I've found a couple of issues that come together to cause the processing time for loading .mtlx files grow linearly.
Syntax::makeValidNameusesstd::findon_reservedWords. This is astd::set<string>and supports a faster O(log N).find(). Give's a minor improvement (5-6%)MaterialX/source/MaterialXGenShader/Syntax.cpp
Lines 152 to 162 in 9ebe5f1
NodeGraph::getNodeDefcallsDocument::getImplementations()on every invocation. This rebuilds a fresh vector by scanning every document-level child with a dynamic_pointer_cast on each, with no result reuse.Document::Cachealready maintains hash-map lookups for ports, nodedefs, and nodedDefImplementation, but the reverse direction (nodegraph-name -> implementation) was missing. I've added a _nodeGraphImplMap to the cache, populated during rebuild() alongside the existing maps, and replaced the linear scan with an O(1) lookup.MaterialX/source/MaterialXCore/Node.cpp
Lines 742 to 756 in b661a4b
If we look at benchmark time I get a significant improvement