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

distance measurement speed tips?

Evan_ReeseEvan_Reese Member Posts: 2,071 PRO
I'm working on a feature that involves possibly thousands of evDistance() calls, which are by far the most intensive part of the whole thing. Does anyone have any tips for me here on how to speed up? One side of the measurement will always be a point vector, but the other side should be able to be any object. Some thoughts below.

- If it were always 2 vectors I could probably just do it with math. Maybe I can at least check if it's 2 vectors and do that as an alternative.
- One way to reduce the number of calls. could be to only make ones that are within a given distance so I'm not measuring ones farther away. But I'm not sure how to do that without measuring it first if there is a clever way.

Any ideas?
Evan Reese / Principal and Industrial Designer with Ovyl
Website: ovyl.io

Best Answers

  • Options
    Jacob_CorderJacob_Corder Member Posts: 129 PRO
    edited February 2023 Answer ✓
    yep.
    for evDistance, calling the @ version eliminates building structures like lines,vectors and other types. 
    var dist = @evDistance(context, {
                                                            "side0" : something,
                                                            "side1" : somethingElse,
                                                            "arcLengthParameterization":false
                                                    });

    for vectors this is MUCH faster than using norm.  Norm is crazy slow
    /**
     * Much faster than norm as this removes the units which is 5x faster
     */
    function pointDistance(vec0, vec1) returns ValueWithUnits
    {
        if (vec0[0] is ValueWithUnits)
            vec0 = removeVectorUnitsReturnArray3D(vec0);
        if (vec1[0] is ValueWithUnits)
            vec1 = removeVectorUnitsReturnArray3D(vec1);
        return point3DDistanceFast(vec0, vec1) * meter;<br>}
    function point3DDistanceFast(vec0  , vec1  )  
    {
        vec0[0] -= vec1[0];
        vec0[1] -= vec1[1];
        vec0[2] -= vec1[2];
        return @sqrt(vec0[0] * vec0[0] + vec0[1] * vec0[1] + vec0[2] * vec0[2]);
         // returns a point with units if vec0 and vec1 have units. returns number if no units
    }
    function removeVectorUnitsReturnArray3D(vec) returns array
    {
        return [vec[0].value, vec[1].value, vec[2].value];
    }
    



    evaluate functions in parasolid do execute using multi core parallelism.  It is faster to measure many points and an object than to measure each point and the same object multiple times.  This is the same for evEdgeTangentLine(s) and evFaceTangentPlane(s)


    also, if you are doing for loops like this  for( var i =0; i<size(something);i+=1)
    it is faster to do this
    var ct = size(something);
    for(var i=0;i<ct;i+=1)
    as size(something) is evaluated at every return to the top of the loop. I have seen this add a second to a huge for loop.


    The last thing to do is profile when its done to find the bottlenecks. Once you find them, and they are in the standard library, ctrl+click the function to see what is taking so long.
  • Options
    Jacob_CorderJacob_Corder Member Posts: 129 PRO
    Answer ✓
    @evDistance and evDistance both work with the same stuff.  evDistance just converts points to vectors with units, parameters for faces to a vector..
    just look at evDistance in the standard library how many conversion checks it goes through.


    For @evDistance, the distance is a number in meters. 
    The returned point (.sides[x].point) is an array of 3 numbers . Converting to vector with units is simple but if not necessary then its wasted time(this takes about 20% of the regen time to convert to a vector with units).

    The parameter from a face (.sides[x].parameter) is also an array of 2 elements, not a vector like evDistance returns
     
    If you need the distance between each point and each object, then in your case it seems you need to run them in a loop, Thousands of points to a face just really cannot be optimized without being able to parallel execute the measurement(which I know you can do in solidworks. Hint hint onshape)



    to check the impact of evDistance vs @evDistance.  Run a profile on your part, then ctrl+click the evDistance function and see how long it takes to execute the extra stuff in the standard library.  In my case right now @evDistance takes 350 ms, but the rest of the stuff is taking 170 ms so total is 520ms. you can see how much time is saved there using @evDistance



