Setting isConstruction has poor performance

Setting isConstruction has poor performance

Anonymous
808 Views
7 Replies
Message 1 of 8

Setting isConstruction has poor performance

Anonymous
Not applicable

I've noticed that when i set isConstruction to either True or False (just once, not toggling it - using python) script execution time greatly increases. performance is poor for both isComputeDeferred True and False cases. The same behavior does NOT happen when calling isFixed, no noticeable performance impact occurs for isFixed.

 

I stumbled over this when I took isConstruction as an argument to a function I was writing which generated many lines (less than 50 - actually not that many) and was just always setting isConstruction.

0 Likes
809 Views
7 Replies
Replies (7)
Message 2 of 8

ekinsb
Alumni
Alumni

I wrote a little text script that draws 100 lines.  I wrote a separate function that does the line creation, to mimic what I believe you have, and called it 100 times.  I'm also drawing geometry that results in many profiles being created.  Here are some timing numbers I'm seeing.

 

  1. Draw the lines - 6.61 seconds
  2. Draw the lines and set the isConstruction for every other one - 3.046 seconds.
  3. Added isComputeDeferred to the line drawing function and setting isConstruction - 3.00 seconds
  4. Added isComputeDeferred to outside the function so it encompasses all lines being created and setting isConstruction - 1.035 seconds

Lines.png

 

As you can see, I'm not seeing a performance issue when setting to isConstruction and in fact it is faster.  I'm a little surprised that it's faster in the second case but thinking about it a bit more it not too surprising.  The most expensive thing in drawing a sketch is the profile calculation that happens as you're drawing geometry.  Setting the isComputeDeferred temporarily turns off the profile calculation.  If you use the isComputeDeferred within the create line function it doesn't really help because it still ends up computing profiles after each line is created.  When I set the property at the beginning, created all the lines and then turned it off, there is just a single profile compute and that's why number 4 is so much faster.  Setting a line to isConstruction also means that the line is ignored when calculating the profiles so the profile calculation step is easier.

 

It could be something with the particular geometry you're drawing and that it somehow has a different affect on the profile calculations. 

 

Here's my test program.

 

import adsk.core, adsk.fusion, traceback
import time


def run(context):
    ui = None
    try:
        app = adsk.core.Application.get()
        ui  = app.userInterface
        des = adsk.fusion.Design.cast(app.activeProduct)
        root = des.rootComponent
        
        sk = root.sketches.add(root.xYConstructionPlane)
        lines = sk.sketchCurves.sketchLines

        sk.isComputeDeferred = True        

        lastLine = drawLine(sk, lines, adsk.core.Point3D.create(-1,1,0), adsk.core.Point3D.create(30,1,0), False)
        lastLine = drawLine(sk, lines, lastLine.endSketchPoint, adsk.core.Point3D.create(30,9,0), False)
        lastLine = drawLine(sk, lines, lastLine.endSketchPoint, adsk.core.Point3D.create(-1,9,0), False)
        lastLine = drawLine(sk, lines, lastLine.endSketchPoint, adsk.core.Point3D.create(-1,1,0), False)

        t0 = time.time()
        lastLine = drawLine(sk, lines, adsk.core.Point3D.create(0,0,0), adsk.core.Point3D.create(0,10,0), True)
        x = 0.25
        y = 0
        for step in range(100):
            if step % 2 == 0:
                lastLine = drawLine(sk, lines, lastLine.endSketchPoint, adsk.core.Point3D.create(x,y,0), True)
            else:
                lastLine = drawLine(sk, lines, lastLine.endSketchPoint, adsk.core.Point3D.create(x,y,0), False)

            x += .25
            if y == 0:
                y = 10
            else:
                y = 0
        t1 = time.time()
        total = t1-t0
        sk.isComputeDeferred = False        

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


def drawLine(sk, lines, point1, point2, isConst):
    newLine = adsk.fusion.SketchLine.cast(lines.addByTwoPoints(point1, point2))
    
    if isConst:
        newLine.isConstruction = True
        
    return newLine

Brian Ekins
Inventor and Fusion 360 API Expert
Mod the Machine blog
0 Likes
Message 3 of 8

Anonymous
Not applicable

It's splines. When the lines interact with splines performance heavily suffers - its actually slower for the isConstruction = True case by a huge factor. I might expect drawing to be slower when isConstruction=False... but it being slower when this is True smells like a bug (my timings are as comments in the bottom of the code)

 

89rqxbW

 

import adsk.core, adsk.fusion, traceback
import time, random

