How to speed up ghpython codes?

I wrote more than 200 lines of Ghpython codes,including one main program and 7 functions constructed by dictionary data structure.I have imported several necessary ghpython function pakages ,such as scriptcontext,rhinoscriptsyntax,ghpythonlib.components,Rhino.Geometry and so on.But it spent about ten seconds every time in finishing all.I want it done less than one second.GH_CPython has been unsuccessfully used to solve the problem because it can not import ghpython pakages metioned above.And I have tried to import numba to sort it out via installing Anacond3 which also confused me,failling at end.I use rhino6 and the grasshopper version is 1.0.0007.So how can I do to speed up my codes?
Please help me. Thank you all.

I‘ll try it with some chinese fortune cookie wisdoms:

The better the question the higher the chance to speed up your definition.

The slowest part of your computer is yourself.

Don‘t blame a Rhino if you don‘t understand the Python :wink:

“Make it run, make it right, make it fast”
That’s a good first step : if your program runs correctly as it is then it is time to start thinking about speed. Here are some tips on improving performance in Python:

If you can identify the parts of your program that are taking a long time then you can see where to concentrate on improvements.
Dictionaries can be very quick if used in the right way. In particular it may be quicker to use the iteritems(), iterkeys(), itervalues() methods rather than their list based equivalents. Checking presence of a key in a dict (mykey in mydict) is much quicker than checking presence in a list (mykey in mydict.keys())

Thanks for your reply.I will describle the problem more details.
I successfully intalled at "E:\ProgramData\Anaconda3\Lib\site-packages\blaze\compute"via Anaconda3 and copyed it into “C:\Program Files\Rhino 6\Plug-ins\IronPython\Lib” .But when I imported numba in ghpython interface,it turned out “Runtime error(ImportException):No module named numpy”.I guess ghpython needs something else to recongnize it.Thank you all the same.

Hi Dancergraham,
Thank you for your suggestions.I used dictionary to store the data like {0:PolylineA,1:PolylineB,2:PolylineC,3:PolylineD} and used the structure “for iValue in Dict.values():” to traverse the values.I will check again and optimize them more careful.
Best regards,
Yuxiang Ni

Yes you cannot install numpy in ironpython as far as I am aware. You could run a Cpython programme as a separate process with the subprocess module but the interface is a little tricky and you lose some time with startup.

And do you need to traverse all the values (e.g. to operate on all of them) or can you break out of the loop once you find a particular value (e.g. once you find the one you are looking for?)
In the latter case, using for iValue in Dict.itervalues() and then using break once you’ve found your object will save you time. itervalues() saves you memory as well relative to .values()

Hi Dancergraham,
Thank you so much.I have once intalled Cpython to speed up,but it could not import ghpython packages.The most effect method I want is to import some functions like numba.jit to make codes quicker.Checking the program,I will change .values() into .itervalues() as you recommend and break out of the loop if necessary.
Much appreciated,
Yuxiang Ni

Rhino/Gh are using IronPython, because both are using ‘Rhinocommon’ a C# library (which again accesses C++ libraries). IronPython is basically with Python Syntax, useful if you deal with libs.
Numpy is not supported for IronPython. You’ll need to use an equivalent library like “Math.Net”.
That also means CPython component does not support calling Rhinocommon and as such cannot access its functionality.
Anyway I doubt its your calculation which takes 10 minutes to perform. If you post code, we can have a look at it. But without any clue about the problem, its impossible to give you advice about speed improvements. Currently it seems you are rather looking for micro-optimization which isn’t helpful. Often problems are on completely different spots.

Hi Tom,
Thanks for your detailed reply from my bottom of my heart.Your answers make me understand much more

about Ghpython as well as CPython,and your guess is right.It’s very nice for you,but the calculating time is about 10 seconds not 10 minutes.Thread package may help to save the time as far as I know.My program not only needs the ghpython codes but also the rhino3D model to accomplish the perform.I will be very appreciated if you may give me your email.My codes is aimed to slove position between plans.Focusing on centerpoints of plans,I carefully study the relationship and find that the position between one and the other is an approximate polyline which I name as “Trajectory”.So the program is to construct the trajectories and
unionboolean them together,after which find one point on the unioned trajectory as a target centerpoint and move the next plan to it.Creat such a loop until the plans run out or cannot find a point on the unioned polyline.
Here are my main lines and the steps.Dict_UnionTraCursSta is a dictionary to store the trajectories between each other.I have defined several functions myself like Func_JudgeLength,Func_TraCur,SouthLineSelf,SouthLineWorld,Func_TraUniBooPts,Func_ConfirmPlan,whose respective functions are like their names.
Best wishes,
Yuxiang Ni