Answers

  • Options
    Jacob_CorderJacob_Corder Member Posts: 129 PRO
    edited February 2023 Answer ✓
    yep.
    for evDistance, calling the @ version eliminates building structures like lines,vectors and other types. 
    var dist = @evDistance(context, {
                                                            "side0" : something,
                                                            "side1" : somethingElse,
                                                            "arcLengthParameterization":false
                                                    });

    for vectors this is MUCH faster than using norm.  Norm is crazy slow
    /**
     * Much faster than norm as this removes the units which is 5x faster
     */
    function pointDistance(vec0, vec1) returns ValueWithUnits
    {
        if (vec0[0] is ValueWithUnits)
            vec0 = removeVectorUnitsReturnArray3D(vec0);
        if (vec1[0] is ValueWithUnits)
            vec1 = removeVectorUnitsReturnArray3D(vec1);
        return point3DDistanceFast(vec0, vec1) * meter;<br>}
    function point3DDistanceFast(vec0  , vec1  )  
    {
        vec0[0] -= vec1[0];
        vec0[1] -= vec1[1];
        vec0[2] -= vec1[2];
        return @sqrt(vec0[0] * vec0[0] + vec0[1] * vec0[1] + vec0[2] * vec0[2]);
         // returns a point with units if vec0 and vec1 have units. returns number if no units
    }
    function removeVectorUnitsReturnArray3D(vec) returns array
    {
        return [vec[0].value, vec[1].value, vec[2].value];
    }
    



    evaluate functions in parasolid do execute using multi core parallelism.  It is faster to measure many points and an object than to measure each point and the same object multiple times.  This is the same for evEdgeTangentLine(s) and evFaceTangentPlane(s)


    also, if you are doing for loops like this  for( var i =0; i<size(something);i+=1)
    it is faster to do this
    var ct = size(something);
    for(var i=0;i<ct;i+=1)
    as size(something) is evaluated at every return to the top of the loop. I have seen this add a second to a huge for loop.


    The last thing to do is profile when its done to find the bottlenecks. Once you find them, and they are in the standard library, ctrl+click the function to see what is taking so long.
  • Options
    Evan_ReeseEvan_Reese Member Posts: 2,071 PRO
    for evDistance, calling the @ version eliminates building structures like lines,vectors and other types. 
    I've never even thought about using the @ version of a function. Good tip. Are you saying that it would or wouldn't still work with other stuff (curves, surfaces bodies etc)?
    evaluate functions in parasolid do execute using multi core parallelism.  It is faster to measure many points and an object than to measure each point and the same object multiple times.  This is the same for evEdgeTangentLine(s) and evFaceTangentPlane(s)
    I get this in theory, but how would you do this in practice? I have an array of thousands of points, and a few objects I want to measure to per point.
    also, if you are doing for loops like this  for( var i =0; i<size(something);i+=1)
    it is faster to do this
    var ct = size(something);
    for(var i=0;i<ct;i+=1)
    as size(something) is evaluated at every return to the top of the loop. I have seen this add a second to a huge for loop.
    Didn't know that either, but it makes sense. 
    Evan Reese / Principal and Industrial Designer with Ovyl
    Website: ovyl.io
  • Options
    S1monS1mon Member Posts: 2,492 PRO
    edited February 2023

    also, if you are doing for loops like this  for( var i =0; i<size(something);i+=1)
    it is faster to do this
    var ct = size(something);
    for(var i=0;i<ct;i+=1)
    as size(something) is evaluated at every return to the top of the loop. I have seen this add a second to a huge for loop.


    The last thing to do is profile when its done to find the bottlenecks. Once you find them, and they are in the standard library, ctrl+click the function to see what is taking so long.

    I guess this isn't too surprising, but I have to wonder why FeatureScript isn't a little smarter to know that size(something) isn't changing in the body of the loop. But then I suppose you could just as easily have a condition where i<foo and you actually want foo to be able to change during iterations of the loop.

    Is this true in other languages? Somehow I would assume a while loop would reevaluate the entire condition each time, but a for loop seems more like something where you pick your beginning and end and iteration, and go.
  • Options
    Jacob_CorderJacob_Corder Member Posts: 129 PRO
    Answer ✓
    @evDistance and evDistance both work with the same stuff.  evDistance just converts points to vectors with units, parameters for faces to a vector..
    just look at evDistance in the standard library how many conversion checks it goes through.


    For @evDistance, the distance is a number in meters. 
    The returned point (.sides[x].point) is an array of 3 numbers . Converting to vector with units is simple but if not necessary then its wasted time(this takes about 20% of the regen time to convert to a vector with units).

    The parameter from a face (.sides[x].parameter) is also an array of 2 elements, not a vector like evDistance returns
     
    If you need the distance between each point and each object, then in your case it seems you need to run them in a loop, Thousands of points to a face just really cannot be optimized without being able to parallel execute the measurement(which I know you can do in solidworks. Hint hint onshape)



    to check the impact of evDistance vs @evDistance.  Run a profile on your part, then ctrl+click the evDistance function and see how long it takes to execute the extra stuff in the standard library.  In my case right now @evDistance takes 350 ms, but the rest of the stuff is taking 170 ms so total is 520ms. you can see how much time is saved there using @evDistance



  • Options
    Evan_ReeseEvan_Reese Member Posts: 2,071 PRO
    Fantastic! Thanks for the help. That gives me a few threads to pull on for now. I've seen regen times around 30 sec and I'm sure it could get a lot higher depending on what I'm trying to do with the feature, so 20% faster is significant. My second heaviest section is a loop of evFaceTangentPlanes so I'll do the same for that too.
    Evan Reese / Principal and Industrial Designer with Ovyl
    Website: ovyl.io
  • Options
    Jacob_CorderJacob_Corder Member Posts: 129 PRO
    for evFaceTangentPlanes, make sure you call it ONLY ONCE per face as it executes in parallel. Calling it multiple times adds a lot of time.. 
    @evFaceTangentPlanes is like 5x faster than evFaceTangentPlanes (or something silly like that)

    for any trig that you need to do, stripping the vector and valuewithUnits makes it SOOOOO much faster.
  • Options
    mahirmahir Member, Developers Posts: 1,296 ✭✭✭✭✭
    S1mon said:

    also, if you are doing for loops like this  for( var i =0; i<size(something);i+=1)
    it is faster to do this
    var ct = size(something);
    for(var i=0;i<ct;i+=1)
    as size(something) is evaluated at every return to the top of the loop. I have seen this add a second to a huge for loop.


    The last thing to do is profile when its done to find the bottlenecks. Once you find them, and they are in the standard library, ctrl+click the function to see what is taking so long.

    I guess this isn't too surprising, but I have to wonder why FeatureScript isn't a little smarter to know that size(something) isn't changing in the body of the loop. But then I suppose you could just as easily have a condition where i<foo and you actually want foo to be able to change during iterations of the loop.

    Is this true in other languages? Somehow I would assume a while loop would reevaluate the entire condition each time, but a for loop seems more like something where you pick your beginning and end and iteration, and go.
    There may be compilers that are smart enough to optimize this type of redundancy during the build process. However, I don't think Onshape is compiling Featurescript code. More likely than not Featurescript is just treated as a procedural macro language that is run line by line whenever the feature is regenerated. 
Sign In or Register to comment.