4

次のパターンが非常に頻繁に発生しています。

 b->last = ngx_cpymem(b->last, "</pre><hr>", sizeof("</pre><hr>") - 1);

リテラル文字列が 2 回使用されていることに注意してください。抜粋は nginx ソースベースからのものです。

コンパイラは、コンパイル単位内でこれらのリテラルが検出されたときに、これらのリテラルをマージできる必要があります。

私の質問は次のとおりです。

  1. 商用グレードのコンパイラ (VC++、GCC、LLVM/Clang) は、コンパイル ユニット内で発生したときにこの冗長性を取り除きますか?
  2. (静的) リンカは、オブジェクト ファイルをリンクするときにそのような冗長性を取り除きますか?
  3. 2 が適用される場合、動的リンク中にこの最適化が行われますか?
  4. 1 と 2 が当てはまる場合、それらはすべてのリテラルに当てはまりますか。

これらの質問は、プログラマーが効率を失うことなく冗長にできるため、重要です。つまり、プログラムに組み込まれている膨大な静的データ モデル (たとえば、低レベルのシナリオで使用される意思決定支援システムのルール) について考えることができます。 .

編集

2 点 / 説明

  1. 上記のコードは、認められた「マスター」プログラマーによって書かれています。男は片手でnginxを書きました。

  2. リテラル ハードコーディングの可能なメカニズムのどれが優れているかは尋ねていません。したがって、話題を逸らさないでください。

編集 2

私の元の例は、非常に不自然で制限的でした。次のスニペットは、内部のハードコーディングされた知識に埋め込まれている文字列リテラルの使用法を示しています。最初のスニペットは、構成パーサーがどの文字列にどの列挙値を設定するかを伝えるためのものであり、2 番目のスニペットは、より一般的にプログラム内の文字列として使用されるためのものです。個人的には、コンパイラが文字列リテラルの 1 つのコピーを使用し、要素が静的であるため、グローバル シンボル テーブルに入らない限り、これで満足しています。

static ngx_conf_bitmask_t  ngx_http_gzip_proxied_mask[] = {
   { ngx_string("off"), NGX_HTTP_GZIP_PROXIED_OFF },
   { ngx_string("expired"), NGX_HTTP_GZIP_PROXIED_EXPIRED },
   { ngx_string("no-cache"), NGX_HTTP_GZIP_PROXIED_NO_CACHE },
   { ngx_string("no-store"), NGX_HTTP_GZIP_PROXIED_NO_STORE },
   { ngx_string("private"), NGX_HTTP_GZIP_PROXIED_PRIVATE },
   { ngx_string("no_last_modified"), NGX_HTTP_GZIP_PROXIED_NO_LM },
   { ngx_string("no_etag"), NGX_HTTP_GZIP_PROXIED_NO_ETAG },
   { ngx_string("auth"), NGX_HTTP_GZIP_PROXIED_AUTH },
   { ngx_string("any"), NGX_HTTP_GZIP_PROXIED_ANY },
   { ngx_null_string, 0 }
};

密接に続く:

static ngx_str_t  ngx_http_gzip_no_cache = ngx_string("no-cache");
static ngx_str_t  ngx_http_gzip_no_store = ngx_string("no-store");
static ngx_str_t  ngx_http_gzip_private = ngx_string("private");

話題にとどまった人たち、ブラボー!

4

5 に答える 5

8

の特定のケースではsizeof("</pre><hr>")、文字列リテラルが出力ファイルに表示されないことは事実上確実であることに注意してください。sizeof式全体は、コンパイル時に整数定数11に評価できます。

それにもかかわらず、コンパイラが同一の文字列リテラルをマージすることは、依然として非常に一般的な最適化です。

于 2010-06-28T10:51:14.543 に答える
7

私はあなたの質問に答えることができませんが、そのような状況では常に const 文字列 (または #define の方がよいでしょう) を使用するようにしてください。問題は、コードをリファクタリングし、一方のリテラルの値を変更してもう一方のリテラルを忘れている場合に発生します(例では、隣り合っているためそうではありませんが、以前に見たことがあります)。

コンパイラができるどんな最適化でも、人間はそれを台無しにすることができます:)

于 2010-06-28T10:09:40.807 に答える
6
  1. はい、GCCの場合、他の人にも当てはまるはずです
  2. たぶんGNUリンカーについてはそうです(-fmerge-constants、-fmerge-all-constantsを参照)
  3. いいえ
  4. わからない
于 2010-06-28T10:22:08.573 に答える
4

私はそのパターンを見るのは非常に残念です-誰かが一方のリテラルを変更せずに他方を変更した場合はどうなりますか? 引き抜く必要があります。かなり小さな名前付き定数を作成します。

何らかの理由で、または実際に質問に答えることができないと仮定すると: (少なくとも、逸話的に)。

C で同様のプログラムを作成し、GCC 4.4.3 でコンパイルしました。結果の実行可能ファイルに定数文字列が 1 回だけ表示されました。

編集:簡単なテストとして役立つかもしれないので、ここに私がテストしたコードがあります...

#include <stdlib.h>
#include <string.h>
#include <stdio.h>

main(){
    char *n = (char*)malloc(sizeof("teststring"));
    memcpy((void*)n, "teststring", sizeof("teststring"));
    printf("%s\n", n);
}

そして、文字列が何回出現したかを確認するために使用したコマンドは次のとおりです...

strings a.out|grep teststring

ただし、可能であれば、エラーが発生しにくいコーディング方法を使用することを検討してください。

于 2010-06-28T10:15:29.060 に答える
4

小さなサンプルコードを書いてコンパイルしました:

void func (void)
{
    char ps1[128];
    char ps2[128];

    strcpy(ps1, "string_is_the_same");
    strcpy(ps2, "string_is_the_same");

    printf("", ps1, ps2);
}

その結果、最適化を行わなくても、アセンブラー ファイルにはリテラル "string_is_the_same" のインスタンスが 1 つしかありません。ただし、これらの文字列が別のファイルに配置されて複製されていないかどうかはわかりません->別のオブジェクトファイル。

于 2010-06-28T10:25:09.363 に答える