The input parameters are:
List Access:Angle_input,Pt_inputidex,Curve_RBHigh,Curve_RBMid,Curve_RBLow,Plan_High,Plan_Mid,Plan_Low,List_PlanExistence;
Item Access:Num_DivUniTra
The outcome parameters are:

Dict_PlanStandby,Dict_PlanExistence,Dict_RB,Dict_Plan,Dict_PlanData,Dict_UnionTraCursSta,Dict_PlanDataArea,Dict_PtCenOri = {},{},{},{},{},{},{},{}#Dict_Plan = {0;[HighArea1,HighArea2],1:[Midarea1,MidArea2],2:[LowArea1,LowArea2]}
for iHigh in xrange(len(Curve_RBHigh)):
for iMid in xrange(len(Curve_RBMid)):
for iLow in xrange(len(Curve_RBLow)):
Dict_SunDis = {0:[45,45,45],1:[35,25,25],2:[20,15,10]}#0-High,1-Mid,2-Low,"0:[30,30,30]"means"High:[High,Mid,Low]"
Dict_HorDis= {0:[13,13,13],1:[13,8,8],2:[13,6,6]}#Num_column:[Raw_0,Raw_1,Raw_2]
for m in xrange(len(List_Standby)):
    Dict_PlanStandby[m] = List_Standby[m]
for n in xrange(len(List_PlanExistence)):
    Dict_PlanExistence[n] = List_PlanExistence[n]
for jHigh in xrange(len(Plan_High)):
    Area_PlanHigh = int(rs.CurveArea(Plan_High[jHigh])[0])
    Dict_Plan.setdefault(0,{})[jHigh] = Area_PlanHigh
    Dict_PlanDataArea[jHigh] = Area_PlanHigh#Dict_PlanDataArea is used to judge which the Plan_Input is
    Dict_PlanData[jHigh] = Plan_High[jHigh]#Dict_PlanData is used to prepare for the Dict_UnionTraCursSta
for jMid in xrange(len(Plan_Mid)):
    Area_PlanMid = int(rs.CurveArea(Plan_Mid[jMid])[0])
    Dict_Plan.setdefault(1,{})[jMid] = Area_PlanMid
    Dict_PlanDataArea[len(Plan_High)+jMid] = Area_PlanMid
    Dict_PlanData[len(Plan_High)+jMid] = Plan_Mid[jMid]
for jLow in xrange(len(Plan_Low)):
    Area_PlanLow = int(rs.CurveArea(Plan_Low[jLow])[0])
    Dict_Plan.setdefault(2,{})[jLow] = Area_PlanLow#Dict_Plan is used to judge the High/Mid/Low of RB and so on
    Dict_PlanDataArea[len(Plan_High)+len(Plan_Mid)+jLow] = Area_PlanLow#Dict_PlanDataArea is used to find the key via area
    Dict_PlanData[len(Plan_High)+len(Plan_Mid)+jLow] = Plan_Low[jLow]#Dict_PlanData is used to construct n*n trajectories
#8.2.Construct the n*n trajectories named as Dict_UnionTraCursSta
for iValue in xrange(len(Dict_PlanData)):#As the Plan_Base
    Dict_PtCenOri[iValue] = rs.CurveAreaCentroid(Dict_PlanData.values()[iValue])[0]
    for jValue in xrange(len(Dict_PlanData)):#As the Plan_Input
        Length_ijSta = Func_JudgeLength(Dict_RB,Dict_SunDis,Dict_HorDis,Dict_Plan,Dict_PlanData.values()[iValue],Dict_PlanData.values()[jValue])#Length_ijSta[3] is the RedBoundary
        Tra_CurSingleSta = Func_TraCur(Dict_PlanData.values()[iValue],Dict_PlanData.values()[jValue],0,Length_ijSta[0],Length_ijSta[1],Length_ijSta[2],Length_ijSta[3])
