36

私は、可能な限り最小のバイナリを生成しようとする課題の 1 つに関与しているため、C または C++ ランタイム ライブラリ (RTL)を使用せずにプログラムをビルドしています。DLL バージョンまたは静的バージョンにはリンクしません。#includeヘッダーファイルさえありません。私はこれでうまくいきました。

などの一部の RTL 関数はmemset()便利なので、独自の実装を追加してみました。デバッグ ビルドでは正常に動作します (コンパイラが への暗黙的な呼び出しを生成する場所でもmemset())。しかし、リリース ビルドでは、組み込み関数を定義できないというエラーが表示されます。リリース ビルドでは、組み込み関数が有効になっておりmemset()、組み込み関数です。

組み込みの for をリリース ビルドで使用したいと考えてmemset()います。これはおそらくインライン化されており、私の実装よりも小さくて高速だからです。しかし、私はキャッチ22のようです。を定義しないmemset()と、リンカーはそれが未定義であると不平を言います。定義すると、コンパイラは、組み込み関数を定義できないと文句を言います。

#pragmaRTL オーバーヘッドを引き込まずに組み込み関数を取得するための定義、宣言、およびコンパイラとリンカー フラグの正しい組み合わせを知っている人はいますか?

Visual Studio 2008、x86、Windows XP+。

問題をもう少し具体的にするには:

extern "C" void * __cdecl memset(void *, int, size_t);

#ifdef IMPLEMENT_MEMSET
void * __cdecl memset(void *pTarget, int value, size_t cbTarget) {
    char *p = reinterpret_cast<char *>(pTarget);
    while (cbTarget > 0) {
        *p++ = static_cast<char>(value);
        --cbTarget;
    }
    return pTarget;
}
#endif

struct MyStruct {
    int foo[10];
    int bar;
};

int main() {
    MyStruct blah;
    memset(&blah, 0, sizeof(blah));
    return blah.bar;
}

そして、私は次のように構築します:

cl /c /W4 /WX /GL /Ob2 /Oi /Oy /Gs- /GF /Gy intrinsic.cpp
link /SUBSYSTEM:CONSOLE /LTCG /DEBUG /NODEFAULTLIB /ENTRY:main intrinsic.obj

の実装でコンパイルすると、コンパイルmemset()エラーが発生します。

error C2169: 'memset' : intrinsic function, cannot be defined

の実装なしでこれをコンパイルするとmemset()、リンカ エラーが発生します。

error LNK2001: unresolved external symbol _memset
4

7 に答える 7

24

私は最終的に解決策を見つけたと思います:

まず、ヘッダー ファイルで、次のmemset()ようにプラグマで宣言します。

extern "C" void * __cdecl memset(void *, int, size_t);
#pragma intrinsic(memset)

これにより、コードで を呼び出すことができますmemset()。ほとんどの場合、コンパイラは組み込みバージョンをインライン化します。

次に、別の実装ファイルで、実装を提供します。コンパイラが組み込み関数の再定義について不平を言うのを防ぐ秘訣は、最初に別のプラグマを使用することです。このような:

#pragma function(memset)
void * __cdecl memset(void *pTarget, int value, size_t cbTarget) {
    unsigned char *p = static_cast<unsigned char *>(pTarget);
    while (cbTarget-- > 0) {
        *p++ = static_cast<unsigned char>(value);
    }
    return pTarget;
}

これにより、オプティマイザーが組み込みバージョンを使用しないと決定した場合の実装が提供されます。

顕著な欠点は、プログラム全体の最適化 (/GL および /LTCG) を無効にする必要があることです。理由はわかりません。グローバル最適化を無効にせずにこれを行う方法を誰かが見つけた場合は、協力してください。

于 2010-05-31T19:33:10.470 に答える
5
  1. VC++に組み込み関数を使用しないように指示するコンパイラフラグがあると確信しています

  2. ランタイム ライブラリのソースは、コンパイラと共にインストールされます。必要な/必要な関数を抜粋することはできますが、多くの場合、それらを大幅に変更する必要があります (必要でない/必要でない機能や依存関係が含まれているため)。

  3. 他のオープン ソース ランタイム ライブラリも利用可能で、カスタマイズの必要性が少ない可能性があります。

  4. これについて本当に真剣に考えているなら、アセンブリ言語を知っている (そしておそらく使用している) 必要があります。

追加するために編集:

コンパイルしてリンクする新しいテスト コードを入手しました。関連する設定は次のとおりです。

Enable Intrinsic Functions: No
Whole Program Optimization: No

組み込みの memset のような「コンパイラ ヘルパー」を抑制するのは、最後の 1 つです。

追加するために編集:

分離されたので、asm コードを memset.asm からプログラムにコピーできます。1 つのグローバル参照がありますが、それを削除できます。インライン化されていないほど十分に大きいですが、速度を上げるために使用するすべてのトリックを削除すると、そのために十分に小さくすることができる場合があります。

上記の例を取り上げ、これを次のように置き換えましたmemset()

void * __cdecl memset(void *pTarget, char value, size_t cbTarget) {
    _asm {
    push ecx
    push edi

    mov al, value
    mov ecx, cbTarget
    mov edi, pTarget
    rep stosb

    pop edi
    pop ecx
    }
    return pTarget;
}

動作しますが、ライブラリのバージョンの方がはるかに高速です。

于 2010-05-30T17:58:43.410 に答える
1

リリース構成をコンパイルするには、最適化を「サイズの最小化(/ O1)」または「無効(/ Od)」に設定する必要があると思います。少なくとも、これがVS 2005でのトリックでした。組み込み関数は速度を重視して設計されているため、他の最適化レベル(速度とフル)で有効になるのは理にかなっています。

于 2010-05-30T18:04:37.650 に答える
0

関数に少し違う名前を付けてください。

于 2010-05-31T17:35:51.900 に答える
-1

「通常の」ランタイム ライブラリがこれを行う方法は、memset の定義を使用してアセンブリ ファイルをコンパイルし、それをランタイム ライブラリにリンクすることです (アセンブリ ファイルは C:\Program Files\Microsoft Visual Studio 10.0\VC 内またはその周辺にあります)。 \crt\src\intel\memset.asm)。そのようなことは、プログラム全体の最適化でも問題なく機能します。

また、コンパイラは memset 組み込み関数を特別な場合 (サイズが一定で小さい場合) にのみ使用することに注意してください。通常は提供された memset 関数を使用するため、最適化されたものを記述する場合を除き、おそらく memset.asm で最適化された関数を使用する必要があります。

于 2010-08-30T00:16:22.063 に答える