Hi,
Since 5.5, a new option to “Show Exec Pin” has been added
The main benefit of Exec Pins nodes VS Pure Node is to “cache” the result as we wouldn’t execute the logic behind the Get on every request to the result
I’d like to know if the new option “Show Exec Pin” reflects that behavior or if it’s just a matter of visual?
So in the following example I’d expect the conversion to be done only once (A) with Exec Pins whereas the conversion would be done three times (B) without exec pins[Image Removed]
If the Pure method is not costy it’s not much of an optimisation but if it is … it’s a very useful pattern and I used to do it with a Macro like that:
[Image Removed]
[Image Removed]
Steps to Reproduce
Right click on a pure node and select “Show Exec Pin”
Hi Raphael,
Here’s why it was added:
The primary use case of this feature is to convert pure nodes (no exec pins) to impure node (with exec pins). When you mark a function as pure, it now means that it “defaults to a pure state”, but you can now toggle that at the call site.
Originally, “pure” was intended to be “functional pure”. In other words, they don’t modify state and are deterministic. If they were, we could employ some tricks to cache the result for a given input and avoid multiple evaluations.
The reality is that we can’t guarantee something like that. Someone can easily write a C++ function that modifies a member variable or is non-deterministic, and then incorrectly mark it as pure. GetTimeSeconds and any of the random stream functions are examples of functions that are tagged as Blueprint pure, but are not deterministic. You can’t really cache these values because they change with every call. As a result, we’re forced evaluate pure functions for every connection they have.
One of the main disadvantages of evaluating pure nodes with every connection is the performance cost that comes with it, which a lot of developers don’t realize. Pure nodes were never intended to be expensive, but that’s not something that was can control. We’ve definitely had cases where someone has written an expensive pure function. When connected to multiple pins, they could show up in perf reports. The compromise was to add this toggle to make it a little easier to control instead of writing specialized code to manually save the result to a variable.
You can also go in the other direction: make functions that default to impure to pure at the call site. However, you have to enable the project setting. Under the category, “Blueprint Project Settings”, check the “Allow conversion of impure nodes to pure ones” box. We don’t enable this by default because we don’t want to users to accidentally run into the aforementioned perf issues tied to pure nodes, but the option is there for anyone who wants to opt in.
-Dave
Hi Dave, thanks for your answer.
However the answer isn’t exactly straight forward but what I get from the quote “The compromise was to add this toggle to make it a little easier to control instead of writing specialized code to manually save the result to a variable.” is that the toggle to exec pin does what I expect aka “cache the value on pin execution”.
In my example, I’m then expecting that in the use case A, the conversion happens only once and then the result is used 3 times in opposition to the use case B where we would repeat the conversion 3 times.
Side notes about pure functions: when declared in cpp, unless the programmer enforces the use of a pure function with the meta “BlueprintPure”, it is expected to be a const function.
- There’s no distinction in the blueprint node UI to know if the function is const (and thus won’t modify member variables) or not. This could be a very important bit of information
- When making a pure function en blueprint : there’s no feedback for the user for him to know that the blueprint function he’s writting may modify member variable. That kind of feedback feels pretty important as it is for many people obscure what a pure node does in terms of behavior. (like you mentioned people don’t realize that executing the same query many times means running the full query many times)
In order to help with the understanding, may I suggest a blueprint debugger improvement? We already have the flow represented in the debugger (exec connections appearing when triggered). It would be a good improvement (although maybe costy in terms of performances depending on the implementation) to represent the flow of these pure nodes to represent each time they’re called.
A bit of improvement as well would be to provide the option to show the number of time a line/connection has been triggered : this would help in the understanding I believe.
Thanks for the insight! If the way it works is as expected, it’s a very very good feature to have added 
> In my example, I’m then expecting that in the use case A, the conversion happens only once and then the result is used 3 times in opposition to the use case B where we would repeat the conversion 3 times.
Yes, that is exactly what happens. The effect is more obvious if you test this with a random integer node.
> There’s no distinction in the blueprint node UI to know if the function is const (and thus won’t modify member variables) or not. This could be a very important bit of information
I can suggest this internally to see what folks think.
> When making a pure function en blueprint : there’s no feedback for the user for him to know that the blueprint function he’s writting may modify member variable.
Just to be clear: “const” doesn’t guarantee no side effects. A const native function can always change a global variable or trigger an IO operation. Or even change a native C++ variable that’s marked as “mutable”. Additionally, purity must be deterministic. For example, the random value nodes don’t change state, but they’re not deterministic since each call returns a different value.
This is what I was getting at previously: “Pure” doesn’t mean “Functional Pure”. It’s better to think of “Pure” as “does this node show exec pins or not”. It’s an unfortunate name that we’re kinda stuck with. Naming the toggle to “Show Exec Pins” was intended to move away from the “Pure” terminology. We’re looking to get the documentation updated here because it still adheres this old (and incorrect) definition.
> represent the flow of these pure nodes to represent each time they’re called.
That has been on our list for a while. It just hasn’t happened due to a lack of man power. It would be quite a bit of work, too (though doable). But yes, it would definitely improve the debugger experience.