Common C# component/plug-in programming practice tips?

I’ve created a fairly extensive C# component (from my perspective at least, +600 lines of code), which I want to turn into a plug-in at a later stage. The component is set up to create a list of a couple of 100K text elements, basically a text file.

What is some common practice advice in C# programming (for GH in particular) that will automatically make the script perform better?

For example, I use a lot of for loops on lists to do an operation on each element. Is that considered efficient or the opposite?

Another example, embedding if statements deep down into a number of for loops instead of on a higher level. If you can limit the number of calculations by duplicating code and first run the if statement for one condition and then for the other, will that be more efficient? Less calculations = faster resulting script? What is the best way to skip parts of code and decrease the amount of calculations that are useless otherwise? (Such as multiplying by a variable that is set to 0)

I’ve read somewhere that for loops over Arrays are far more efficient than over List? I don’t know the programming behind list operations with standard GH components, so I wonder if there is another way to loop through a list of objects? I’ve created a List which I continuously fill with string elements with the .Add() method. I then write the entire list to the output at the end of the script.

Is using global variables considered good or bad? What about creating custom functions as much as possible (didn’t do that at this point)?

Also, when developing the plug-in at some point, is it wise to create custom classes and use them to my advantage, if suitable? Is that more efficient or does the performance not increase?

Lastly, what is more efficient: just 1 all-in-one component that takes a lot of inputs and then gives you the output OR an assembly component that takes a couple of other input components (a more visual scripting, connecting components approach).

I have a background in structural engineering, so my programming knowledge is more intuitive than computationally efficient. So until now, if it did what it should had done, it worked for me.

Some general tips are welcome at this point. I can still rewrite the code at this stage, if required for huge performance improvements.

I’m not able to share the code unfortunately, as it is part of an ongoing research project.

1 Like

Are you aware of the C# plugin development course of Long Nguyen?

I think it covers all major aspects of plugin development of C# including good practices and workflows. I guess you can also just watch Part 1 - 3 to get to know the major concepts.

If you develop your code in Visual Studio and compile the code, then I guess it will run faster compared to running it in a custom c# component (not precompiled).

2 Likes

For loops will usually perform faster than Foreach loops.
If you have to do operation on each element of array/list there is no other way then loop…

Less calculations of the same “weight” will produce definitely faster code. But it is question which operation is faster, multiplying with 0, or checking if one value is 0 and skip multiplying. Generally you have to test it by yourself if “early exit condition”/“skip condition code” performs faster then code you want to skip (and can skip).

Code with Arrays can perform slightly faster, but if you perform some complex calculations inside the (for) loop, then the performance of array versus list may be so marginally small, that eventually, it doesn’t matter.

Use global variable only if have to, otherwise use local. Make custom functions for more complex code, for few lines that will not be change often and its functionality is unique for that location keep it inside “main” code.

Create your own class if you have to => if you have more additional data/functionality that you need to “link” to existing class then do it, it will help you to manage the code later. Performance usually will not increase (it will probably decrease).

Probably 1 All-in-one since there will be no copying and casting data from one component to the other.

An easy rule is to only care about performance if you encounter performance issues!

You should never create software the other way around. Keep it maintainable, well documented, easy to follow. Use source control, track issues and bugs. Think about a software architecture before coding.
Follow and understand the SOLID principles. Read about patterns and choose one . But, on the other hand, only stick to a pattern if it fits in. Simplicity is the key, Over-Engineering is a common problem.
If you like the global approach have a look at the Singelton or Service Locator pattern. Its an easy way in structuring a solution. Write (Unit)tests and never stop learning… :slightly_smiling_face:

2 Likes

IF you use recursion - either nested or flat - with a certain amount of vars, then use as many global vars as possible (avoid the locomotive effect in Method calls etc etc).

IF you use clustering of some sort - either nested of flat - see above.

Other than that:

  1. Always have the forest in mind (the tree is a nothing thing). If, say, you are after solid ops and you use a surface modeller (like Rhino) then trying to write speedy code is kinda trying to tune a Harley Davidson.
  2. Always use the slowest/less capable computer to test your stuff (unless you want to calculate the mass of some black hole).
  3. If the result requires a year and a half to compute … either blame yourself (99%) or the task that you are after is not worth solving (leave others to cut the mustard, life’s short).
  4. Fully get the gist of // in coding: is not the Nirvana red pill (or the blue one). // is kinda a double forward in windsurfing: good but only if the conditions are right and if there’s some girls around to admire the brave.
  5. Locate the Methods that you expect/suspect to be the most expensive ones (time wise) and mastermind a Plan B, C, … if tests solely related with these are pathetic.

Example: assume that you want to do some Dijkstra thing in some graph. But … prior anything you should check islands. Now … if you do that using the VV connectivity tree (ops with integers) is fast like a Ducati. If you do it with any other way is slower than any Toyota.

Don’t optimise your code without first profiling it. You can use System.Diagnostics.Stopwatch to accurately measure time spent in various portions of your script. Although the shorter the time, the less accurate (relatively) the measurement will be.

One usual optimisation strategy for when you have nested loops is to sort (some of) the data. That may allow you to go from an O(n) loop to an O(log n) one because you can perform a binary search on sorted data.

Finding keys in dictionaries is faster than finding them in unsorted data. Finding collisions using HashSets is faster still.

But first; profile, profile, profile.

And second; don’t optimise inside a C# script component. The component compiles the code with the debug flavour. Perform your optimisation in VS using release buids.

3 Likes

More functions is almost always better. The compiler will inline them if it feels there’s a performance benefit to be had there.

These days I try to make my own data types immutable and all functions small and pure*.

I like the rest of your advice :slight_smile:

* “pure” means it has no side-effects and always returns the same result given the same arguments. It’s much, much easier to test correctness of pure functions, and to use them in multi-threaded environments.

1 Like

Thanks a lot!

I’ll take everthing you mentioned into consideration and will report back once I’m able to share more details or when I have additional questions regarding this specific topic.

After having participated in the recent C# level 3 scripting course of Long Nguyen, I learned a lot new thing.

I’m currently implementing and experimenting with custom data types and class libraries. Much cleaner code now.

Hopefully in a month or 2 this plug-in will see the light of day. :crossed_fingers:

1 Like

:+1: From what I’ve seen Long Nguyen does a great job in providing C# tutorials for GH Addin development. However since C# and .Net has a much bigger scope, it always make sense to move on with general software development. Especially because Rhino (and as every other piece of software) is kind of biased in its architecture and coding style.

At some point the language doesn’t even matter, since the concept of writing code, especially applying code patterns, is something which is universal. For 3rd party development all it matters is to understand how to make it run and what is stored where at the api. Since Rhino devs also stick to certain patterns and architectures, its quite easy to guess where something is stored within their library.
That essentially means, once you know these principles, its easy to work with almost any quality-3rd-party-code in almost any language.

1 Like