I noticed there’s some differences in profiling time when it comes to using the Evaluate component vs the c# component to generate a list of true/false booleans values. See screenshot attached.
I also included a smaller than component to test and this is surprisingly even faster, can anyone elaborate as to why this is?
Side note: they all output 13824 values, the list is still small, but I’d imagine the compute time to increase exponentially when working with larger/longer lists.
The difference is in the processing of the inputs, the smaller than has a quick cast to bool (or maybe input data is already bool), C# scripting inputs are a bit special, it has a slower casting I guess, and Evaluate has a parser to convert text to operations so is expected to be much slower and also a generic input which requires casting.
A C# script component copies value-types data into a new list with wrapped GH_Types (before and after processing). There are ways in optimizing this. This is the major bottleneck. The next is the fact that it’s a script, which needs to be compiled. The first execution after changing the text will always be slower. So always compute a second time!
Furthermore there is something called branchless programming. Don’t use if and else if not really necessary.
private void RunScript(List<System.Object> x, ref object A)
{
var comp = GrasshopperDocument.Objects.First(c => c.NickName == "Random X1") as GH_Component;
var data = ((GH_Structure<IGH_Goo>) comp.Params.Output[0].VolatileData)[0];
var valTrue = new GH_Boolean(true);
var valFalse = new GH_Boolean(false);
var list = new List<GH_Boolean>(data.Count);
var cnt = data.Count;
for(var i = 0;i < cnt;i++)
{
var num = ((GH_Number) data[i]).Value;
if(num < 10.0)
{
list.Add(valTrue);
}else{
list.Add(valFalse);
}
}
A = list;
}
This is fantastic! There is obviously a lot of understanding here that I didn’t get from doing C# courses. Could you give a novice some pointers about how he might go about learning to code like that?
When I really want to squeeze everything out of the code then I tend to embed lists in objects (classes defined in a dll) shared by both ends. Then the profiler time goes down to less than 1 ms (= time not even visible):
My computer is quite slow, but
the uppermost component is a for-loop, identical to @gankeyu’s, and
the lowest component is the “linq-version”, also by @gankeyu.
Both of these show no time (< 1 ms) if not sending the list to the output. So the very output assignment can be optimized. And so I did.
The middle component sends its list (209304 booleans) but it takes less than 1 ms (no profiler time visible, see blue markers)
The list is simply embedded in an object only containing (“embedding”) the list, that’s all. So I pack it and unpack the list. that’s all (see code in text panels).
The dll content of the referenced RILGH_Marshal.dll looks like so:
Huge lists can be passed between components in no time. Passing lists is expensive. Very expensive.
FEATURE REQUEST
For debugging reasons I would of course also have an output with “native” lists/types but for this I would like the outputs to simply skip assigning the values to them if no wire is connected to the output!
(I use to code such “output awareness” myself, but it really should be inbuilt in the component ports and updated on connect/disconnect, now I can detect only after refreshing the GH definition = bad ).
that LINQ expression was part of my solution, but anyways. I wasn’t even happy with that solution, because it breaks too much of the flow of Grasshopper and is barely readable.
If you come up with more than 5 lines for a statement which should have been 1 line, then I’m not so sure if this is already a bad tradeoff. Especially if we speak about 50 ms on lower performing laptops…
In reality I would also question the need for a dataset of 200k+ items .
In the end, all that could be required, but my point here would then be, that Grasshopper itself is the bottleneck and should be optimized out. If you think of, and understand these types of optimizations, I’m pretty sure you can write large parts of that algorithm into one single library and achieve even higher performance and better readability!
Coming back to the initial question, the reason why simple if-statements are that costly in Grasshopper is the data-piping of Grasshopper. This was my initial intention to show here. In practice I was never in need to apply this knowledge within a definition.
Ops, sorry for that! I just looked at that one Gh definition below and thought the one above was from the same source. Sloppy of me,
Agreed.
Absolutely.
However, there’s also the benefit in building solutions step for step (component for component) and do experiments with the part-results (between components), for which Grasshopper is a perfect (lab deck) tool. In my case I often transform very heavy meshes, meaning I often have hundreds of thousands of elements (vertices and/or faces) to shuffle through different processes (which are best achieved with fully coded algorithms), but debugging and experimenting with the part-results is an essential part of the development workflow spotting “corner cases” on complex meshes. So speed can matter also during the development stage, and some process steps are generic and can be reused etc.
In any case, I learn so much by studying coding strategies and different approaches. This forum really is something special! Thanks to all of you!
I heard David Rutten mention at RhinoWorld that GH2 will be able to use large lists of value types (int, bool, double, etc) directly without being wrapped into GH_Boolean etc, avoiding a lot of the overhead. The overhead will be created if you attach metadata to each item in those lists though.