web-dev-qa-db-ja.com

Cでは、大きな「ポインターチェーン」はパフォーマンスやコードのクリーン度に悪影響を及ぼしますか?

たとえば、次のとおりです。

i = readString(&packet->data.play_server.updatesign.line1, pbuf, ps);

ネストされた構造体/共用体が大量にあります。これは一般的にコードの清潔さで嫌われているのですか、それともパフォーマンスが悪いのですか?

3
JavaProphet

あなたの例ではパフォーマンスが悪いのは疑問です。シーケンスでの真のランタイムポインター逆参照は、キャッシュコヒーレンシに悪影響を与える可能性があります。しかし、あなたの例はこれを示していません。逆参照するランタイムポインターは1つだけです。

Cは静的に型付けされた言語です。したがって、コンパイル時に、コンパイラはすべてのオブジェクトのすべてのメンバーサブオブジェクトのサイズとレイアウトを認識します。したがって、.play_server.updatesign.line1は基本的に、ポインターの静的整数オフセットにコンパイルされます。 &packet->data + static_offsetになります(Cポインター演算ではなくバイト加算を使用)。

これらの.sが->sであった場合、それは別の問題になります。このような各ポインタは、実行時に特定の値になる可能性があります。したがって、コンパイラーはすべてを静的なオフセットにまとめることはできません。アクセスのチェーンとして実行時に実行する必要があり、各アクセスは次のオブジェクトからポインターを読み取ります。したがって、リンクされたリストを反復処理する場合と同様に、キャッシュの一貫性などが低下する可能性があります。

コードの清浄度に関しては、それはむしろあなたがどこにいて何をしているかに依存します。 packet->dataが不変条件を必要としない単純なデータ構造であれば、問題ありません。これらのサブオブジェクトは単なる集約であり、カプセル化を必要とするスマートコンテナではありません。このデータ構造内で何らかの不変要素が存在する場合、そのようなデータ構造を直接変更すると、カプセル化に違反するため危険です。

さらに、Cでカプセル化を提供するには、通常、宣言(前方宣言など)を隠す必要があるため、インライン化が難しくなる傾向があります。そして、それらのサブオブジェクトアクセスのそれぞれがカプセル化されたアクションである場合、コンパイラは静的オフセットまでアクセスをコンパイルすることが簡単にできないため、懸念される一種の「ポインタチェーン」を表す可能性があります。そのため、Cプログラムは、個々のシステム自体ではなく、主要なシステムまたはAPIの境界でカプセル化を採用する傾向があります。

あるプログラマにとってクリーンなものは、別のプログラマにとってクリーンではありません。

4
Nicol Bolas