Anteckning
Åtkomst till den här sidan kräver auktorisering. Du kan prova att logga in eller ändra kataloger.
Åtkomst till den här sidan kräver auktorisering. Du kan prova att ändra kataloger.
I det här dokumentet beskrivs några metodtips för att optimera C++-program i Visual Studio.
Alternativ för kompilator och länkare
Profilstyrd optimering
Visual Studio stöder profilstyrd optimering (PGO). Den här optimeringen använder profildata från träningskörningar av en instrumenterad version av ett program för att driva senare optimering av programmet. Det kan vara tidskrävande att använda PGO, så det kanske inte är något som alla utvecklare använder, men vi rekommenderar att du använder PGO för den slutliga versionen av en produkt. Mer information finns iProfile-Guided optimeringar.
Dessutom har hela programoptimeringen (även kallat länktidskodgenerering) och /O1- och /O2-optimeringarna förbättrats. I allmänhet är ett program som kompilerats med något av dessa alternativ snabbare än samma program som kompilerats med en tidigare kompilator.
Mer information finns i /GL (Helt programoptimering) och /O1, /O2 (Minimera storlek, Maximera hastighet).
Vilken optimeringsnivå som ska användas
Om det alls är möjligt bör de slutliga versionsversionerna kompileras med profilstyrda optimeringar. Om det inte går att skapa med PGO, oavsett om det beror på otillräcklig infrastruktur för att köra instrumenterade versioner eller inte har åtkomst till scenarier, föreslår vi att du skapar med optimering av hela programmet.
Växeln /Gy är också mycket användbar. Den genererar en separat COMDAT för varje funktion, vilket ger länkaren större flexibilitet när det gäller att ta bort orefererade COMDAT och COMDAT-sammanslagning. Den enda nackdelen med att använda /Gy är att det kan orsaka problem vid felsökning. Därför rekommenderas det vanligtvis att använda den. För mer information, se /Gy (Aktivera Function-Level länkning).
För länkning i 64-bitarsmiljöer rekommenderar vi att du använder länkningsalternativet /OPT:REF,ICF och i 32-bitarsmiljöer /OPT:REF rekommenderas. Mer information finns i /OPT (Optimeringar).
Vi rekommenderar också starkt att du genererar felsökningssymboler, även med optimerade versionsbyggen. Det påverkar inte den genererade koden, och det gör det mycket enklare att felsöka ditt program om det behövs.
Flyttalsväxlar
Kompilatoralternativet /Op har tagits bort och följande fyra kompilatoralternativ som hanterar flyttalsoptimeringar har lagts till:
| Alternativ | Beskrivning |
|---|---|
/fp:precise |
Detta är standardrekommendationsen och bör användas i de flesta fall. |
/fp:fast |
Rekommenderas om prestanda är av yttersta vikt, till exempel i spel. Detta resulterar i den snabbaste prestandan. |
/fp:strict |
Rekommenderas om exakta undantag för flyttals och IEEE-beteende önskas. Detta resulterar i långsammaste prestanda. |
/fp:except[-] |
Kan användas tillsammans med /fp:strict eller /fp:precise, men inte /fp:fast. |
Mer information finns i /fp (Ange Floating-Point beteende).
Optimeringsdeklarationer
I det här avsnittet tittar vi på två declspecs som kan användas i program för att hjälpa prestanda: __declspec(restrict) och __declspec(noalias).
Declspec restrict kan endast tillämpas på funktionsdeklarationer som returnerar en pekare, till exempel __declspec(restrict) void *malloc(size_t size);
Declspec restrict används för funktioner som returnerar icke-aliaserade pekare. Det här nyckelordet används för implementeringen av malloc C-Runtime-biblioteket eftersom det aldrig returnerar ett pekarvärde som redan används i det aktuella programmet (om du inte gör något olagligt, till exempel när minnet har frigjorts).
Declspec restrict ger kompilatorn mer information för att utföra kompilatoroptimeringar. En av de svåraste sakerna för en kompilator att avgöra är vilka pekare som alias andra pekare, och att använda den här informationen hjälper kompilatorn avsevärt.
Det är värt att påpeka att detta är ett löfte till kompilatorn, inte något som kompilatorn kommer att verifiera. Om programmet använder denna restrict declspec på ett olämpligt sätt kan programmet ha ett felaktigt beteende.
Mer information finns i restrict.
Declspec noalias tillämpas också endast på funktioner och anger att funktionen är en semi-ren funktion. En halvren funktion är en funktion som endast refererar till eller ändrar lokala variabler, argument och indirekta referenser av argument på första nivån. Den här declspec:en är ett löfte till kompilatorn, och om funktionen refererar till globala variabler eller sekundära indirectioner av pekarargument kan kompilatorn generera kod som kan göra att programmet kraschar.
Mer information finns i noalias.
Optimeringspragma
Det finns också flera användbara pragmas för att optimera kod. Den första vi diskuterar är #pragma optimize:
#pragma optimize("{opt-list}", on | off)
Med den här pragman kan du ange en viss optimeringsnivå baserat på funktion för funktion. Detta är idealiskt för de sällsynta tillfällen då programmet kraschar när en viss funktion kompileras med optimering. Du kan använda detta för att inaktivera optimeringar för en enda funktion:
#pragma optimize("", off)
int myFunc() {...}
#pragma optimize("", on)
Mer information finns i optimize.
Inlining är en av de viktigaste optimeringarna som kompilatorn utför och här pratar vi om ett par av de pragmas som hjälper till att ändra det här beteendet.
#pragma inline_recursion är användbart för att ange om du vill att programmet ska kunna infoga ett rekursivt anrop. Som standard är den inaktiverad. För ytlig rekursion av små funktioner kan du aktivera detta. Mer information finns i inline_recursion.
En annan användbar pragma för att begränsa djupet av inlining är #pragma inline_depth. Detta är vanligtvis användbart i situationer där du försöker begränsa storleken på ett program eller en funktion. Mer information finns i inline_depth.
__restrict och __assume
Det finns ett par nyckelord i Visual Studio som kan hjälpa prestanda: __restrict och __assume.
För det första bör det noteras att __restrict och __declspec(restrict) är två olika saker. Även om de är något relaterade, är deras semantik olika.
__restrict är en typkvalificerare, som const eller volatile, men uteslutande för pekartyper.
En pekare som modifieras med __restrict kallas för en __restrict-pekare. En __restrict pekare är en pekare som bara kan nås via __restrict pekaren. En annan pekare kan med andra ord inte användas för att komma åt data som pekas på av __restrict pekaren.
__restrict kan vara ett kraftfullt verktyg för Microsoft C++-optimeraren, men använd det med stor försiktighet. Om den används felaktigt kan optimeraren utföra en optimering som skulle bryta ditt program.
Med __assumekan en utvecklare be kompilatorn att göra antaganden om värdet för en variabel.
Till exempel __assume(a < 5); talar om för optimeraren att variabeln a på den kodraden är mindre än 5. Återigen är detta ett löfte till kompilatorn. Om a är faktiskt 6 vid denna tidpunkt i programmet kan beteendet för programmet när kompilatorn har optimerats inte vara vad du kan förvänta dig.
__assume är mest användbart före switch-uttryck och/eller villkorsuttryck.
Det finns vissa begränsningar för __assume. Först, som __restrict, är det bara ett förslag, så kompilatorn kan ignorera det.
__assume Dessutom fungerar för närvarande endast med varierande ojämlikheter mot konstanter. Det sprider inte symboliska ojämlikheter, till exempel anta(a < b).
Inbyggt stöd
Intrinsics är funktionsanrop där kompilatorn har inbyggd kunskap om anropet, och i stället för att anropa en funktion i ett bibliotek genererar den kod för den funktionen. Rubrikfilen <intrin.h> innehåller alla tillgängliga inbyggda funktioner för var och en av de maskinvaruplattformar som stöds.
Intrinsiska funktioner ger programmeraren möjlighet att fördjupa sig i koden utan att behöva använda assembler. Det finns flera fördelar med att använda intrinsiker:
Koden är mer portabel. Flera av de inbyggda funktionerna är tillgängliga i flera CPU-arkitekturer.
Koden är lättare att läsa eftersom koden fortfarande är skriven i C/C++.
Koden får nytta av kompilatoroptimeringar. När kompilatorn blir bättre förbättras kodgenereringen för de inbyggda funktionerna.
Mer information finns i Kompilatorns inbyggda funktioner.
Undantag
Det finns en prestandapåverkan associerad med användningen av undantag. Vissa begränsningar införs när du använder try-block som hindrar kompilatorn från att utföra vissa optimeringar. På x86-plattformar finns det ytterligare prestandaförsämring från försöksblock på grund av ytterligare tillståndsinformation som måste genereras under kodkörningen. På 64-bitarsplattformarna försämrar försöksblock inte prestanda lika mycket, men när ett undantag utlöses kan processen med att hitta hanteraren och varva ned stacken vara dyr.
Därför rekommenderar vi att du undviker att införa try/catch-block i kod som egentligen inte behöver den. Om du måste använda undantag använder du synkrona undantag om det är möjligt. Mer information finns i Structured Exception Handling (C/C++).
Slutligen kan du bara utlösa undantag för undantagsfall. Om du använder undantag för det allmänna kontrollflödet blir prestandan sannolikt lidande.