ARM > 抜粋 > Writing efficient C for the ARM / キーフレーズ

Writing efficient C for the ARM
http://hackipedia.org/Platform/3D0/html,%203DO%20SDK%20Documentation/Type%20A/tktfldr/acbfldr/4acbb.html

~~50msecの計算処理を20msecに落とすためには上記の記載事項を実践することになるのだろうか?~~ 達成できた。

ベアメタルアプリケーションではない点、話が複雑になっている。


キーワード・キーフレーズ (気になったもの)

- maximise low-level efficiency
- function call overhead on the ARM is small and often in proportion to the work done by the called function
   - (補足) 別のリンクで見たものと整合しない
- two very important types of function call
- Good general advice is to keep functions small
- how to assist the C compiler when functions cannot be kept small
- leaf functions (a leaf function is one which makes no calls from within its body).
- using just 5 registers (a1-a4 and ip)
   - carry no function entry or exit overhead
- registers 
   - efficient to save them using STM
- Veneer functions (Simple fail continued functions)
- call can be converted to a tail continuation


### Fast paths and slow paths - A useful transformation

```c
int f(Buffer *b)
{    if (b->n > 0)
     {   /* The usual path through the function... */
         /*     95% of all calls.*/
         /* Simple calculation involving b->buf, b->n, etc.*/
         return ...;
     }
     /* Exceptional path through the function... */
     /*     5% of all calls.  */
     /* Complicated calculation involving calls
     /*     to other functions.*/
     return ...;
}
```

the entry and register-save overhead caused by the infrequent heavyweight pathを緩和するコードは以下。

```c
int f2(Buffer *b)
{     /* Exceptional path through the function... */
      /*     5% of all calls.  */
      /* Complicated calculation involving calls */
      /*     to other functions.*/
      return ...;
}

 int f(Buffer *b)
{    if (b->n > 0)
         {     /* The usual path through the function... */
               /*     95% of all calls.*/
               /* Simple calculation involving b->buf, b->n, etc.*/
               return ...;]
        }
    return f2(b);
}
```

-  it works particulary well for the ARM because of the careful optimisation of tail continuation in lightweight functions.

### Function arguments and argument passing

- influences low-level efficiency
- argument passing
- Under the ARM Procedure Call Standard, up to four argument words can be passed to a function in registers
- up to four integral (not floating point) arguments are particularly efficient
- the 5th, 6th, etc., words will be passed on the stack
- How can argument passing overhead be minimised?
   - 色々提案されている
   - Factor out read-mostly global control state and make this static ...
   - wrap it up in a struct and pass a pointer to it
- Collect related data into structs 

上記を読んでいると、実装済みのstrcutのポインタ渡しは効率の観点から悪くない。

### Register allocation and how to help it

- register allocation is critical to the efficiency of code compiled for RISC processors
- only 16 registers
- a particularly pathological expression could, in principle, occupy most of the allocatable registers

レジスタを使い切ってしまうと、残りの変数はメモリ上で処理され効率が落ちる、という解釈をした。

- As a rule of thumb, avoid very large expressions (more than 30 terms).

- The more serious problem is with long scope program variables.

```c
int f()                            int f()
{   int i, j, ...;                 {   int j, ...;
                                     { int i;
    for (i = 0;  i < lim;  ++i)        for (i = 0;  i < lim;  ++i)
    {                                  {
        ...                               ...
    }                                  }
                                     }
                                     { int i;
    for (i = 0;  i < lim;  ++i)        for (i = 0;  i < lim;  ++i)
    {  /* register pressure in this    {
       loop forces 'i' to memory */
    }                                  }
                                     }
                                     { int i;
    for (i = 0;  i < lim;  ++i)        for (i = 0;  i < lim;  ++i)
    {                                  {
        ...                                ...
    }                                  }
                                     }
}                                  }
```

[解釈] 左の場合は、iがregister割当てでなかった場合、関数内でずっとmemory上処理になってしまう。右の場合は、forのたびにiのregister割当てがトライされる。

- As a rule of thumb, keep variable declarations local, especially in large functions.

- When a local variable is spilled to memory, there is a stack adjustment on each entry to and exit from its containing scope.

- In principle, the compiler could be more intelligent about adjusting the stack locally for large variables

結局、ブロックで囲った方がいいのかどうかは文面を読みきれていない。

### Static and extern variables - minimising access costs

(ファイルスコープ)static変数について

- if there are many uses of static variables within a function then there is a good chance that the static base will become a global common subexpression (CSE)
- overall, access to static variables will be no more expensive than to auto variables on the stack.

CSEになったあかつきにはアクセスコストは低くなる。

extern宣言の変数について

- Extern variables are fundamentally more expensive

LDR, STRが関係する。global CSEになりそうにない。
extern変数をたくさん使うと、アクセスコストが跳ね上がる。

- in the absence of inter-procedural data flow analysis
- any non- const static or extern variable could be side-effected by the call
- the standard streams: stdin, stdout and stderr

```c
extern FILE *out;                  extern FILE *out;
/* the output stream */            /* the output stream */

void print_it(Thing *t)            void print_it(Thing *t)
{                                  {   FILE *f = out;
    fprintf(out, ...);                 fprintf(f, ...);
    print_1(t->first);                 print_1(t->first);
    fprintf(out, ...);                 fprintf(f, ...);
    print_2(t->second);                print_2(t->second);
    fprintf(out, ...);                 fprintf(f, ...);
    ...                                ...
}                                  }
```

左の例ではoutがprintのたびにre-computed, re-loadedされる。右の例ではfはレジストに保持されたままになる。

- saved more than 5% of its code space

### The switch() statement

- the ARM C compiler does a good job of deciding when to compile jump tables and when to compile trees of if-then-elses

その他、文面を読みきれてないかもしれないが、速度に関する向上ではなくコードサイズ関連の記載のようだ。

- The uniform application of this transformation to the disassembler module of the ARM C compiler saved between 5% and 10% of its code space.