Jak używać funkcji wewnętrznych VC++ bez biblioteki run-time
Jestem zaangażowany w jedno z tych wyzwań, w którym próbujesz stworzyć najmniejszy możliwy plik binarny, więc buduję mój program BEZ bibliotek run-time C lub C++ (RTL). Nie linkuję do wersji DLL lub wersji statycznej. Nie mam nawet #include
plików nagłówkowych. Mam to działa dobrze.
Niektóre funkcje RTL, jak memset()
, mogą być użyteczne, więc próbowałem dodać własną implementację. Działa dobrze w kompilacjach debugowania (nawet w tych miejscach, w których kompilator generuje implicit call to memset()
). Ale w Release builds, dostaję błąd mówiąc, że nie mogę zdefiniować wewnętrznej funkcji. Widzisz, w Release buildach, funkcje wewnętrzne są włączone i memset()
jest wewnętrznymi.
Chciałbym użyć intrinsic for memset()
W moich release buildach, ponieważ prawdopodobnie jest wbudowany, mniejszy i szybszy niż moja implementacja. Ale wydaje mi się, że jestem w catch-22. Jeśli nie zdefiniuję memset()
, linker narzeka, że jest niezdefiniowany. Jeśli to zdefiniuję, kompilator skarży się, że nie mogę zdefiniować funkcji wewnętrznej.
Czy ktoś zna właściwą kombinację definicji, deklaracji, #pragma
, oraz flag kompilatora i linkera, aby uzyskać wewnętrzną funkcję bez przeciągania RTL nad głową?
Visual Studio 2008, x86, Windows XP+.
Aby problem był bardziej konkretny:
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;
}
A ja buduję Tak:
cl /c /W4 /WX /GL /Ob2 /Oi /Oy /Gs- /GF /Gy intrinsic.cpp
link /SUBSYSTEM:CONSOLE /LTCG /DEBUG /NODEFAULTLIB /ENTRY:main intrinsic.obj
Jeśli kompiluję z moją implementacją memset()
, otrzymuję kompilator błąd:
error C2169: 'memset' : intrinsic function, cannot be defined
Jeśli skompiluję to bez mojej implementacji memset()
, dostaję błąd linkera:
error LNK2001: unresolved external symbol _memset
6 answers
Myślę, że w końcu znalazłem rozwiązanie:
Najpierw w pliku nagłówkowym zadeklaruj memset()
za pomocą pragmy, Tak:
extern "C" void * __cdecl memset(void *, int, size_t);
#pragma intrinsic(memset)
, który pozwala na wywołanie kodu memset()
. W większości przypadków kompilator wbuduje wewnętrzną wersję.
Po drugie, w oddzielnym pliku implementacji, podaj implementację. Sztuczka uniemożliwiająca kompilatorowi narzekanie na ponowne zdefiniowanie funkcji wewnętrznej polega na pierwszym użyciu innej pragmy. Tak:
#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;
}
To zapewnia implementacja dla tych przypadków, w których optymalizator decyduje się nie używać wersji wewnętrznej.
Wyjątkową wadą jest to, że musisz wyłączyć optymalizację całego programu (/GL i /LTCG). Nie wiem dlaczego. Jeśli ktoś znajdzie sposób, aby to zrobić bez wyłączania global optimization, proszę o kontakt.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-01-16 17:47:26
-
Jestem prawie pewien, że jest flaga kompilatora, który mówi VC++ nie używać intrinsics
Źródło do biblioteki runtime jest instalowane wraz z kompilatorem. Masz do wyboru fragmenty funkcji, które chcesz / potrzebujesz, choć często będziesz musiał je szeroko modyfikować(ponieważ zawierają one funkcje i/lub zależności, których nie chcesz / potrzebujesz).
Dostępne są również inne biblioteki runtime open source, które mogą wymagać mniej Personalizacja.
Jeśli myślisz o tym poważnie, musisz znać (i być może używać) język asemblacji.
Edited to add:
Mam Twój nowy kod testowy do skompilowania i linkowania. Są to odpowiednie ustawienia:
Enable Intrinsic Functions: No
Whole Program Optimization: No
To ten ostatni, który tłumi "pomocników kompilatora", takich jak wbudowany memset.
Edited to add:
Teraz, gdy jest odsprzęgnięty, możesz skopiować kod asm z memsetu.asm do twojego program -- ma jedno globalne odniesienie, ale możesz to usunąć. Jest wystarczająco duży, aby był nie inlined, chociaż jeśli usuniesz wszystkie sztuczki, których używa, aby zyskać prędkość, możesz być w stanie zrobić go wystarczająco małym.
Wzięłam powyższy przykład i zastąpiłam memset()
tym:
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;
}
To działa, ale wersja biblioteki jest znacznie szybsza.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-05-31 20:01:27
Myślę, że musisz ustawić optymalizację na " Minimalizuj Rozmiar (/O1)" lub "wyłączony (/Od)", Aby uzyskać konfigurację Wydania do kompilacji; przynajmniej to, co mi się udało Z VS 2005. Intrinsics są zaprojektowane dla prędkości, więc ma sens, że będą one włączone dla innych poziomów optymalizacji(prędkość i pełne).
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-05-30 18:04:37
Po prostu nazwij funkcję czymś nieco innym.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-05-31 17:35:51
To zdecydowanie działa z VS 2015: Dodaj opcję wiersza poleceń / Oi-. Działa to, ponieważ "nie" na wewnętrznych funkcjach nie jest przełącznikiem, jest nieokreślone. / Oi - i wszystkie Twoje problemy znikną (powinno działać z optymalizacją całego programu, ale nie testowałem tego poprawnie).
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-02-12 20:48:03
Sposób, w jaki "zwykła" biblioteka runtime robi to poprzez kompilację pliku asemblera z definicją memset i połączenie go z biblioteką runtime (plik asemblera można znaleźć w lub wokół niego C:\Program Files \ Microsoft Visual Studio 10.0 \ VC \ crt \ src \ intel \ memset.asm). Takie rzeczy działają dobrze nawet przy optymalizacji całego programu.
Zauważ również, że kompilator będzie używał memset intrinsic tylko w niektórych szczególnych przypadkach (gdy rozmiar jest stały i mały?). Zwykle będzie użyj funkcji memset dostarczonej przez Ciebie, więc prawdopodobnie powinieneś użyć zoptymalizowanej funkcji w memset.asm, chyba, że masz zamiar napisać coś tak zoptymalizowanego.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-06-06 23:33:35