Scripting components in Grasshopper can be a great way to get extra power and functionality for those comfortable with C# or VB. It can also be a much tidier way of doing certain tasks than using components, such as using mathematical formulae on a list of values.
Calculating the minimum value in a list of numbers is a great example of this – difficult to do using Grasshopper but dead easy with a few lines of script. The algorithm is simple – look through every item in the list, keeping track of the smallest item found. When we are at the end of the list, simply return this smallest value.
To implement this in Grasshopper, we can use the C# component. We’d start by specifying the input data types by right-clicking on the input, selecting ‘list’ and selecting ‘double’.
Then we’d go into the component and add our code.
private void RunScript(List<double> x, ref object A) { double minsofar = x[0]; foreach(double val in x) { minsofar = Math.Min(val, minsofar); } A = minsofar; }
This works fine and gives the expected result.
But try and run this code for quite a large list, say 100000 items. This takes around a second to compute. Not an especially long time, but for such a simple calculation it still adds a noticeable lag to the canvas. I just accepted it for what it was, thinking maybe that’s simply how long it actually takes to run a bit of code.
One day, I was writing another simple component and was feeling especially lazy. I couldn’t be bothered to do the right-clicking malarkey, and decided to just cast the data types in the Grasshopper component itself. Again, there was a large list going into the component, but to my surprise it calculated in mere milliseconds. Investigating further, I found that this speed difference occurs time and time again, and can be easily demonstrated:
Here, we have a list of 100000 numbers (a series of doubles) and we’re trying to find the smallest value among them. (Of course, with an ascending series it’s going to be the first value, but the algorithm doesn’t know that.) The code for the first C# component is:
double minsofar = x[0]; foreach(double val in x) { minsofar = Math.Min(val, minsofar); } A = minsofar;
This is the same as before – we have set the input data type (called x) as double, and assumed that x is a double in the code. This consistently takes 1.2s to calculate on my laptop.
The code for the second C# component is:
double minsofar = (double) x[0]; foreach(object val in x) { minsofar = Math.Min((double) val, minsofar); } A = minsofar;
The only difference here is that we don’t tell the component input to expect a particular data type – we leave it set as ‘object’. (We still have to tell it it’s going to be a list.) Then, in the code, whenever we want to do something with this list that can only be applied as numbers, we cast the item in the list we are looking at as a double, e.g. x[0] becomes (double)x[0].
The difference this makes is astonishing – we go from a calculation time of 1.2s to a tiny 7 milliseconds.
Why this is I don’t know. Whether it works for other data types I am still investigating, and it would be great to hear if this technique is repeated elsewhere in different scenarios.
In the meantime, the lesson is clear: if you are looking to optimise your canvas as much as possible, it is worth trying to cast your variables inside your component instead.
Download the example file:
Interesting findings! Did you ever figure out a reason or if there were other cases where this trick works? Have you asked David Rutten about it on the GH boards?
Hi Nate, there was a separate discussion on this on the Grasshopper forum a while back that answers your question, but I can’t actually find it right now.
If I remember, the gist is that, when you set a data type for an input, lots of magic is happening in the background that allows data types to be cast from one type to another. It is this mechanism that, for example, allows you to feed a curve directly into a mesh, resulting in a patch mesh within that curve. These extra checks take time.
The rationale behind this is that it is better for Grasshopper to be intuitive and easy to use rather than just be fast. The method presented in this post in a sense is a lot less robust than the native method in Grasshopper. What we are essentially doing when using the Object type is dispensing with all of the type conversion code baked into Grasshopper, and saying that we will handle the type conversion ourselves. Needless to say, this is for more advanced users only. But this is one of the things that I do like in Grasshopper – that steps have been taken to make it so accessible for beginners, but more advanced users are able to dig in and tailor it to their needs.