Which approach is most appropriate - writing python code

I’ve been writing a few python scripts now again but I’ve never questioned which package is most appropriate to use until I came across two different approaches to generate curves from text with a big difference in calculation time.

The first example with Rhino is very fast to compute - 45ms:

import Rhino as rc

preText = rc.RhinoDoc.ActiveDoc.Objects.AddText(text, location, text_height, font, True, False)

postText = rc.RhinoDoc.ActiveDoc.Objects.Find(preText)

textCrvs = postText.Geometry.Explode()

rc.RhinoDoc.ActiveDoc.Objects.Delete(postText, True)

crvs = textCrvs

However, the second example with rhinoscriptsyntax takes forever at 2.6s:

import rhinoscriptsyntax as rs

geoList = []

preText = rs.AddText(text, plane, height, font)
textGeo = rs.ExplodeText(preText, True)

for i in textGeo: geoList.append(rs.coercegeometry(i))

for i in textGeo: rs.DeleteObject(i)

Can someone clarify what’s going on? How does one decide to use Rhino vs Rhinoscriptsyntax?

text2curve.gh (6.9 KB)

Most time is actually spent redrawing. It takes time to redraw the document. Some time is spent also creating the Undo-Redo operations.

This script requires to add objects to the Rhino document, because there is no TextEntity in GhPython. This might be added in the future, especially once Grasshopper itself will support formatted text as geometry, but for now, this is what we have.

So I added:
sc.doc = Rhino.RhinoDoc.ActiveDoc to set up the target document and
sc.doc = ghdoc #required at the end to make up for the changed setup.

Also, I added:
rs.EnableRedraw(False) to remove redrawing and
rs.EnableRedraw(True) at the end to re-enable redraw.

With these changes, the script is still slower with RhinoScript, but by a smaller amount.

text2curve_fixed.gh (11.8 KB)

I also removed a few lines that are not required in the RhinoScript counterpart.
Overall, this particular sample is not so great in RhinoScript, because you always need to target the Rhino document and that is particularly slow. We added the ghdoc exactly to mitigate this slowdown.



Giulio Piacentino
for Robert McNeel & Associates

You don’t need rs.coercegeometry(), at least not when you run it in Rhino (not sure about GH)

Also, you don’t need the geoList.

Try this:

import rhinoscriptsyntax as rs

preText = rs.AddText(text, plane, height, font)
textGeo = rs.ExplodeText(preText, True)

rs.DeleteObjects(textGeo )

If you do need to make a new list, use list comprehension like so:

geoList = [ i for i in textGeo ]
1 Like

Thanks for the clarification Guilio, I have a few more questions.

rhinoscriptsyntax is still ~8x slower but if I understand correctly this is only becuase GH doesn’t contain a native Text type? Otherwise, for most other cases it should be just as fast?

sc.doc = ghdoc - why is this required?

Lastly, have I missed a document/article on best practices for python within GH?

No, they will generally be slightly slower to some extent. This becomes especially noticeable when making many computations, as demonstrated/discussed here:

There are some great bits here:

Unfortunately a lot of the nitty-gritty best practices are floating around this and the old Grasshopper forum. Specific to performance, there’s a bunch of nuggets to be found here (specifically how setting type-hints on inputs, and wrapping outputs can speed things up):

Also, always be profiling your code within the GHPython component, to get a more accurate diagnosis of where precisely your bottlenecks are. I tend to use simple classes such as this one for profiling:

import time

class Timer(object):
    """ A simple profiler """
    def start(self):

    	# Start the timer
        self.startTime = time.time()
    def stop(self):
        # Print and return elapsed time
        elapsedSeconds = time.time() - self.startTime
        elapsedMilliseconds = elapsedSeconds*1000
        print str(round(elapsedMilliseconds,3)) + " ms"
        return elapsedMilliseconds

For cases where you actually work in “high-level”, RhinoScriptSytanx is generally just as fast as RhinoCommon, because the long calculation happens anyways in C++ in the Rhino core… think for example of Boolean intersections, extruding things, moving large objects.

For math-like operations, probably this scale of performances will help you:

assembly < C/C++ < C#/Vb.Net < Python:RhinoCommon < Python:rhinoscriptsyntax

Somewhere from C#/Vb.Net to after IronPython:rhinoscriptsyntax, there’s programming in Grasshopper components, depending from what you are doing.

Of course there are exceptions, but this is more or less the state of things. The closer you are to the hardware, the more you can squeeze… but is it really worth it? It will also take much longer to write the same things.

Also, often the real bottleneck is not the performance on the language level, but the right choice of algorithm. An O^2 algorithm will take around 1’0000 times longer to compute for just 32 times the size of input, etc.

The suggestion is to always program where you are most comfortable. We support all these environments so that you can find the right place for you.