swift-nav / plover

Plover is a language for matrix algebra on embedded systems.
http://swift-nav.github.io/plover
MIT License
47 stars 14 forks source link

feature: Inlining data when possible #69

Open ryanorendorff opened 8 years ago

ryanorendorff commented 8 years ago

For model predictive control (and other applications), it is common to calculate a quadratic function as follows.

quad_cost {n, p, N}     -- For a horizon of N
  (P :: double[n, n])   -- Terminal cost
  (Q :: double[n, n])   -- Stage cost
  (R :: double[p, p])   -- Controller cost
  (x :: double[n, N])   -- N state vectors
  (u :: double[p, N-1]) -- (N-1) control vectors
  :: double
  := ( x[:, N]^T*P*x[:, N] +
       sum (vec k in N-1 -> x[:, k]^T*Q*x[:, k] + u[:, k]^T*R*u[:,k]);
     )[0];

In many cases, since we are not changing the matrices used in the quadratic function (either the matrices do not need to change or can't anyway because doing so would require a recompile), it is convenient to make functions where some of the inputs are prefilled in.

quad_cost_filled
  (x :: double[2, 2])
  (u :: double[2, 1]) :: double
  := quad_cost (mat (1, 0; 0, 1)) (mat (1, 0; 0, 1)) (mat (1, 0; 0, 1)) x u;

However, this version results in more assembly instructions (using gcc -O2) than the following, where the inlined data has been placed inside the quadratic function itself (loops can be unrolled, stack size can be constant, etc).

quad_cost2 -- For a horizon of 2
  (x :: double[2, 2])
  (u :: double[2, 1]) :: double
  := (
    P := (mat (1, 0; 0, 1));
    Q := (mat (1, 0; 0, 1));
    R := (mat (1, 0; 0, 1));
    N := 2;
    x[:, N]^T*P*x[:, N] +
       sum (vec k in N-1 -> x[:, k]^T*Q*x[:, k] + u[:, k]^T*R*u[:,k]);
     )[0];

When converted into assembly, quad_cost2 has 5x fewer instructions than quad_cost and it is possible to get rid of the loops.

It would be convenient if Plover had a way of simplifying any functions where some of the fields already contain constant data to help the C compiler later make more optimized versions of the code.

This could also probably be done if an inline tag existed that could be passed into the C code.