Community
Fusion API and Scripts
Got a new add-in to share? Need something specialized to be scripted? Ask questions or share what you’ve discovered with the community.
cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

Script performance degradation / Release Memory

6 REPLIES 6
Reply
Message 1 of 7
imajar
352 Views, 6 Replies

Script performance degradation / Release Memory

Good Morning!

 

I have a script that processes fairly large amounts of information and I am running into some memory / speed issues.

 

As a starting point, my script produces alot of temporary geometry and then deletes that temporary geometry once it is no longer needed.  In the sample script below, I create and delete a simple extrude 10,000 times, and it seems to exhibit the same behavior as my larger script. 

 

On Startup:  Fusion Private Memory= 1,455 MB   Speed= 80 create/delete cycles per second

  StartMemory.PNG

Script Finished:  Fusion Private Memory= 2,700 MB   Speed=2 create/delete cycles per second

FinalMemory.PNG

 

 

Is there any way I can get fusion to release the memory after I delete the object?  I assume the accumulating memory is what is slowing the script down?

On a similar note, is there a way to clear out transient objects?

 

Thanks in advance!

 

import adsk.core, adsk.fusion, traceback
def run(context):
    ui = None
    try:
        app = adsk.core.Application.get()
        ui = app.userInterface
        product = app.activeProduct
        design = adsk.fusion.Design.cast(product)
        rootComp = design.rootComponent
        extrudes = rootComp.features.extrudeFeatures
        sketches = rootComp.sketches   
        centerPoint = adsk.core.Point3D.create(0, 0, 0)   
        
        nIterations=10000
        progressdialog=ui.createProgressDialog()
        progressmessage="Extrude create/delete: %v of %m, %p% Complete"
        progressdialog.show("Progress Dialog", progressmessage,0,nIterations) #(title, message, minimumValue, maximumValue, delay)
        adsk.doEvents()    
        
        for i in range(nIterations):
            if (i%10)==0:
                if progressdialog.wasCancelled: 
                    break
                progressdialog.progressValue=i
            sketch = sketches.add(rootComp.xZConstructionPlane)        
            sketchCircles = sketch.sketchCurves.sketchCircles
            circle = sketchCircles.addByCenterRadius(centerPoint, 5.0)
            prof = sketch.profiles.item(0)
            distance = adsk.core.ValueInput.createByReal(5)
            extrude1 = extrudes.addSimple(prof, distance, adsk.fusion.FeatureOperations.NewBodyFeatureOperation)        
            # Get the extrusion body
            body1 = extrude1.bodies.item(0)
            sketch.deleteMe()
            body1.deleteMe()
    except:
        if ui:
            ui.messageBox('Failed:\n{}'.format(traceback.format_exc()))

Aaron Jarrett, PE
Inventor 2019 | i7-6700K 64GB NVidia M4000
LinkedIn

Life is Good.
6 REPLIES 6
Message 2 of 7
imajar
in reply to: imajar

Just wondering if anyone has any insight on this? 

 

I just wrote another script to delete bodies from a file based on volume (file contains 69,194 bodies).  Things start off nicely, deleting bodies at a nice fast rate, but slows to a painful crawl after the first few thousand delete operations.  If I stop the script, save the file and restart fusion, it runs fast again and slows down again after running for a while. . .

Capture.PNG

 

#Delete bodies in the root component based on Volume.
#Bodies with volume in cm^3 less than specified in variable "limit" will be deleted.
#31 July 2019 Aaron Jarrett

import adsk.core, adsk.fusion, adsk.cam, traceback

def run(context):
    ui = None
    try:
        app = adsk.core.Application.get()
        ui = app.userInterface
        product=app.activeProduct
        design=adsk.fusion.Design.cast(product)
        design.designType = adsk.fusion.DesignTypes.DirectDesignType
        rootComp=design.rootComponent
        failedOcc=rootComp.occurrences.addNewComponent(adsk.core.Matrix3D.create())
        failedOcc.component.name="FailedBodies"
        bRep=rootComp.bRepBodies
        nBodies=bRep.count

        progressdialog=ui.createProgressDialog()
        progressmessage="Collecting list of bodies to delete.  %v of %m, %p% Complete"
        progressdialog.show("Progress Dialog", progressmessage,0,nBodies) 
        adsk.doEvents()

        limit=600  #Remove part with volume less than this in cm^3
        toDelete=[]
        count=0
        for body in bRep:
            if (count %100):
                progressdialog.progressValue=count
            try:
                bodyVolume=body.volume
                if bodyVolume<limit and bodyVolume!=0:
                    toDelete.append(body)
            except:
                body.moveToComponent(failedOcc)
            count+=1
              
        progressdialog=ui.createProgressDialog()
        progressmessage="Deleting Bodies:  %v of %m, %p% Complete"
        progressdialog.show("Progress Dialog", progressmessage,0,nBodies) 
        adsk.doEvents()   
        count=0
        failurecount=0        
        nBodies=len(toDelete)
        for body in toDelete:
            if progressdialog.wasCancelled: 
                break
            if (count %100)==0:
                progressdialog.progressValue=count
            ifsuccess=body.deleteMe()
            if ifsuccess==False:
                failurecount+=1
            count+=1
        message="Complete!  Number of failed deletes: "+str(failurecount)
        progressdialog.hide
        ui.messageBox(message,"Summary")

    except:
        if ui:
            ui.messageBox((str(count)+'Failed:\n{}').format(traceback.format_exc()))