#10.Main program:Fistly,find the correct trajectory via Key_Input and Key_Base;Secondly,move the Tra to the Plan_Base one by one;Thirdly,union all of the Tras
#Fourthly,judge whether the Plan_input is overflow based on the pts on UnionTraCurs
Dict_KeyExi,Dict_KeySta,Dict_UpdateTemp = {},{},{}
for i in xrange(len(Dict_PlanStandby)):
    Dict_UnionTraCurs = {}#Store the trajectory
    Area_PlanSta = int(rs.CurveArea(Dict_PlanStandby.values()[i])[0])
    Key_Input = {vSta:kSta for kSta,vSta in copy.deepcopy(Dict_PlanDataArea).iteritems()}[Area_PlanSta]
    Pt_CenterSta = rs.CurveAreaCentroid(Dict_PlanStandby.values()[i])[0]#Pt_CenterSta help move Plan_Standby to move to Pts[Pt_inputidex]
    for j in xrange(len(Dict_PlanExistence)):
        Area_PlanExi = int(rs.CurveArea(Dict_PlanExistence.values()[j])[0])
        Pt_jCenterBase = rs.CurveAreaCentroid(Dict_PlanExistence.values()[j])[0]
        Key_Base =  {vExi:kExi for kExi,vExi in copy.deepcopy(Dict_PlanDataArea).iteritems()}[Area_PlanExi]
        Cur_TraOri = Dict_UnionTraCursSta[Key_Base][Key_Input]#Fistly,find the Trajectory between NO.i and NO.j
        Line_rot0 = SouthLineSelf(Dict_PlanExistence.values()[j])#Calculate the angle of the Plan_base to rotate the Dict_Trajectory
        Line_rotbase0 = SouthLineWorld(Dict_PlanExistence.values()[j]) 
        Angle_rot0 = rs.Angle2(Line_rot0,Line_rotbase0)[0]
        if ghcomp.CurveClosestPoint(rs.CurveEndPoint(Line_rot0),Line_rotbase0)[2] < ghcomp.CurveClosestPoint(rs.CurveStartPoint(Line_rot0),Line_rotbase0)[2]:
            Angle_rot0 = Angle_rot0*(-1)
        Cur_TraTemp = rs.CopyObject(Cur_TraOri,rs.VectorCreate(Pt_jCenterBase,Dict_PtCenOri.values()[Key_Base]))
        Tra_SingleFin = rs.RotateObject(Cur_TraTemp,Dict_PtCenOri.values()[Key_Base],Angle_rot0)#Secondly,copy Cur_TraOri and move it to the Pt(named as Dict_PtCenOri.itervalues()[Key_Base])
        Dict_UnionTraCurs[j] = Tra_SingleFin
    if len(Dict_PlanExistence) == 1:
        UnionTraCurs = Dict_UnionTraCurs.values()
    if len(Dict_PlanExistence) > 1:
        UnionTraCurs = rs.CurveBooleanUnion(Dict_UnionTraCurs.values())#Thirdly,union all of the Tras
    for key,value in Dict_Plan.iteritems():
        if int(rs.CurveArea(Dict_PlanStandby.values()[i])[0]) in value.itervalues():
            Curve_RedBoundaryFin = Dict_RB[key].values()
    Dict_PtsFinal = Func_TraUniBooPts(UnionTraCurs,Num_DivUniTra,Dict_PlanStandby.values()[i],Curve_RedBoundaryFin)#Func_TraUniBooPts return a dictionary
    if Dict_PtsFinal:#Fourthly,judge whether the Plan_input is overflow
        Plan_FinalInput = Func_ConfirmPlan(Dict_PlanStandby.values()[i],Angle_input[i],Dict_PtsFinal.values(),Pt_inputidex[i])
        Dict_UpdateTemp[1000+i] = Plan_FinalInput#Just be different
if len(List_PlanExistence)+len(List_Standby) == len(Dict_PlanExistence):
    a = True
else:a = False
b = Dict_PlanExistence.values()

You are very frequently using a range constructor where the following method may be quicker, clearer and more ‘pythonic’, for instance in your first loop :

for m, standby in enumerate(List_Standby):
    Dict_PlanStandby[m] = standby

This avoids repeatedly looking up the same values, particularly in the longer loops or those which run many times

Also I may be missing something but couldn’t you simplify Dict_PlanExistence.values()[j] to Dict_PlanExistence[j] ?
This is much quicker as it is a hash table lookup whereas your method involves creating a list on each loop. I imagine this runs a lot of times as it is in your nested i, j loop.

Many thanks for your kind reply.I will carefully optimize the codes as your suggestions and study more about python in depth.The .itervalues() you recommend do speed up successfully.Sincerely thanks again.
Best regards,
Yuxiang Ni

1 Like

This thread on the old forum also has some (perhaps unexpected) tips on how to speed up GHPython components. Mainly related to typehints and inputting/outputting large lists, not so much the actual Python code itself:

Thanks for your reply.I will study them carefully.
Best wishes,
Yuxiang Ni