graph::invoke and other apex questions

   1658   15   3
User Avatar
Member
27 posts
Joined: Nov. 2017
Offline
I've been wondering about apex best practices.

We're using it for non-rigging use cases and building some rather large graphs. Apex performance seems to drop off once you reach a few thousand nodes.

One solution to this seems to be using multiple sop level invoke graphs, or to use sop::apex::invokegraph inside apex. There is also a graph::invoke, but I haven't seen any examples of how to use this. Additionally, I was wondering if there is any difference in how a subnetwork is evaluated compared to a subgraph, or just "expanding" everything out.


So, my questions are:

1. How is graph::invoke used, are there any examples of this?

2. Is graph::invoke the "intended" way to invoke other apex graphs in apex, and does it have any caveats/advantages over using sop::apex::invokegraph

3. Is the large-node-count apex performance issue understood, is it an area of concern and active development?

4. Are there more apex performance-improvements incoming that relate to large graphs or working on large geometry?
User Avatar
Member
27 posts
Joined: Nov. 2017
Offline
Could somebody also shed some light on the "intended" way to set attrib values on a whole load of points?

I see that according to the docs, setpointattribvalues is supposed to set an attrib value on all points and take an array as an input. But the node itself in Houdini doesn't take an array as an argument. https://www.sidefx.com/docs/houdini20.5/nodes/apex/geo--SetPointAttribValues.html [www.sidefx.com]

I also cannot see a corresponding getpointattribvalues.

In theory, would this method be faster than using setpointattribvalue and running it in a loop over all points?

Attachments:
Screenshot 2024-07-13 151147.png (107.7 KB)

User Avatar
Member
373 posts
Joined: June 2023
Offline
I'm by no mean an APEX expert, but I don't think geo::SetPointAttribValues can do what you describe. It sets all points' attributes to the same value. For the same reason there is no get version.

SetPointAttribValuesByName kinda does it, but you need to have an index attribute first (e.g. Enumerate SOP with string type) and it takes a variadic input instead of an array, so it's still less than ideal...
Edited by kodra - July 14, 2024 02:09:39
User Avatar
Member
27 posts
Joined: Nov. 2017
Offline
Aha. Thanks for the insight. Any idea how to set a whole load of a whole lot of points efficiently?
User Avatar
Member
7871 posts
Joined: July 2005
Online
Can't think of anything better than a manual loop offhand. How are you generating the array of values? I think most often, we'd expect to use geo::SetPointAttribValues as the values are being generated.
User Avatar
Member
27 posts
Joined: Nov. 2017
Offline
So, geo::SetPointAttribValue sets the value of a single attrib on a single point, and geo:setpointattribvalues sets an attribute across a whole load of points?

I may be misunderstanding, but geo::SetPointAttribValue is quite slow for large point numbers no, since it means the geometry needs to be updated repeatedly in a loop?

If there was a way to set an attribute across all of the points at once wouldn't this be more optimal, since it only needs to update the geo once?

The utility of geo::setpointattribvalues seems very limited, because you are limited to setting all points to the same attribvalue, which is rarely needed. It seems like it would be more useful to be able feed an array, where the array index corresponds to the point number that is being set? The docs seem to describe this functionality.
Edited by GeorgeHulm - July 15, 2024 09:00:03
User Avatar
Member
373 posts
Joined: June 2023
Offline
I don't know how APEX works internally. But generally speaking, assumption like this:

I may be misunderstanding, but geo::SetPointAttribValue is quite slow for large point numbers no, since it means the geometry needs to be updated repeatedly in a loop?

isn't necessarily true (unless you know how APEX works internally and have a reason to believe it).

geo::SetPointAttribValue doesn't make a copy of the geometry. Generally speaking geo:: nodes are very fast. There isn't an obvious reason to assume it's slow for me. Of course I might be wrong. The only way to know is to profile it.

If there was a way to set an attribute across all of the points at once wouldn't this be more optimal, since it only needs to update the geo once?

