ランタイムライブラリを備えたVC ++内因性関数の使用方法
-
05-10-2019 - |
質問
私はあなたが可能な限り小さなバイナリを生産しようとするそれらの課題の1つに関与しているので、私は私のプログラムを構築しています それなし CまたはC ++ランタイムライブラリ(RTL)。 DLLバージョンまたは静的バージョンにリンクしません。私もそうしません #include
ヘッダーファイル。私はこれをうまく機能させています。
一部のRTL関数 memset()
, 、便利な場合があるので、独自の実装を追加してみました。デバッグビルドでは正常に動作します(コンパイラが生成する場所であっても 暗黙 へ電話する memset()
)。しかし、リリースビルドでは、本質的な関数を定義できないというエラーが発生します。リリースビルドでは、本質的な関数が有効になり、 memset()
本質的です。
本質的なものを使いたいです memset()
私のリリースでは、おそらく私の実装よりも小さく、より小さく、より速いためです。しかし、私はCatch-22のようです。定義しない場合 memset()
, 、リンカーは、それが未定義であると不平を言っています。定義した場合、コンパイラは本質的な関数を定義できないと不満を述べています。
定義、宣言の正しい組み合わせを知っている人はいますか? #pragma
, 、およびコンパイラとリンカーフラグは、RTLのオーバーヘッドを引っ張らずに固有関数を取得しますか?
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
解決
私はついに解決策を見つけたと思います:
まず、ヘッダーファイルで、宣言します 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)を無効にする必要があることです。理由がわかりません。誰かがグローバルな最適化を無効にすることなくこれを行う方法を見つけた場合は、チャイムでお願いします。
他のヒント
VC ++を使用しないように指示するコンパイラフラグがあると確信しています
ランタイムライブラリのソースは、コンパイラとともにインストールされています。必要な/必要な抜粋機能を選択できますが、多くの場合、それらを広範囲に変更する必要があります(必要/必要としない機能や依存関係が含まれているため)。
他のオープンソースランタイムライブラリも利用できますが、カスタマイズが少ない場合があります。
あなたがこれについて本当に真剣であるならば、あなたはアセンブリ言語を知る(そしておそらく使用する)必要があります。
追加するために編集:
コンパイルしてリンクするための新しいテストコードを取得しました。これらは関連する設定です。
Enable Intrinsic Functions: No
Whole Program Optimization: No
組み込みのMemsetのような「コンパイラヘルパー」を抑制するのは、最後のものです。
追加するために編集:
これで分離されたので、Memset.asmから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;
}
動作しますが、図書館のバージョンははるかに高速です。
「サイズ(/o1)を最小化する」または「無効(/od)」に最適化を設定して、コンパイルするリリース構成を取得する必要があると思います。少なくともこれは、2005年のVSで私にとってトリックであったことです。内insicsは速度で設計されているため、他の最適化レベル(速度とフル)で有効になるのは理にかなっています。
関数に少し違うものに名前を付けるだけです。
これは間違いなくVS 2015で動作します:コマンドラインオプション /oi-を追加します。これは、本質的な関数の「いいえ」がスイッチではなく、不特定のものであるためです。 /oi-およびすべての問題はなくなります(プログラム全体の最適化で動作するはずですが、これを適切にテストしていません)。
「通常の」ランタイムライブラリがこれを行う方法は、MEMSETの定義を備えたアセンブリファイルをコンパイルし、ランタイムライブラリにリンクすることです(C: Program Files Microsoft Visual Studio 10.0 VC内またはその周辺にアセンブリファイルを見つけることができます。 crt src intel memset.asm)。そのようなことは、プログラム全体の最適化でもうまく機能します。
また、コンパイラは、いくつかの特別な場合(サイズが一定で小さい場合)でのみMemset本質を使用することに注意してください。通常、あなたが提供するMemset関数を使用するため、最適化されたものを書く場合を除き、おそらくMemset.asmで最適化された関数を使用する必要があります。