Megjegyzés
Az oldalhoz való hozzáféréshez engedély szükséges. Megpróbálhat bejelentkezni vagy módosítani a címtárat.
Az oldalhoz való hozzáféréshez engedély szükséges. Megpróbálhatja módosítani a címtárat.
Ez a dokumentum a C++ programok Visual Studióban való optimalizálásának ajánlott eljárásait ismerteti.
A Fordító és a Linker beállításai
Profilvezérelt optimalizálás
A Visual Studio támogatja a profilalapú optimalizálást (PGO). Ez az optimalizálás az alkalmazás egy rendszerezett verziójának betanítási végrehajtásából származó profiladatokat használja az alkalmazás későbbi optimalizálásának hajtóerejéhez. A PGO használata időigényes lehet, ezért előfordulhat, hogy nem minden fejlesztő használ ilyet, de azt javasoljuk, hogy a termék végleges kiadási buildjéhez használja a PGO-t. További információ: Profile-Guided optimalizálás.
Emellett a teljes programoptimalizálás (más néven Link Time Code Generation) és /O1 az /O2 optimalizálás is javult. Általánosságban elmondható, hogy egy ilyen lehetőséggel összeállított alkalmazás gyorsabb lesz, mint ugyanaz az alkalmazás, amely egy korábbi fordítóval van lefordítva.
További információt a (Teljes programoptimalizálás) és a (Méret minimalizálása, Sebesség maximalizálása) című témakörben talál/GL./O1/O2
A használni kívánt optimalizálási szint
Ha egyáltalán lehetséges, a végleges kiadási buildeket profilvezérelt optimalizálással kell összeállítani. Ha a PGO-val nem lehet építeni, akár a rendszerezett buildek futtatásához szükséges infrastruktúra hiánya, akár a forgatókönyvekhez való hozzáférés hiánya miatt, javasoljuk a teljes programoptimalizálással való építést.
A /Gy kapcsoló is nagyon hasznos. Minden függvényhez külön COMDAT-t hoz létre, nagyobb rugalmasságot biztosítva a csatolónak a nem hivatkozott COMDAT-ok eltávolításához és a COMDAT-összecsukáshoz. A használat /Gy egyetlen hátránya, hogy hibakereséskor problémákat okozhat. Ezért általában ajánlott használni. További információ: /Gy (Function-Level csatolás engedélyezése).
A 64 bites környezetekben való csatoláshoz ajánlott a /OPT:REF,ICF linker lehetőséget használni, és 32 bites környezetekben /OPT:REF ajánlott. További információ: /OPT (Optimalizálás).
Emellett kifejezetten ajánlott hibakeresési szimbólumok létrehozása, még optimalizált kiadási buildek esetén is. Ez nem befolyásolja a létrehozott kódot, és sokkal egyszerűbbé teszi az alkalmazás hibakeresését, ha szükséges.
Lebegőpontos kapcsolók
A /Op fordítóbeállítás el lett távolítva, és a következő négy fordítóbeállítással bővült a lebegőpontos optimalizálás:
| Lehetőség | Leírás |
|---|---|
/fp:precise |
Ez az alapértelmezett javaslat, amelyet a legtöbb esetben érdemes használni. |
/fp:fast |
Ajánlott, ha a teljesítmény rendkívül fontos, például játékokban. Ez a leggyorsabb teljesítményt eredményezi. |
/fp:strict |
Ajánlott, ha pontos lebegőpontos kivételekre és IEEE-viselkedésre van szükség. Ez a leglassabb teljesítményt eredményezi. |
/fp:except[-] |
Használható együtt /fp:strict vagy /fp:precise, de nem /fp:fast. |
További információt a (Floating-Point viselkedésének megadása) című témakörben talál/fp.
Optimalizálási declspecs
Ebben a szakaszban két olyan declspecet tekintünk meg, amelyeket a programokban a teljesítmény javítására használhatunk: __declspec(restrict) és __declspec(noalias).
A restrict deklaráció csak olyan függvénydeklarációkra alkalmazható, amelyek mutatót adnak vissza, például __declspec(restrict) void *malloc(size_t size);
A restrict declspec olyan függvényeken használatos, amelyek nem megadott mutatókat adnak vissza. Ez a kulcsszó a C-Runtime Library implementációjához malloc használatos, mivel soha nem ad vissza olyan mutatóértéket, amely már használatban van az aktuális programban (kivéve, ha illegális műveletet végez, például a memória felszabadítása után).
A restrict declspec további információkat nyújt a fordítónak a fordítóoptimalizálások elvégzéséről. A fordítók számára az egyik legnehezebb feladat meghatározni, hogy mely mutatók mutatnak ugyanarra a memóriacímre, és ennek az információnak a használata nagyban segíti a fordítót.
Érdemes kiemelni, hogy ez a fordító ígérete, nem pedig olyasmi, amit a fordító ellenőrizni fog. Ha a program nem megfelelően használja ezt restrict a declspec-et, előfordulhat, hogy a program helytelenül viselkedik.
További információért lásd restrict.
A noalias declspec csak a függvényekre alkalmazható, és azt jelzi, hogy a függvény félig tiszta függvény. A félig tiszta függvény olyan függvény, amely csak a helyi elemekre, argumentumokra és az argumentumok első szintű indirektjeire hivatkozik vagy módosítja azokat. Ez a declspec ígéretet jelent a fordító számára, és ha a függvény globális vagy másodszintű indirekt mutatóargumentumokra hivatkozik, akkor a fordító olyan kódot hozhat létre, amely megszakítja az alkalmazást.
További információért lásd noalias.
Optimalizálási pragmák
A kód optimalizálásához számos hasznos pragma is rendelkezésre áll. Elsőként a következőt fogjuk megvitatni #pragma optimize:
#pragma optimize("{opt-list}", on | off)
Ez a pragma lehetővé teszi egy adott optimalizálási szint függvényenkénti beállítását. Ez ideális azokhoz a ritka esetekhez, amikor az alkalmazás összeomlik, amikor egy adott függvényt optimalizálással állít össze. Ezzel kikapcsolhatja az optimalizálást egyetlen függvény esetében:
#pragma optimize("", off)
int myFunc() {...}
#pragma optimize("", on)
További információért lásd optimize.
Az inlining az egyik legfontosabb optimalizálás, amelyet a fordító végez, és itt néhány olyan pragmáról beszélünk, amelyek segítenek módosítani ezt a viselkedést.
#pragma inline_recursion hasznos annak megadásához, hogy szeretné-e, hogy az alkalmazás képes legyen egy rekurzív hívást inline-olni. Alapértelmezés szerint ki van kapcsolva. A kis függvények sekély rekurziója esetén ezt bekapcsolhatja. További információért lásd inline_recursion.
Egy másik hasznos pragma az inlinelés mélységének korlátozására a #pragma inline_depth. Ez általában olyan helyzetekben hasznos, amikor egy program vagy függvény méretét szeretné korlátozni. További információért lásd inline_depth.
__restrict és __assume
A Visual Studióban néhány olyan kulcsszó található, amelyek segíthetnek a teljesítményben: __restrict és __assume.
Először is meg kell jegyezni, hogy két __restrict__declspec(restrict) különböző dolog. Bár némileg rokonok, szemantikájaik különböznek.
__restrict típus-minősítő, például const vagy volatile, de kizárólag mutatótípusokhoz.
A módosított __restrict mutatót __restrict mutatónak nevezzük. A __restrict mutató olyan mutató, amely csak a __restrict mutatón keresztül érhető el. Más szóval egy másik mutató nem használható a __restrict mutató által mutatott adatok eléréséhez.
__restrict a Microsoft C++ optimalizáló hatékony eszköze lehet, de nagy körültekintéssel használja. Helytelen használat esetén az optimalizáló olyan optimalizálásokat hajthat végre, amelyek megszakítják az alkalmazást.
Ezzel __assumea fejlesztő meg tudja mondani a fordítónak, hogy feltételezéseket tegyen bizonyos változók értékéről.
Például __assume(a < 5); azt mondja az optimalizálónak, hogy a kód ezen sorában a változó a kisebb, mint 5. Ez ismét ígéret a fordítónak. Ha a a ebben a programban ezen a ponton valóban 6, akkor a fordító optimalizálása után a program viselkedése lehet, hogy nem az lesz, amit várna.
__assume az utasítások és/vagy feltételes kifejezések váltása előtt a legcélrasztosabb.
Bizonyos korlátozások a következőkre __assumevonatkoznak: . Először is, a __restrict csupán egy javaslatként van megadva, így a fordító szabadon figyelmen kívül hagyhatja.
__assume Emellett jelenleg csak az állandókkal szembeni változó egyenlőtlenségekkel működik. Nem propagálja a szimbolikus egyenlőtlenségeket, például feltételezi(a < b).
Belső támogatás
Az intrinsics függvényhívások, amelyekben a fordító belső ismeretekkel rendelkezik a hívásról, és ahelyett, hogy függvényt hívna meg egy könyvtárban, a függvény kódját bocsátja ki. Az intrin.h< fejlécfájl >tartalmazza az összes elérhető belső elemet az egyes támogatott hardverplatformokhoz.
Az intrinsics lehetővé teszi a programozó számára, hogy az assembly nyelv használata nélkül mélyebben belemenjen a kódban. Az intrinsics használatának számos előnye van:
A kód hordozhatóbb. Az intrinsics számos része több CPU-architektúrán is elérhető.
A kód könnyebben olvasható, mivel a kód továbbra is C/C++-ban van megírva.
A kód a fordítóoptimalizálás előnyeit élvezi. Ahogy a fordító egyre jobb lesz, az intrinsics kódgenerációja javul.
További információ: Compiler Intrinsics.
Kivételek
A kivételek használata teljesítménycsökkenéssel jár. Bizonyos korlátozások akkor lépnek fel, ha olyan próbablokkokat használ, amelyek gátolják a fordítót bizonyos optimalizálások végrehajtásában. Az x86-platformokon további teljesítménycsökkenés tapasztalható a próbablokkok miatt, mivel a kódfuttatás során létre kell hoznia további állapotinformációkat. A 64 bites platformokon a kipróbálási blokkok nem csökkentik annyira a teljesítményt, de ha kivételt vetnek ki, a kezelő megkeresése és a verem visszatekerése költséges lehet.
Ezért javasolt elkerülni a try/catch blokkok bevezetését olyan kódba, amelynek valóban nincs rá szüksége. Ha kivételeket kell használnia, ha lehetséges, használjon szinkron kivételeket. További információ: Strukturált kivételkezelés (C/C++).
Végül pedig csak kivételes esetekben kivételeket kell kivenni. Az általános vezérlési folyamat kivételeinek használata valószínűleg a teljesítmény romlását eredményezi.