Welcome to the Onshape forum! Ask questions and join in the discussions about everything Onshape.

First time visiting? Here are some places to start:
  1. Looking for a certain topic? Check out the categories filter or use Search (upper right).
  2. Need support? Ask a question to our Community Support category.
  3. Please submit support tickets for bugs but you can request improvements in the Product Feedback category.
  4. Be respectful, on topic and if you see a problem, Flag it.

If you would like to contact our Community Manager personally, feel free to send a private message or an email.

Options

Why is Multi-mirror heavier than individual mirror features?

Evan_ReeseEvan_Reese Member Posts: 2,064 PRO
edited September 2020 in FeatureScript
I started making this feature a while ago and it got a great overhaul from @MBartlett21 that made it way more stable and concise. I noticed recently on a project using it that it had a 6 second regen time, but doing the same thing with the standard mirror feature, was a lot less. Anybody have any idea which area is causing this, and whether it can be optimized more? I love using it to keep my tree shorter and to not have to use like 8 individual mirror features, but in some cases, like the one above, the time cost is too much.

Here's a link to the feature
Evan Reese / Principal and Industrial Designer with Ovyl
Website: ovyl.io

Answers

  • Options
    Jacob_CorderJacob_Corder Member Posts: 126 PRO
    @Evan_Reese
    Have you tried profiling the feature?  it will definitely show you where and why it is taking so long. Just a thought.
  • Options
    Evan_ReeseEvan_Reese Member Posts: 2,064 PRO
    I don't do a ton of coding, so I'm not sure what that means. Do you mean set up some timers for each section and see what's taking the longest? I'll play with that
    Evan Reese / Principal and Industrial Designer with Ovyl
    Website: ovyl.io
  • Options
    ilya_baranilya_baran Onshape Employees, Developers, HDM Posts: 1,175
    Ilya Baran \ VP, Architecture and FeatureScript \ Onshape Inc
  • Options
    Evan_ReeseEvan_Reese Member Posts: 2,064 PRO
    Ah, I've never really known what that was! thanks for pointing it out.

    In testing it out it seems that the opBoolean is the culprit. I was surprised by this since using the native mirror feature would execute just as many booleans, but the total time for those 12 mirrors was just 3.7 seconds against Multi-Mirror's 7.9. That got me thinking that it must have something to do with the fact that Multi-mirror's boolean sets were all of 4 bodies (since it's mirrored two ways), but the standard mirror features were each only a 2-part boolean. To confirm this, I tried using just 2 multi-mirrors with one axis each, and sure enough, the time came way down.

    So, I think the thing to try is opBoolean in a for loop and only boolean pairs of bodies at once. @ilya_baran is that the path you'd take?

    Thank you both for being willing to educate on something that must feel obvious to people using FS all the time.
    Evan Reese / Principal and Industrial Designer with Ovyl
    Website: ovyl.io
  • Options
    MBartlett21MBartlett21 Member, OS Professional, Developers Posts: 2,034 EDU
    @Evan_Reese
    To test the performance of Onshape's features, I find that copy-pasting the code from std into a new document and running the profiler on it works well.
    mb - draftsman - also FS author: View FeatureScripts
    IR for AS/NZS 1100
  • Options
    Evan_ReeseEvan_Reese Member Posts: 2,064 PRO
    good tip! thanks
    Evan Reese / Principal and Industrial Designer with Ovyl
    Website: ovyl.io
  • Options
    emagdalenaC2iemagdalenaC2i Member, Developers, Channel partner Posts: 859 ✭✭✭✭✭
    @Evan_Reese You can take a look to this old post of @NeilCooke about performance in patterns and boolean features
    https://forum.onshape.com/discussion/9314/faster-patterns
    Un saludo,

    Eduardo Magdalena                         C2i Change 2 improve                         ☑ ¿Por qué no organizamos una reunión online?  
                                                                         Partner de PTC - Onshape                                     Averigua a quién conocemos en común
  • Options
    Evan_ReeseEvan_Reese Member Posts: 2,064 PRO
    Well I tried doing each boolean one at a time with a loop and it's slightly worse, but you can also see how much better it is to use the native mirror feature a ton of times. So the profiling has helped me find what's taking so long, but I still don't understand why and my best guess seems to have been wrong. Any other ideas or pointers?


    Evan Reese / Principal and Industrial Designer with Ovyl
    Website: ovyl.io
  • Options
    MBartlett21MBartlett21 Member, OS Professional, Developers Posts: 2,034 EDU
    @Evan_Reese
    The reason it is still slow is because the booleans are not done in the same way as the mirror.

    For instance, lets say you have the following two-way mirror:

    Part 1  |  Part 2
    --------+--------
    Part 3  |  Part 4
    

    Two mirrors (horizontal, then vertical) will do the following patterns and booleans:

    Pattern 1: Part 1 => Part 2
    Boolean 1: Part 1 + Part 2 => (Part 1+2)
    Pattern 2: (Part 1+2) => (Part 3+4)
    Boolean 2: (Part 1+2) + (Part 3+4) => END

    The Multi-mirror feature

    Pattern 1: Part 1 => Part 2, Part 3, Part 4
    Boolean 1: Part 1 + Part 2 => (Part 1+2)
    Boolean 2: (Part 1+2) + Part 3 => (Part 1+2+3)
    Boolean 3: (Part 1+2+3) + Part 4 => END
    mb - draftsman - also FS author: View FeatureScripts
    IR for AS/NZS 1100
  • Options
    Evan_ReeseEvan_Reese Member Posts: 2,064 PRO
    Thanks, that does make sense. I've thought on it more last night and this morning and have another idea to try.
    Evan Reese / Principal and Industrial Designer with Ovyl
    Website: ovyl.io
  • Options
    Evan_ReeseEvan_Reese Member Posts: 2,064 PRO
    Ok! I reworked it again with much better results. Here are regen times as used on a fairly complex project recently. Thanks!



    In profiling it, I found that this line (and a leftover debug) was taking up enough time to make it still slightly worse than just using standard mirror.
    setExternalDisambiguation(context, id, loopBody);
    I got rid of it with apparently no ill effect. I don't understand what this is doing or whether I should keep it. Neither the description in the Standard nor a Google search made it clear to me. I assume @MBartlett21 put it there with good reason, but is it still important with my rewrite?
    Evan Reese / Principal and Industrial Designer with Ovyl
    Website: ovyl.io
  • Options
    romeograhamromeograham Member, csevp Posts: 657 PRO
    @Evan_Reese - it's great having an industrial designer thinking about custom FS! You're solving my problems too...your FeatureScripts often precisely address element(s) of my workflow that are repetitive, error-prone, and present on most of my parts. It's only after I find one of your FSs that I realize some of the time saving opportunities there are in my workflow.

    ...and this (of course) doesn't even touch on the many repetitive model features / elements that are specific to my workflow and the parts that we make. When (if) I ever start making my own FS, Onshape will get even better!

    Thanks @Evan_Reese, and thanks Onshape and this community!

    Romeo
  • Options
    kevin_o_toole_1kevin_o_toole_1 Onshape Employees, Developers, HDM Posts: 565
    edited September 2020
    @Evan_Reese
    The journey of making subtle improvements to features is a long one!

    See this discussion for why setExternalDisambiguation can be helpful:
    https://forum.onshape.com/discussion/comment/64129/#Comment_64129
    (EDIT: linked to correct comment in thread)

    That said, I think iterating through those bodies explicitly is probably not needed in your feature in the first place. A single pattern with multiple bodies will handle disambiguation fine on its own, and I think the correct boolean behavior after a single pattern can be achieved by calling processNewBodyIfNeeded() (which mostly just calls opBoolean with targetsAndToolsNeedGrouping: true, enabling heuristics we use with standard features creating bodies, which help with performance and ensure we don't union preexisting bodies).

    If you want to reuse even more code, it's possible both the pattern and the boolean can be done in a single call to applyPattern(). I'm seeing the boolean parameters of applyPattern are not documented, but reading the code, looks like the fields in booleanStepTypePredicate and booleanStepScopePredicate are handled properly.
  • Options
    MBartlett21MBartlett21 Member, OS Professional, Developers Posts: 2,034 EDU
    edited September 2020
    @Evan_Reese
    I think if you add
    const bodyId is Id = topId + unstableIdComponent(i);
    setExternalDisambiguation(context, bodyId, loopBody);
    just after loopBody = bodies[i];, then, in the inside loop, use const id = bodyId + j;, instead of const id = topId + unstableIdComponent(i) + unstableIdComponent(j); and then remove the setExternalDisambiguation in the inner loop, it should work ok.
    mb - draftsman - also FS author: View FeatureScripts
    IR for AS/NZS 1100
  • Options
    Evan_ReeseEvan_Reese Member Posts: 2,064 PRO
    @kevin_o_toole_1
    Thanks so much for the detailed reply, and the breadcrumb trail of links back to other detailed replies. Thanks for taking the time to explain this stuff in so much detail. I read them all a few times! Since I've got this feature already functioning as I'd hope, I think I'll leave these kinds of optimizations for future features. I'll remember to reach for applyPattern() or processNewBodyIfNeeded() before opBoolean on the next one. I also understand the value of disambiguation at least a bit more now.
    Evan Reese / Principal and Industrial Designer with Ovyl
    Website: ovyl.io
  • Options
    Evan_ReeseEvan_Reese Member Posts: 2,064 PRO
    @Evan_Reese
    I think if you add
    const bodyId is Id = topId + unstableIdComponent(i);
    setExternalDisambiguation(context, bodyId, loopBody);
    just after loopBody = bodies[i];, then, in the inside loop, use const id = bodyId + j;, instead of const id = topId + unstableIdComponent(i) + unstableIdComponent(j); and then remove the setExternalDisambiguation in the inner loop, it should work ok.
    Thanks! I'll give this a go.
    Evan Reese / Principal and Industrial Designer with Ovyl
    Website: ovyl.io
  • Options
    Evan_ReeseEvan_Reese Member Posts: 2,064 PRO
    @Evan_Reese - it's great having an industrial designer thinking about custom FS! You're solving my problems too...your FeatureScripts often precisely address element(s) of my workflow that are repetitive, error-prone, and present on most of my parts. It's only after I find one of your FSs that I realize some of the time saving opportunities there are in my workflow.

    ...and this (of course) doesn't even touch on the many repetitive model features / elements that are specific to my workflow and the parts that we make. When (if) I ever start making my own FS, Onshape will get even better!

    Thanks @Evan_Reese, and thanks Onshape and this community!

    Romeo
    Thanks, Romeo! I'm honestly either just writing the features I've always wanted, or thinking up a project that is interesting to me, marginally handy, and not too ambitious so I can learn Featurescript.

    I recommend that you keep a written list of repetitive tasks unique to your workflow that are good candidates for custom features. That way if you get started you can just tick them off one at a time.
    Evan Reese / Principal and Industrial Designer with Ovyl
    Website: ovyl.io
Sign In or Register to comment.