[Home]

So I was recently writing some code for my robotics class, and I wrote this code:

/*
  alpha:    repulsion coefficient
  gamma:    attraction coefficient
  x0x, x0y: position of robot 0
  xjx, xjy: position of robot j
  r0, rj:   robot radii
  dSafe:    safe distance
  dFar:     "too far" distance
 */
Vector2 getFollowerAttraction(double alpha, double gamma,
    double x0x, double x0y, double xjx, double xjy, double r0, double rj,
    double dSafe, double dFar) {
  // Create the return struct
  Vector2 fa;
  // Calculate the distance between the center of robot j to 0
  double sx = x0x - xjx;
  double sy = x0y - xjy;
  double sm = sqrt(sx*sx + sy*sy);
  // Subtract the radii of the robots
  double d = sm - (r0 + rj);
  // Calculate the vector conversion factor
  double ka;
  if (d < dSafe) {
    ka = -alpha / (d*d * sm);
  } else if (d < dFar) {
    fa.x = 0;
    fa.y = 0;
    return fa;
  } else {
    ka = gamma * d*d / sm;
  }
  // Calculate the vector of the force
  fa.x = ka * sx;
  fa.y = ka * sy;
  return fa;
}

Vector2 getFollowerAttraction() {
  Pose &x0 = pose[0];
  Pose &xj = pose[ID];
  return getFollowerAttraction(REPULSION_CONSTANT_ALPHA,
      ATTRACTION_CONSTANT_GAMMA, x0.x, x0.y, xj.x, xj.y, ROBOT_RADIUS_M,
      ROBOT_RADIUS_M, SAFE_DISTANCE_M, FAR_DISTANCE_M);
}

I figured that having the option to override the constants if necessary by calling the first form was a nice thing to have. But I was concerned that adding this ability would cost me performance. Putting 640 bits of arguments on the stack seemed a bit overkill if they weren’t needed. So I decided to do a bit of an experiment, and I am very pleased with the results. I wrote a simple C++ file called test.cpp:

const double A = 0;
const double B = 1;
const double C = 2;
const double D = 3;
const double E = 4;
const double F = 5;
const double G = 6;
const double H = 7;
const double I = 8;
const double J = 9;

double add(double a, double b, double c, double d, double e, double f,
        double g, double h, double i, double j) {
    return a + b + c + d + e + f + g + h + i + j;
}

double add() {
    return add(A, B, C, D, E, F, G, H, I, J);
}

int main() {
    return (int)add();
}

I tested it:

0 ✓ ntd5@ntd5-mbpro ~
% g++ test.cpp -o test -O2
0 ✓ ntd5@ntd5-mbpro ~
% ./test
45 ✗ ntd5@ntd5-mbpro ~
% g++ test.cpp -S -o test.s -O2

And this was what I got for the assembly (test.s):

    .file   "test.cpp"
    .text
    .p2align 4,,15
    .globl  _Z3adddddddddddd
    .type   _Z3adddddddddddd, @function
_Z3adddddddddddd:
.LFB0:
    .cfi_startproc
    addsd   %xmm1, %xmm0
    addsd   %xmm2, %xmm0
    addsd   %xmm3, %xmm0
    addsd   %xmm4, %xmm0
    addsd   %xmm5, %xmm0
    addsd   %xmm6, %xmm0
    addsd   %xmm7, %xmm0
    addsd   8(%rsp), %xmm0
    addsd   16(%rsp), %xmm0
    ret
    .cfi_endproc
.LFE0:
    .size   _Z3adddddddddddd, .-_Z3adddddddddddd
    .p2align 4,,15
    .globl  _Z3addv
    .type   _Z3addv, @function
_Z3addv:
.LFB1:
    .cfi_startproc
    movsd   .LC0(%rip), %xmm0
    ret
    .cfi_endproc
.LFE1:
    .size   _Z3addv, .-_Z3addv
    .section    .text.startup,"ax",@progbits
    .p2align 4,,15
    .globl  main
    .type   main, @function
main:
.LFB2:
    .cfi_startproc
    movl    $45, %eax
    ret
    .cfi_endproc
.LFE2:
    .size   main, .-main
    .section    .rodata.cst8,"aM",@progbits,8
    .align 8
.LC0:
    .long   0
    .long   1078362112
    .ident  "GCC: (Ubuntu 4.8.4-2ubuntu1~14.04.1) 4.8.4"
    .section    .note.GNU-stack,"",@progbits

As you can see below _Z3addv, the constants got applied into the function. One can imagine that you would get similar optimization in my robotics code. Yay!