Unless APEX stores the values of an attribute across all points in continuous memory (like one single C++ array), I don't think "update all attributes at once" is a thing.
Edited by kodra - July 15, 2024 09:19:54
User Avatar
Member
27 posts
Joined: Nov. 2017
Offline
I can't speak to how apex works under the hood, and i'm sure that many of my assumptions/ideas are unfounded, since admittedly, I am coming from vex/sops and trying to understand this new context. buuuuut...

It's very easy to prove that geo::setpointvalues is able to set attributes on all points on a geometry *much faster* than using geo::setpointvalue inside of a loop. The difference in cost becomes quite huge when applied to lots of points, or when this is done many times.

But the limitation that all points need to take the same value kind of nullifies setpointattribvalues to very niche use cases. I'm unsure why it has this limitation, if its intended, if sidefx are aware of this, if its an issue/mistake, or if there's another intended way to set different values on different points that maintains speed.
Edited by GeorgeHulm - July 15, 2024 09:40:32

Attachments:
setpointvalue_test.hip (132.2 KB)
Screenshot 2024-07-15 153410.png (15.8 KB)

User Avatar
Member
27 posts
Joined: Nov. 2017
Offline
It feels like something to this effect should be possible, but perhaps this is just a pipe-dream and performance improvements will come from elsewhere?
Edited by GeorgeHulm - July 15, 2024 09:48:05

Attachments:
Screenshot 2024-07-15 154401.png (70.2 KB)

User Avatar
Member
8784 posts
Joined: July 2007
Offline
AFAIK APEX is still missing flow control for parallel operations on arrays

Equivalent to map(), reduce(), filter() in other languages

Its also likely that APEX may need a specialized parallel foreach loop for geometry elements to replace the need of resorting to VEX for that, since current foreach is allows or requires feedback of previous result it's highly likely that its not parallelized and therefore much slower

For now you can use sop::attribvop node to use VEX snippet code for any per element operations which will likely be faster than looping in APEX on heavy geo
Tomas Slancik
FX Supervisor
Method Studios, NY
User Avatar
Member
130 posts
Joined: June 2019
Offline
Was about to suggest the same thing. Just use vex.

I haven't even understood yet what is wrong with it and why there is this idea coming: "vex is bad inside apex". So you have this very robust established framework to work with geometry and then you just giving up on it and trying to replace it with this? I get it's not great to evaluate a rig with complicated dependencies and just a couple of matrix operations, but I don't think it's banned

I think apex is very new and there is no "best practices" yet, but I'm feeling that if you hit the performance penalty with huge geometry it's just because it's an anti-pattern. Probably you don't need apex at all. It's a very interesting framework to build up dependency tree for delayed evaluation, but is delayed evaluation even a problem for foliage and generally procedural geometry. Can it be solved with modern sop things like graph invoking? (how render procedurals works which is also delayed evaluation) etc

Also at this point I think it would be great not to replicate SOP in apex context. I think we need something like SOP Import in Copernicus - a node we can dive in and build sop network from compilable nodes (hdas included btw) that would be just implicitly compiled and invoked via sop::invokegraph
User Avatar
Member
27 posts
Joined: Nov. 2017
Offline
Thank you for the great responses.

edit: For reference - we use precompiled vex in our foliage toolset as well as opencl when it is appropriate. To give some insight into how slow we have found vex to be inside of apex, we built the same solver in apex and in vops and they run at approximately the same speed over a few million points, which feels insane - but we get fairly consistent results. Vex pulls into the lead when we start getting into the dozens and hundreds of millions.

A multithreaded loop in apex sounds amazing for what we are doing, I think we would see some enormous benefits if this could be pulled off.

Is there some fundamental reason why apex is bad with giant geo? You say anti-pattern, what do you mean by this with regards to apex - will it always be slower than vex or sops with large geo?

It is my understanding as well that many sop verbs are multithreaded. Does this hold true in the apex context when the verb is used?


