Would C++ be considerably faster than C# or Python for Rhino-plugins?
The task is to generate random-style geometry with LOTS of conditional looping.
The question is more about creating a whole design-project like in Grasshopper rather than about additional plugin-based commands.
My thought ist that by choosing the plugin-approach one would get access to C++ and it’s potential speed via Visual Studio…
But the Visual Studio debugger is one of the best around so errors can be found and fixed.
I have found the best approach is to develop everthing in Python first using Rhinocommon calls, and then push the performance critical parts into C++ code.
Python is the easiest language to develop in and can provide good performance when most of the time is spent in Rhinocommon functions. But best speed will almost always be achieved by using C++ with the Rhino C++ SDK.
But note that my biggest speedups have always come from architectural/algorithmic improvements. These can be pioneered in Python and then transfered to C++ code. For example, let’s say you want to find and remove duplicate faces in a mesh. You could do a brute force n^2 comparision of each face to the others. Or you could use an unordered multimap in C++ to find the duplicates. This will be orders of magnitude faster for large meshes. This is what Rhino does to find duplicate faces. But by really thinking about the problem, I found there is a 30x faster way to find the duplicates that builds on top of what an unordered multimap does: you build your own custom hash function for the XYZ of the face center to do the comparison and sort the mesh faces into geographically separate bins (effectively build a tree) which can be checked in parallel. With this approach, all the duplicate faces in a 100M faces mesh can be found in just 1.7 sec, faster than I ever thought possible.
I do agree that you should avoid C++ like the plague as a first development environment. Search for possible solutions using Python including careful thinking about algorithmic optimizations (search Google extensively for ideas). Then speed up the slow parts using C++. I have created a step-by-step example on calling C++ DLL’s from IronPython which is referenced in Dale’s recommendations for adding this functionality:
I would recommend C# to get started. In general we have better support for C# than other programming languages in Rhino. If you have some specific need for some highly optimized code, you can investigate calling down into C++ in a similar fashion as Terry suggests. Typically you don’t need to do this though and are better off analyzing your algorithms to see if they can be improved.
From my experience, VS’es debugging ability on C++ is limited, not to say some code will behave differently with -O3 and -ffast-math.
Mostly what I rely on is auto-vectorization (SIMD) of clang or ICC.
I’m not saying C++ is a plague to avoid. But coders should think carefully. For example, you could make use of map-like structure in the duplication removal task in any language. (The fastest algorithm is sorting points through BVH/BSP tree probably)
C# is like a cross of Python and C++; it provides more complex, object oriented structures and libraries like Python but it also allows fairly high performance for speed-critical portions of your code.
Its big downside is that it requires pre-declaring all variables and has more syntax requirements that Python. Nothing beats Python for the percentage of time spent working with code that executes vs percentage of time debugging. Nothing. At least this is my experience after 54 years of programming in over a dozen different languages.
Using map like structures in any language is not the fastest way to find duplicates. All these have significant overhead associated with hash creation and collision handling. Plus the hash function is non-optimum for sorting XYZ data.
Initially I thought using built-in structures would be fastest also. But then after deep contemplation of each aspect of this problem, I found improvements that lead to the 30X speedup.
Trying to use established tree libraries has the same problem. They are not optimized for the specific XYZ range of data in the data sets I am handling and have their own overhead.
What I did was to essentially smash the map and tree code down flat with my code so I could co-optimize all components. This takes considerable time and excessive patience but with the excellent debugging capabilities of Microsoft Visual Studio, it was achievable. I believe my code is now pretty fast for this operation: 1.7 sec to find all duplicate faces in a 100M faces mesh on a 16-core, 4.5 GHz computer with 3400 MHz DDR4 128 GB and 7000 MHz 2-TB M.2 SSD.
It would be interesting to try autovectorization (SIMD) to see if further improvement is possible.
premature optimization is an antipattern and evil. Write your code in the language you like, as clean and as encapsulated as possible, and then identify the bottlenecks by measuring performance.
The answer is not trivial. Of course, C and C++ are better performing in average, but this is pointless if the code you wrote has no performance issue, but instead the library code of Rhino. Apart from that, performance optimizations like SIMD or “branchless programming” are also applied by a good compiler for certain situations without any need to explicitly code them.
A modern compiler does more things for you than you would expect. And as a bad programmer, you can create slow code in any language. This is why I would always tend to say, take the language best-suited.
And I can only agree that writing plugins for the Rhino ecosystem is best written in C#.
You can also call C/C++ code in C#, but the overhead of marshalling, often defeats the purpose of writing it in two languages. The same is true for CPython and IronPython.
If you really want to improve performance, learn more about algorithms, algorithmic complexity and performance profiling and don’t expect to write fast code, just because it is in C++.
Thank you all for your proposals, they match with the evaluations of coders I had already been discussing the issue.
The graphic in my post is basically about randomly slicing solids within a while-loop. And it seemed, in both C# and Python, it was that slicing which made the code slow. Generally speaking I had the impression, that creating new geometry from scratch is fast, retrieving information (like XYZ coordinates) from geometry is already slower and that anything concerning the interaction between different geometrical objects by slicing or boolean operations is considerably slower.
From your experience would you say that this observation is correct? And if so, why is that?
In the end the core question is wether (multiple) interactions between objects would be faster in C++?
Again, C and unmanaged C++ are usually faster than C# and Iron-Python. This is simply due to the fact that C# and IronPython code is compiled into “Intermediate Language” before being executed. This makes it slightly slower by nature. However, this extra layer is so optimized, that for the majority of use-cases this is absolutely irrelevant.
I guess you are calling the library code if you speak of “slicing”. This is pointless. The library code will perform the same no matter which language you are using. The only part you can measure and optimize is your code in between. Accessing thousands of properties and fields is a matter of nanoseconds. So I don’t see a problem with that particular issue.
You can improve performance in many ways and measuring of performance is multi-dimensional. I personally wouldn’t even trust you that you (as a beginner) are capable of measuring performance correctly.
Really don’t bother too much about performance, become a better developer and then learn to “profile” and to improve performance in second step. As an experienced programmer, you can write code in any language.
IL (C#, IronPython, IronRuby, F#, VB .Net) compiled code can be decompiled, reversed and converted from one to another language. There are obfuscation mechanisms to counter this. But, with a bit of understanding of assembly language, you can also disassemble and reverse-engineer C and C++ code. This is more complicated, but not impossible. You can also counter disassembling. But are you really that rocket scientist with that super clever algorithm people are not supposed to see? If you are, then yes, writing in C/C++ gives you an extra layer of (false) security.
Based on my personal experience I would highly recommend using C# rather than Python for numerically heavy calculations. It is way faster. And by way faster I mean around 50 times faster which really is a big deal in many scenarios.
I am not an expert programmer and have very limited knowledge on both, not to mention C++, but this is what I have concluded. There’s a lot of great points being made in the thread about how the algorithms you use make a difference.
“Slicing” is an AutoCAD expression and the equivalent to Rhino’s “WireCut” command.
I used that term because I’ve been working with AutoCAD for more than 20 years…
Your guess is right: By using “WireCut” for splitting geometry I meant calling objects, methods etc. from some “Rhino library” (like from “rhinoscriptsyntax” in Rhino Python).
If I get your point, you mean that when accessing the content of some imported “Rhino library” within Visual Studio it doesen’t make much difference wether you do so from a C++ or a C# template, because performance doesn’t depend on the language but instead on the capabilities of that “same” Rhino library? So if library based solutions tend to be slow in some particular case, using C++ will not miraculously accelerate them?
I’ve observed the same with merely mathematical samples without any link to CAD libraries.
Anything was just derived from 2D-coordinates and then visualized within VS and C# was much faster than Python.
I doubt that IronPython is so much slower than C#, because IronPython is written in C# and translates into almost the same Opcode as a C#. IronPython is slower due to little more abstraction, but it shouldn’t be a large difference. Usually what you do to measure raw performance is the following:
Close as many processes on your PC, especially your Web-browsers.
Test as isolated as possible, this means do not test in Rhino/GH if possible
Wrap a function into a measuring function. (StopWatch/timeit)
Repeat measuring a couple of times, compute the average
Collect/Flush the Garbage Collector before and after using the function. You don’t want to measure the time the Garbage Collector requires cleaning up unused resources
Compile the code without debugging information and with optimizations (“Release”).
As a plus, measure how much memory is allocated. Allocation and de-allocation can also affect performance
As an alternative, all language have libraries doing this for you. E.g. Benchmark(.)Net