Aaron Jarrett, PE
Inventor 2019 | i7-6700K 64GB NVidia M4000
LinkedIn

Life is Good.
Message 3 of 7
MichaelT_123
in reply to: imajar

Hi Mr Ajarrett,

 

You are not alone!

It is very likely the structural issue in F360 code/architecture. The resolution of it might be difficult and easy at the same time. It would be like taking a spoonful of castor oil (Ricinus communis) .

 

https://forums.autodesk.com/t5/fusion-360-api-and-scripts/undo-via-api/m-p/8841548#M8000

 

Regards

MichaelT

MichaelT
Message 4 of 7
imajar
in reply to: MichaelT_123

Whew - not alone!  

 

I am intrigued by the undo redo history as being a possible culprit to my slowing script. 

 

I stumbled across this post which shows how to wrap a function into a single transaction.  I rewrote my little test script with this functionality. I am very happy to know of a way to wrap it up so that a single undo transaction is created!  But unfortunately, the performance degradation was still painfully present.  Not sure if this proves or disproves the undo stack as being the culprit?

 

Still Stumped. . .


Aaron Jarrett, PE
Inventor 2019 | i7-6700K 64GB NVidia M4000
LinkedIn

Life is Good.
Message 5 of 7
saurabh.singhKD42F
in reply to: imajar

Hi @imajar ,
You can try using TemporaryBRepManager. It significantly speeds up the process of creation of new BRepBodies.
You can use following script for reference.

import adsk.core, adsk.fusion, adsk.cam, traceback
from timeit import default_timer as timer
def run(context):
   ui = None
   try:
       start = timer()
       app = adsk.core.Application.get()
       ui  = app.userInterface
       app.activeProduct.designType = adsk.fusion.DesignTypes.DirectDesignType
       design = adsk.fusion.Design.cast(app.activeProduct)
       rootComp = design.rootComponent
       
       brep = adsk.fusion.TemporaryBRepManager.get()
       composite_body = None
       for x in range(20):
           for y in range(20):
               cylinder = brep.createCylinderOrCone(adsk.core.Point3D.create(x, y, 0),.2,adsk.core.Point3D.create(x, y, -1),.2)
               if composite_body is None:
                   composite_body = brep.copy(cylinder)
               else:
                   brep.booleanOperation(composite_body, brep.copy(cylinder),
                                       adsk.fusion.BooleanTypes.UnionBooleanType)
       composite_body = rootComp.bRepBodies.add(composite_body)
       sketch = rootComp.sketches.add(rootComp.xYConstructionPlane)
       sketch.include(composite_body)
       end = timer()
       ui.messageBox(str(end-start))
           
   except:
       if ui:
           ui.messageBox('Failed:\n{}'.format(traceback.format_exc()))
Message 6 of 7

Depending on our requirements,  we can also sometimes turn off the compute of the sketch(sketch.isComputeDeferred = True) initially, and later turn it ON again after all our construction is over, it also speeds up the process significantly, as computation of sketch profiles will take place altogether at last, rather than computing it everytime we create a new sketch profile.
For reference - 

import adsk.core, adsk.fusion, adsk.cam, traceback
from timeit import default_timer as timer
def run(context):
   ui = None
   try:
       start = timer()
       app = adsk.core.Application.get()
       ui  = app.userInterface
       design = adsk.fusion.Design.cast(app.activeProduct)
       rootComp = design.rootComponent
       sketches = rootComp.sketches
       plane = rootComp.xYConstructionPlane
       sketch = sketches.add(plane)
       
       sketch.isComputeDeferred = True
       
       circles = sketch.sketchCurves.sketchCircles
       for i in range(100) :
            circleA = circles.addByCenterRadius(adsk.core.Point3D.create(-10,0,10*i),5)
            circleB = circles.addByCenterRadius(adsk.core.Point3D.create(5,0,10*i),5)
            circleC = circles.addByCenterRadius(adsk.core.Point3D.create(20,0,10*i),5)


       sketch.isComputeDeferred = False

       end = timer()
       ui.messageBox(str(end-start))
   except:
       if ui:
           ui.messageBox('Failed:\n{}'.format(traceback.format_exc()))

 

Message 7 of 7
imajar
in reply to: saurabh.singhKD42F

Hi @saurabh.singhKD42F 

 

Thank you for the tips!  As it turns out, I already use the temporary BRepManager, and I love it!  But, it is also limited in functionality so there are many times that I cannot use it, such as when we create a geometry using sketches.  I wrote the sample script the way I did to represent a much larger script I had written where I could not use temporary b-rep geometry - therefore, the sample script was a simplified way to communicate a larger problem I was facing.

 

But I did not know about deferring sketch computation, that is good to know!  Thanks again!

 


Aaron Jarrett, PE
Inventor 2019 | i7-6700K 64GB NVidia M4000
LinkedIn

Life is Good.

Can't find what you're looking for? Ask the community or share your knowledge.

Post to forums  

Autodesk DevCon in Munich May 28-29th


Autodesk Design & Make Report