早すぎる議論が根源的な悪の講義を挿入する
そうは言っても、不必要な効率を避けるために私が習得したいくつかの習慣があり、場合によってはコードをよりシンプルで正確にすることもできます。
これは一般的な原則の議論ではありませんが、コードに不必要な非効率性を持ち込むことを避けるために知っておくべきいくつかの事柄の議論です。
これはおそらく上記の長い議論に統合されるべきです。内側のループが計算を繰り返すループ内のループが遅くなるのは、かなり常識です。例えば:
for (i = 0; i < strlen(str); i++) {
...
}
文字列が本当に長い場合、ループの各反復で長さが再計算されるため、これには非常に長い時間がかかります。GCCstrlen()
は純粋な関数としてマークされているため、実際にこのケースを最適化することに注意してください。
100万個の32ビット整数をソートする場合、バブルソートは間違った方法です。一般的に、ソートはO(n * log n)時間(または基数ソートの場合は良い)で実行できるため、データが小さくなることがわかっていない限り、少なくともO(n *ログn)。
同様に、データベースを扱うときは、インデックスに注意してください。あなたならばSELECT * FROM people WHERE age = 20
、あなたは人(年齢)にインデックスを持っていない、と、それは、シーケンシャルスキャンではなくはるかに高速O(Nログ)インデックス・スキャンよりもO(n)を必要とします。
整数算術階層
Cでプログラミングする場合、算術演算の中には他の演算よりも高価なものがあることに留意してください。整数の場合、階層は次のようになります(最も高価ではない)。
確かに、コンパイラは通常、主流のコンピューターをターゲットにしている場合n / 2
にn >> 1
自動的に最適化するようなものですが、組み込みデバイスをターゲットにしている場合、その贅沢は得られないかもしれません。
また、% 2
および& 1
異なる意味を持っています。除算とモジュラスは通常ゼロに丸められますが、実装が定義されています。良いオール>>
と&
常に負の無限大に向かって丸めます(私の意見では)これははるかに理にかなっています。たとえば、私のコンピューターでは:
printf("%d\n", -1 % 2); // -1 (maybe)
printf("%d\n", -1 & 1); // 1
したがって、意味のあるものを使用してください。% 2
あなたがもともと書くつもりだったときに使うことによってあなたが良い男の子であると思わないでください& 1
。
高価な浮動小数点演算
以下のような浮動小数点演算を避ける重いpow()
とlog()
整数を扱う場合は特に、本当に、それらを必要としないコードに。たとえば、数字を読んでみましょう:
int parseInt(const char *str)
{
const char *p;
int digits;
int number;
int position;
// Count the number of digits
for (p = str; isdigit(*p); p++)
{}
digits = p - str;
// Sum the digits, multiplying them by their respective power of 10.
number = 0;
position = digits - 1;
for (p = str; isdigit(*p); p++, position--)
number += (*p - '0') * pow(10, position);
return number;
}
この使用pow()
(およびそれを使用するために必要なint
<-> double
変換)がかなり高価であるだけでなく、精度が失われる可能性があります(偶然、上記のコードには精度の問題はありません)。そのため、このタイプの関数が数学以外のコンテキストで使用されているのを見ると、私はひるむ。
また、各反復で10倍になる以下の「賢い」アルゴリズムは、実際には上記のコードよりも簡潔であることに注意してください。
int parseInt(const char *str)
{
const char *p;
int number;
number = 0;
for (p = str; isdigit(*p); p++) {
number *= 10;
number += *p - '0';
}
return number;
}