edit: our performance issues with vex stemmed from other areas, but there are real vex overheads which are detailed later in this discussion
Edited by GeorgeHulm - July 17, 2024 06:27:11
User Avatar
Member
7871 posts
Joined: July 2005
Online
George_Hulm
It is my understanding as well that many sop verbs are multithreaded. Does this hold true in the apex context when the verb is used?

Yes, individual APEX nodes are free to multi-thread. It's just that there's no multi-threading at the APEX node level right now.
User Avatar
Member
130 posts
Joined: June 2019
Offline
This is just my intuition and I didn't have much experience with apex, just some weird experiments around rigging and python handles.

I just think that extensive processing on geometry is better to be done in sop context. Not expressed as loops in apex. This is what I think is antipattern, but again it's just an intuition. So apex is more like "scheduler" of higher-level operations rather than framework to express heavy calculations.

For example kinefx being based on vex/vops theoretically could transform hundreds of millions of joints, but the thing that with hierarchy you don't benefit from parallel computations. Rigs are not particle systems. Scheduling it in a way apex does makes way more sense and also opens up to better viewport integration. The other way would be to generate a huge python script like TOP, but that would kill viewport performance

I don't feel it's very practical to add multithreaded loops in apex. Just think it's not possible without bumping complexity to even higher level with some bizarre rules in network to avoid race conditions. It's already not very friendly with all this explicitness about in-place operators and how to avoid copies. If you add some rules on top for multi-threaded loop scope like writing by reference or is this read-only, is that writeable only on current iteration etc, I'm afraid complexity just explodes. If you limit those loops on geometry I also won't get it. It's essentially adding vex runtime inside a very new (and also not very polished) context.

So yeah, apex from my point of view is exactly like scheduler, very flexible one. Of course you benefit from multithreading if you're using already multithreaded verbs. In the same way you benefit from gpu using cop:: verbs. But your graph is not parallelized. May be it will be, I don't know Most likely it'll be in a form of scheduling subnets or individual graphs and collecting result as synching point, rather than explicit loops, but who knows

If you can get gains from not only delayed evaluation, but also from partial - it's definitely a good fit. But if you're trying to create solvers and geo processing, idk - it just feels bit wrong, and I personally doubt it will ever be capable of doing that efficiently.
User Avatar
Member
27 posts
Joined: Nov. 2017
Offline
Really informative discussion thank you. Thanks to this conversation and others I think I have a fuller picture now, which I'll post here for others as well:

  • The main cost of VEX compared to native sop verbs is during graph rebuilding, where the vex gets compiled
  • During evaluation, Vex has no "hidden cost" compared to using sop verbs. The cost is due to marshalling data into geometry
  • If VEX runs over a small amount of elements(eg <1024), then vex runs in single-threaded mode, which means the vex is likely slower than the equivalent operations in native apex at these element counts.
  • There is a relatively high cost to invoking compiled sop graphs in apex

The implications of this are as follows:

Large numbers of VEX nodes are "dangerous" for performance, but only in the graph building step, due to the time it takes to compile a large number of snippets. In theory this issue can be resolved by using precompiled vex. Which we have done, with some caveats.

The only way we found to run compiled vex from within apex was to invoke a sops level compiled graph. This *works*, but sacrifices some runtime performance in order to speed up the graph building step dramatically. Run Snippet and AttribVop can not run compiled vex from disk.

If Apex attribvop and runvex were updated to run compiled vex, this would be the ideal result for high-performance systems that require high numbers of vex snippets.

I've attached an example and will submit an RFE

Attachments:
Apex_RunVex.zip (27.7 KB)

User Avatar
Member
27 posts
Joined: Nov. 2017
Offline
Some more context thanks to the kind devs at sidefx who helped us learn how to do things correctly:

graph::templates that get invoked only need to be "setup" once and these can get invoked at any point using graph::invoke in apex.
  • Quick Links