人々がコメントで言ったように:
if(SQRT[a*a+b*b]>something)
恐ろしいユースケースの例です。それが SQRT に必要なすべての場合は、単に square something
.
何もエイリアスしないことをコンパイラに伝えることができる限りSQRT
、実行時ループは実行可能ファイルを小さくし、起動時にわずかな CPU オーバーヘッドを追加するだけです。絶対に使わuint8_t
ないでint
ください。8 ビットのメモリ ロケーションから 32 ビットのテンポラリをロードすることは、ゼロで埋められた 32b のメモリ ロケーションからロードするよりも遅くはありません。(メモリ オペランドを使用する代わりに、x86 で余分なmovsx
命令を使用すると、キャッシュ汚染が減少するという点で十分な代償を払うことができます。いずれにせよ、RISC マシンは通常、メモリ オペランドを許可しないため、値をレジスタにロードする命令が常に必要です。 )
また、sqrt
Sandybridge でのレイテンシは 10 ~ 21 サイクルです。頻繁に必要としない場合、int->double、sqrt、double->int チェーンは、L2 キャッシュ ヒットよりもそれほど悪くはありません。そして、L3 やメイン メモリに移動するよりも優れています。多くの が必要な場合はsqrt
、必ず LUT を作成してください。テーブル内でバウンスして L1 ミスを引き起こしている場合でも、スループットは大幅に向上します。
次のようなもので、sqrtingの代わりにsquaringすることで初期化を最適化できます
uint8_t sqrt_lookup[65536];
void init_sqrt (void)
{
int idx = 0;
for (int i=0 ; i < 256 ; i++) {
// TODO: check that there isn't an off-by-one here
int iplus1_sqr = (i+1)*(i+1);
memset(sqrt_lookup+idx, i, iplus1_sqr-idx);
idx = iplus1_sqr;
}
}
存在することの利点を引き続き得ることができますsqrt_lookup
(const
コンパイラーはエイリアスできないことを知っています)。を使用するかrestrict
コンパイラに嘘をつくので、テーブルのユーザーにはconst
配列が表示されますが、実際には配列に書き込みます。
extern const
これには、コンパイラを初期化するファイルではなく、ほとんどの場所で宣言することにより、コンパイラに嘘をつくことが含まれる場合があります。これが実際に機能し、2 つの異なるシンボルを参照するコードを作成しないことを確認する必要があります。それを初期化する関数で単にキャストするconst
と、コンパイラーがそれを配置した場合にセグメンテーション違反が発生する可能性があります(または、一部のプラットフォームで可能であれば、初期化されていない場合はrodata
読み取り専用メモリー?)bss
おそらく、次のようにして、コンパイラに嘘をつくことを避けることができます。
uint8_t restrict private_sqrt_table[65536]; // not sure about this use of restrict, maybe that will do it?
const uint8_t *const sqrt_lookup = private_sqrt_table;
実際には、これは単なるデータconst
へのポインターconst
であり、それが指しているものが別の参照によって変更できないという保証ではありません。