Grasshopper

algorithmic modeling for Rhino

Best way to run function many times (and output multiple data types)

The file I'm working with is really messy, so I don't think it will help to post it here. Instead I'll do my best to describe my issue (hopefully it wont be too terrible).

Ok, so I have been slowly assimilating components from a large definition into a single custom scripted component (eventually to be made into a GHA).

As of now I have 6 identical components in a chain (one after the other) which have several inputs and outputs. Some inputs are from the same user-defined sources and some are outputs from the previous component in the chain. My goal was to combine them all into a single scripted component - which I've done - but the amount of time it takes to process the solution is huge (compared to having them all chained in a row).

When chained, each component only take 50 or 60 ms to compute (300-360 ms total). When all crammed into one component, it can take several seconds (this also depends on the amount of inputs, though). 

My theory is that I've just done something less efficient / more stupid than the best scripting practices would call for (since it's a relatively new realm for me) - So this is what I ask: Is there a better way to script what I have scripted? I don't want to complicate things by posting my few hundred lines of code, so I'll simplify:

To combine these previously disparate scripted components, I:

Create a class with various properties: (I am using a class because I have several data types -planes, points, numbers- that I want to output as the result of a function)

Class exampleClass

public aProp

public bProp

public cProp

etc...

create a function with 6 inputs (which is basically exactly the same as a single scripted component):

exampleFunction(a,b,c,d,e,f)

inside the function solve for 6 variables based on inputs

aVar = some math

bVar = some more math

cVar = even more math

etc...

At the end of the function create an instance of exampleClass (and set properties equal to variables solved for in the function)

classInstance = exampleClass

classInstance.aProp = aVar

classInstance.bProp = bVar

classInstance.bProp = bVar

etc...

Finally set the return value of the function equal to the instance of the class

exampleFunction = classInstance

So that's the setup. Now I want to run the function 6 times so I do something like:

dim funtion01 = exampleFunction(a0,b0,c0,d0,e0,f0)

dim funtion02 = exampleFunction(a1,b1,c1,d1,e1,f1)

dim funtion03 = exampleFunction(a2,b2,c2,d2,e2,f2)

dim funtion04 = exampleFunction(a3,b3,c3,d3,e3,f3)

dim funtion05 = exampleFunction(a4,b4,c4,d4,e4,f4)

dim funtion06 = exampleFunction(a5,b5,c5,d5,e5,f5)

So that's it. Now I can access any of the results from any of the 6 functions. 

This works exactly the way I'd like it to, but it takes many times longer to calculate than when this whole script is broken up into separate components (360 ms total vs. 3.4 seconds in one script).

Any ideas why this may be? Should I do something different to achieve the same desired result?

Thanks in advance, I hope that wasn't overly confusing.

-Brian Harms

Views: 3006

Replies to This Discussion

Hi Brian,

a sharp increase in computation time like this is indeed pretty suspect. The most important thing I've learned with regards to optimization is that thinking about it won't help. There is one thing you must always do if you're seeking to optimize code, and that is profiling.

You should add some code that measures how long it takes to perform certain subtasks of your script. That way you can narrow it down to the offending lines (or sometimes line if you're lucky) within minutes. If you're stumped by the performance of some code in my experience it's never slow where you expect it to be slow. You can use the System.Diagnostics.StopWatch class for accurate profiling, it has better accuracy than DateTime.UTCNow:

Dim profiler As New Stopwatch()

profiler.Start()

'Do something here

profiler.Stop()

Rhino.RhinoApp.WriteLine("This code took {0}ms", profiler.ElapsedMilliseconds)

--

David Rutten

david@mcneel.com

Poprad, Slovakia

This is great. thanks, David, I'll give it a shot.

So there are a couple things I've run into. 

The First thing is that within the single scripted component (the one that defines a function which outputs an instance of a class and then calls the function 6 times) I can only profile each function call (not what happens inside each function). Maybe because I have the function defined in the <Custom additional code> area? Keep in mind this component takes over 6 seconds to solve (according to profiler widget).

So then to test what happens in one of these functions I take the contents of one function and have it loose in runscript. The methods in the script get the component's inputs directly and there are no more functions or user-defined classes being used. In this case the component only takes 60 ms. I'm confused!

The second thing is that many times the result of the stopwatch will be something like 5ms while the profiler in GH says the component took around 60 ms. I have 

Dim profiler As New Stopwatch()

profiler.Start()

at the beginning of runscript, and

profiler.Stop()

Rhino.RhinoApp.WriteLine("This code took {0}ms", profiler.ElapsedMilliseconds)

at the very end. So all of the user-defined stuff should be getting measured within this stopwatch.

-Brian

Maybe it's better to just show you. I've attached a file (disregard everything but the 2 scripted components on the right) that shows the performative difference between 2 custom scripted components that achieve the same exact thing but with different methods. The one that uses a function and returns a class instance takes 12x longer than the other. I'd like to use the function and class method so I can repeat the function multiple times in this component (right now it is only called once, just for simplicity's sake. But each additional time the function is called, the solve time increases - if 2 seconds at 1 function call, its 12 seconds at 6 function calls).

This means that unfortunately the stopwatch profiling hasn't been very helpful - because when I isolate the contents of the function to see what's slowing it down, the component only takes a tenth of a second! but when those contents are placed inside the function it goes back up to 1 or 1.2 seconds.

Hopefully someone has some insight. I'm sure there is something I'm missing or doing incorrectly.

thanks,

Brian

forgot the file. Should be used with a rhino file with units set to inches.

Attachments:

I'm getting NaNs out of the ArcSine component which is causing overflow errors in both scripts. Is that supposed to happen?

--

David Rutten

david@mcneel.com

Poprad, Slovakia

hmm, nope - not supposed to happen :)

The file is unit-sensitive (it actually is designed not to be - thats what I was trying to accomplish with the unit detection and object scaling. It used to work - haven't diagnosed the problem yet. 

Are you using a rhino file with units set to inches? (I forgot to mention it right when I posted, I edited the post a few minutes later - maybe it was after you had already seen it).

When I switch to inches it works.

--

David Rutten

david@mcneel.com

Poprad, Slovakia

Your functions and many of your variables are not strongly typed. This is throwing compiler warnings and causing a lot of variables to be treated as System.Object, which always slows things down as it requires boxing/unboxing and typechecking at runtime. Code like this:

Class jointClass
  Public angles
  Public rotPlns
  Public curPlns
  Public corIndx
  Public limIndx
  Public fProtect
End Class

should really have As Type clauses for all of those fields:

Class jointClass
  Public angles As Double
  Public rotPlns As Plane
  Public curPlns As Plane
  Public corIndx As Integer
  Public limIndx As Integer
  Public fProtect As Boolean
End Class

or whatever type they are supposed to be.

--

David Rutten

david@mcneel.com

Poprad, Slovakia

Ah ok that's helpful. If it is a list of planes should I write that here?

i.e.

 Public curPlns As list (of Plane)

?

And when I define the function, should I do this as well?

i.e. 

function (a as whatever, b as list (of whatever), etc...)

?

Yes, most definitely. Also define the return type of the function:

Public Function SomeName(ByVal arg0 As SomeType) As SomeOtherType

I suppose I should make the VB compiler treat these omissions as errors rather than warnings, there's no way C# will let you get away with this.

--

David Rutten

david@mcneel.com

Poprad, Slovakia

RSS

About

Translate

Search

Photos

  • Add Photos
  • View All

Videos

  • Add Videos
  • View All

© 2024   Created by Scott Davidson.   Powered by

Badges  |  Report an Issue  |  Terms of Service