GH: Compiling ScriptComponent doesn't call "BeforeRunScript()"

When compiling a ScriptComponent it doesn’t call “BeforeRunScript()” before calling RunScript. This keeps causing runtime errors in RunScript() after each code modification in cases where BeforeRunScript() is required to run before executing the main method RunScript(…).

This is really bad if the required settings done in “BeforeRunScript()” is disovered very late in the RunScript method, meaning, it takes long before it crashes, and only after first crash the component runs as expected (since it will then actually call the method “BeforeRunScript()”)

In order to get components to behave which has this requirement, I will always have to wait for the first crash, and then press F5 or implement a “Reset” method to get to the first “clean” run of the component.

private LetsSayANullableTypeOfSorts m_criticalSetting = null;

public override void BeforeRunScript()
{
    // This code will NOT be executed on compile, 
    // which leads to a runtime error. :( 
    m_criticalSetting = GetCriticalSetting();
}

private void RunScript(Inputs..., Outputs...)
{
    // lotsa code here
    // ...
    if (m_criticalSetting.IsOK)     <--- # KABOOM! #
    {
       // Bah! Never mind
    }
}

Q: Fixed in next service release? :relieved:

// Rolf

There’s a BeforeSolveInstance() method you can override in regular components, and call the BeforeRunScript() method there.

Huh? Now (again) I didn’t get it.

This problem is with the ScriptComponent, how can I solve it in a “regular component”?

OK, I’m slow today, but I really didn’t get it.

// Rolf

Maybe I’m confused then. You’re doing something where you want to copy paste from a script component into VS and have it work right?

Nah. Although I’m also dealing with the sync to VS (yes i am), but in this particular case I just want the ScriptComponent to not crash when I press the green compile arrow.

In VS the BeforeSolveInstance is doing its job, but OTOH, VS is not trying to run the Component after each Build… :wink:

// Rolf

Nope, even more confused now. Do you have a regular script component with an overridden BeforeRunScript() method and that method isn’t called during every solution before any calls to RunScript()?

Yes.

It is not called when I press compile (green arrow) although the RunScript method is called. This is why it crashes once. After the compile-press, then it always calls the BeforeRunScript as expected. Only not when pressing the compile button, then it seems to rush to call RunScript directly after compilation finishes (bypassing BeforeRunScript).

// Rolf

I cannot repeat that here, so I’ll need a script that does this.

This test always prints the Before and After messages to the Rhino command line. Do note that the code has to change before the Green Arrow button makes sense.

BeforeDuringAfter.gh (5.6 KB)

Yup, I just prepared a debug case for you.

To reproduce, just add an Output, which will cause the component to recompile (it goes bananas, due to not calling BeforeRunScript()). Then press F5 or Reset to let it have a second chance to behave. Poke on the R-slider to make it do its thing.

Point_Inclusion_Debug.gh (2.6 MB)

// Rolf

BTW, I notice that the filtering of the points takes about 5 ms while the entire solution spends the rest of some ~240 ms figuring out point id:s. According to the VS profiler it is the looking up of guids which wastes all this time. :frowning:

Is there any way one could avoid wasting so much valuable periods of grace only looking up silly guids?

// Rolf

I think the problem is that you’re invoking the constructor of the GH_StructureIterator with a null argument.

The Component field must be set to the actual component, because it is passed into the constructor and the iterator will throw if it’s given a null.

I guess it makes sense for the Component field to be assigned prior to the first ever call to BeforeRunScript, but I think at the moment it is assigned from SolveInstance() in the script component code.

I solved it by making the access instance property lazy.

Point_Inclusion_Debug.gh (2.6 MB)

You mean Rhino object guids or .NET type guids?

Hm, I’ll have to look it up in the profiler log. bbs

Edit:
I will have to read up on how to interpret this profiler, but this is the summary. Hm, it looks like it’s a .NET guid which is taxing here. Now I don’t know if this about elapsed time or accesses. I will have to read up on the profiler.

// Rolf

Traditionally, calls to System.RuntimeType.GUID occur frequently if there are many reflection / COM operations.

GH also uses a lot in GH_Convert class, when converting from a object to a RhinoCommon/GH datatype.

1 Like

OK, that explains my troubles. Good to know the reason at least.

One nasty solution would be to set the Component member in the very GetDataAccess/ConstructCurrentDataAccess method where it’s needed:

this.Component = (IGH_Component) owner; // +this
object _instance = m_da_constructor.Invoke(new object[] { Component });

As for the oddities you commented in the code it was leftovers from a much larger experimental code producing several variants of the filtering algorithm (and the use of the m_DA was supposed to break the code if BeforeRunScript wasn’t doing its thing).

And seriously: Thou Shalt Not Remove My Handcrafted Low-level Methods For Checking Distances. This is because Rhino .NET methods (like DistanceToSquared()) doesn’t run natively on the GPU, where it eventually may end up. :stuck_out_tongue:

I also noticed that you don’t seem to like my m_member naming convention. That’s OK. As for me I prefer the _var style for locals only. :sunglasses:

Anyway, thank you very (very) much for the debugging! Happy enough to find a workaround. Your support is super!

// Rolf

Hm. I’m not fully versed in the .NET boxing thing. Is that what is happening with the PointCloud items, like this: “point[i].Location”? However, a PointCloudItem is a class while the Location property returns a Struct, so it shouldn’t stem from that.

// Rolf

Well, PointCloudItem is a class so it’s not a value type.

  1. Every time you call a PointCount[i], a new PointCloudItem class is created
  2. PointCloudItem.Location involves many expensive calls (managed/unmanaged switching).

It is better to use PointCloud.GetPoints() first to retrieve a copy of points.

1 Like

I think you may be optimising the wrong thing here :slight_smile:

I had to rewrite and simplify the code so that I could wrap my own head around it. I know it’s not .NET standard but I prefer to prefix private and protected class level variables with an underscore. The m_ style has fallen out of favour in recent years it seems and I applaud that.

In this case yes. But that was clear only after profiling (80% of the original code stripped out where I also compared with the Rhino methods).

Anways, there are cases where the “handcrafted” (gpu) versions are doing a great job. For example when doing realtime collision tests between big meshes (50x50k vertices and the alike). In combination with RTree very “interesting speed” can be achieved.

It just so happened that I started testing the IGH_DataAccess thing on this experimental testing component.

// Rolf

Btw, I use C++AMP to code lightweight GPU programs