def linesTest(ui, sk):
  def drawLine(sk, lines, point1, point2, isConst):
    newLine = adsk.fusion.SketchLine.cast(lines.addByTwoPoints(point1, point2))
    if isConst:
      newLine.isConstruction = True
    return newLine

  lines = sk.sketchCurves.sketchLines

  sk.isComputeDeferred = True

  lastLine = lines.addByTwoPoints(adsk.core.Point3D.create(-1,1,0), adsk.core.Point3D.create(30,1,0))
  lastLine = lines.addByTwoPoints(lastLine.endSketchPoint, adsk.core.Point3D.create(30,9,0))
  lastLine = lines.addByTwoPoints( lastLine.endSketchPoint, adsk.core.Point3D.create(-1,9,0))
  lastLine = lines.addByTwoPoints(lastLine.endSketchPoint, adsk.core.Point3D.create(-1,1,0))

  t0 = time.time()
  lastLine = drawLine(sk, lines, adsk.core.Point3D.create(0,0,0), adsk.core.Point3D.create(0,10,0), True)
  x = 0.25
  y = 0
  for step in range(100):
      if step % 2 == 0:
          lastLine = drawLine(sk, lines, lastLine.endSketchPoint, adsk.core.Point3D.create(x,y,0), True)
      else:
          lastLine = drawLine(sk, lines, lastLine.endSketchPoint, adsk.core.Point3D.create(x,y,0), False)

      x += .25
      if y == 0:
          y = 10
      else:
          y = 0
  t1 = time.time()
  total = t1-t0
  sk.isComputeDeferred = False

  ui.messageBox('Total time: ' + str(total))

def makeSplineBlob(sk, seed=None):
  w = 40
  h = 12
  oc = adsk.core.ObjectCollection.create()
  random.seed(seed)
  for i in range(0, 12):
    p = adsk.core.Point3D.create(random.uniform(0, w), random.uniform(0, h), 0)
    oc.add(p)

  oc.add(oc.item(0))
  sk.sketchCurves.sketchFittedSplines.add(oc)

def run(context):
    ui = None
    try:
      app = adsk.core.Application.get()
      ui  = app.userInterface
      des = adsk.fusion.Design.cast(app.activeProduct)
      root = des.rootComponent
      sk = root.sketches.add(root.xYConstructionPlane)

      makeSplineBlob(sk, 98767493)
      linesTest(ui, sk)
    except:
        if ui:
            ui.messageBox('Failed:\n{}'.format(traceback.format_exc()))


# Baseline: Refactored (but otherwise unmodified) script on my computer (multiple runs)
# 1.1380650997161865
# 1.1350648403167725
# 1.1320645809173584

# modification 1 commenting out the IF isCons and setting of construction
#  #if isConst:
#      #newLine.isConstruction = True
# 0.08600497245788574
# 0.08500480651855469
# 0.08400487899780273

# always setting construction to false, still behind IF
#    if isConst:
#        newLine.isConstruction = False
# 0.11200642585754395
# 0.11800670623779297


# always setting constructin to isCons
# newLine.isConstruction = isConst
# 1.1240642070770264
# 1.1260643005371094


# with spline blob (seed 98767493) AND NOT setting construction to any value
#  #if isConst:
#      #newLine.isConstruction = True
# 0.08200478553771973
# 0.08300471305847168

# with spline blob (seed 98767493) AND ALWAYS setting construction = False
#    newLine.isConstruction = False
# 0.08400487899780273
# 0.08500504493713379

# with spline blob (seed 98767493) AND ALWAYS setting construction = True
#    newLine.isConstruction = True
# 12.036688566207886
# 12.079690933227539

# with spline blob (seed 98767493) AND setting constuction behind IF check
#    if isConst:
#      newLine.isConstruction = True
# 22.79530358314514
# 22.137266159057617

0 Likes
Message 4 of 8

Anonymous
Not applicable

Ok, not just splines! I turned my spline blob into a line blob and the same bad behavior happened (but not as bad). I suspect your initial attempt failed to reproduce the problem because lines which lie on an axis require special code paths - not to mention the obvious optimizations which are possible. I'm willing to bet that if you rotated your drawing (or drew all lines at some minor angle) it would also see some slowdown.

 

import adsk.core, adsk.fusion, traceback
import time, random

def linesTest(ui, sk):
  def drawLine(sk, lines, point1, point2, isConst):
    newLine = adsk.fusion.SketchLine.cast(lines.addByTwoPoints(point1, point2))
    if isConst:
      newLine.isConstruction = True
    return newLine

  lines = sk.sketchCurves.sketchLines

  sk.isComputeDeferred = True

  lastLine = lines.addByTwoPoints(adsk.core.Point3D.create(-1,1,0), adsk.core.Point3D.create(30,1,0))
  lastLine = lines.addByTwoPoints(lastLine.endSketchPoint, adsk.core.Point3D.create(30,9,0))
  lastLine = lines.addByTwoPoints(lastLine.endSketchPoint, adsk.core.Point3D.create(-1,9,0))
  lastLine = lines.addByTwoPoints(lastLine.endSketchPoint, adsk.core.Point3D.create(-1,1,0))

  t0 = time.time()
  lastLine = drawLine(sk, lines, adsk.core.Point3D.create(0,0,0), adsk.core.Point3D.create(0,10,0), True)
  x = 0.25
  y = 0
  for step in range(100):
      if step % 2 == 0:
          lastLine = drawLine(sk, lines, lastLine.endSketchPoint, adsk.core.Point3D.create(x,y,0), True)
      else:
          lastLine = drawLine(sk, lines, lastLine.endSketchPoint, adsk.core.Point3D.create(x,y,0), False)

      x += .25
      if y == 0:
          y = 10
      else:
          y = 0
  t1 = time.time()
  total = t1-t0
  sk.isComputeDeferred = False

  ui.messageBox('Total time: ' + str(total))

def makeSplineBlob(sk, seed=None):
  w = 40
  h = 12
  oc = adsk.core.ObjectCollection.create()
  random.seed(seed)
  for i in range(0, 12):
    p = adsk.core.Point3D.create(random.uniform(0, w), random.uniform(0, h), 0)
    oc.add(p)

  oc.add(oc.item(0))
  sk.sketchCurves.sketchFittedSplines.add(oc)

def makeLineBlob(sk, seed=None):
  lines = sk.sketchCurves.sketchLines
  w = 40
  h = 12

  random.seed(seed)
  p0 = adsk.core.Point3D.create(random.uniform(0, w), random.uniform(0, h), 0)
  pp = p0
  for i in range(0, 11):
    p = adsk.core.Point3D.create(random.uniform(0, w), random.uniform(0, h), 0)
    lines.addByTwoPoints(p, pp)
    pp = p
  lines.addByTwoPoints(p, p0)

def run(context):
    ui = None
    try:
      app = adsk.core.Application.get()
      ui  = app.userInterface
      des = adsk.fusion.Design.cast(app.activeProduct)
      root = des.rootComponent
      sk = root.sketches.add(root.xYConstructionPlane)

      #makeSplineBlob(sk, 98767493)
      makeLineBlob(sk, 98767493)
      linesTest(ui, sk)
    except:
        if ui:
            ui.messageBox('Failed:\n{}'.format(traceback.format_exc()))


# Baseline: Refactored (but otherwise unmodified) script on my computer (multiple runs)
# 1.1380650997161865
# 1.1350648403167725
# 1.1320645809173584

# modification 1 commenting out the IF isCons and setting of construction
#  #if isConst:
#      #newLine.isConstruction = True
# 0.08600497245788574
# 0.08500480651855469
# 0.08400487899780273

# always setting construction to false, still behind IF
#    if isConst:
#        newLine.isConstruction = False
# 0.11200642585754395
# 0.11800670623779297


# always setting constructin to isCons
# newLine.isConstruction = isConst
# 1.1240642070770264
# 1.1260643005371094


# with spline blob (seed 98767493) AND NOT setting construction to any value
#  #if isConst:
#      #newLine.isConstruction = True
# 0.08200478553771973
# 0.08300471305847168

# with spline blob (seed 98767493) AND ALWAYS setting construction = False
#    newLine.isConstruction = False
# 0.08400487899780273
# 0.08500504493713379

# with spline blob (seed 98767493) AND ALWAYS setting construction = True
#    newLine.isConstruction = True
# 12.036688566207886
# 12.079690933227539

# with spline blob (seed 98767493) AND setting constuction behind IF check
#    if isConst:
#      newLine.isConstruction = True
# 22.79530358314514
# 22.137266159057617


# with line blob (seed 98767493) AND setting constuction behind IF check
#    if isConst:
#      newLine.isConstruction = True
# 6.4013659954071045
# 6.428367614746094


# with line blob (seed 98767493) AND NOT setting construction to any value
#  #if isConst:
#      #newLine.isConstruction = True
# 0.0870048999786377
# 0.08600473403930664
0 Likes
Message 5 of 8

Anonymous
Not applicable
I have a workaround which speeds this slow case back up - from 22 seconds down to 0.1000056266784668 just by setting sketch.areProfilesShown = False then setting back to True after turing isComputeDeferred back off. I'd past the fixed code but the forums are broken AGAIN/still.
0 Likes
Message 6 of 8

ekinsb
Alumni
Alumni

I've been fighting several other fires and haven't had time to look at this again yet.  I would expect using the areProfilesShown and isComputeDeferred properties to have a similar effect but the isComputeDeferred to possibly have a bigger impact.  To take advantage of the isComputeDeferred capability you need to set it to True, do all of your work, and then set it to False. Using it to bracket the creation of each line will not help performance.  The same would be true of areProfilesShown.  The idea is that you're turning off some of the normal computations that Fusion does as you're creating sketch geometry and then re-enabling it at the very end so that it's only done once.  Using these to bracket smaller amounts of work will cause that compute to happen for that chunk of work.


Brian Ekins
Inventor and Fusion 360 API Expert
Mod the Machine blog
0 Likes
Message 7 of 8

Anonymous
Not applicable
Acknowledged, but I would expect isComputeDeferred to be the ONE flag to control all secondary computation and areProfilesShown to be largely cosmetic. Anyway, i have a workaround and will use it for the time being... again i'd post my code with the workaround but the forums are still in bad shape - i assume it's one of the larger fires on your plate at the moment 😉
0 Likes
Message 8 of 8

Anonymous
Not applicable

Here's the workround in context:

import adsk.core, adsk.fusion, traceback
import time, random

def linesTest(ui, sk):
  def drawLine(sk, lines, point1, point2, isConst):
    newLine = adsk.fusion.SketchLine.cast(lines.addByTwoPoints(point1, point2))
    if isConst:
      newLine.isConstruction = True
    return newLine

  lines = sk.sketchCurves.sketchLines
  sk.areProfilesShown = False  # <----------------------------- THE WORKAROUND
  sk.isComputeDeferred = True

  lastLine = lines.addByTwoPoints(adsk.core.Point3D.create(-1,1,0), adsk.core.Point3D.create(30,1,0))
  lastLine = lines.addByTwoPoints(lastLine.endSketchPoint, adsk.core.Point3D.create(30,9,0))
  lastLine = lines.addByTwoPoints(lastLine.endSketchPoint, adsk.core.Point3D.create(-1,9,0))
  lastLine = lines.addByTwoPoints(lastLine.endSketchPoint, adsk.core.Point3D.create(-1,1,0))

  t0 = time.time()
  lastLine = drawLine(sk, lines, adsk.core.Point3D.create(0,0,0), adsk.core.Point3D.create(0,10,0), True)
  x = 0.25
  y = 0
  for step in range(100):
      if step % 2 == 0:
          lastLine = drawLine(sk, lines, lastLine.endSketchPoint, adsk.core.Point3D.create(x,y,0), True)
      else:
          lastLine = drawLine(sk, lines, lastLine.endSketchPoint, adsk.core.Point3D.create(x,y,0), False)

      x += .25
      if y == 0:
          y = 10
      else:
          y = 0
  t1 = time.time()
  total = t1-t0
  sk.isComputeDeferred = False
  sk.areProfilesShown = True   # <----------------------------- THE WORKAROUND

  ui.messageBox('Total time: ' + str(total))

def makeSplineBlob(sk, seed=None):
  w = 40
  h = 12
  oc = adsk.core.ObjectCollection.create()
  random.seed(seed)
  for i in range(0, 12):
    p = adsk.core.Point3D.create(random.uniform(0, w), random.uniform(0, h), 0)
    oc.add(p)

  oc.add(oc.item(0))
  sk.sketchCurves.sketchFittedSplines.add(oc)

def makeLineBlob(sk, seed=None):
  lines = sk.sketchCurves.sketchLines
  w = 40
  h = 11

  random.seed(seed)
  p0 = adsk.core.Point3D.create(random.uniform(0, w), random.uniform(0, h), 0)
  pp = p0
  for i in range(0, 12):
    p = adsk.core.Point3D.create(random.uniform(0, w), random.uniform(0, h), 0)
    lines.addByTwoPoints(p, pp)
    pp = p
  lines.addByTwoPoints(p, p0)

def run(context):
    ui = None
    try:
      app = adsk.core.Application.get()
      ui  = app.userInterface
      des = adsk.fusion.Design.cast(app.activeProduct)
      root = des.rootComponent
      sk = root.sketches.add(root.xYConstructionPlane)

      makeSplineBlob(sk, 98767493)
      #makeLineBlob(sk, 98767493)
      linesTest(ui, sk)
    except:
        if ui:
            ui.messageBox('Failed:\n{}'.format(traceback.format_exc()))

P.S. i had to SHIFT+F5 to force my browser to skip the cache to get the forum working again - must have had a bad javascript file or something.

0 Likes