Recently I
have faced the problem with Intellisense performance in Visual Studio 2012 for
C++ , caused by Precompiled Headers feature being disabled. After eye-parsing
some pages on the web including MSDN itself, MSDN blogs, stackoverflow and some
other, I have managed to make the thing work fast enough, so that it doesn’t
prevent me from coding. Still I find both the issue as well as the
solution to it to be pretty undocumented and will thus explain my findings here in the
form of a walkthrough.
The last
night I decided to explore Cinder – a C++ library for ‘professional-quality
creative coding’ as they put it on the website. For me this stands for graphics
in the first place, so I wanted to remember how one makes primitives appear on
the screen, leverages power of translation and rotation matrices and so on. I had Visual Studio 2012 Express for Desktop installed already, so
I was sure everything will go pretty well and by midnight there will be something
like a beautiful tetrahedron moving nicely across my display. Unfortunately, the
things have taken a different road.
Soon after
cloning and building the github repository I faced a dread beast. It jumped
at me as soon as I started writing the first method: Intellisense was scanning
included files. Obviously, no hints were available from it while it moved from
0 to 3000-some files parsed. Even worse, until the end of this process, which
took a minute or two, the overall performance of the IDE was awful: it would
hang from time to time, output text with a considerable lag and so on. It would
be affordable should it happen only once – I am a patient person, I would have waited.
In reality, I have found that Visual Studio comes back to this exercise from time to time –
maybe once in 20 minutes, maybe every 5 – such state of affairs turned Sunday-evening
coding into a horror.
The first
article on the web, which was related to my problem, spoke of Precompiled Headers. As this nice entry by Andy Rich explains, Intellisense uses its own kind of PCH to be fast. Andy pointed
out that, in case one experiences problems with Intellisense performance it is
most likely caused by PCHs being not generated or being generated improperly.
In my particular case the feature was turned off, because TinderBox – Cinder's tool for creating Visual Studio projects – doesn’t
enable it by default. However, the process of
enabling it and making Intellisense for C++ nice and workable is not that
straightforward. Below are the concrete steps that I have taken to make Visual
Studio generate and use precompiled headers for my project.
- First of all, we need to tell our project to employ PCH. The corresponding settings are located in the project properties (right click project and choose ‘Properties’) under Configuration Properties -> C/C++ -> Precompiled Headers. There we have only 3 settings, which is pretty good. Precompiled Header is a flag, determining whether the environment will try to create or use (or neither) PCH for the selected piece of solution (HelloCinder project in my case). We want ‘Use’ option (/Yu) here. The next setting is Precompiled Header File – that’s a traditional C++ header which, as I get it, determines what should be there in the PCH and, if included in a particular translation unit, serves to tell environment to use the PCH. The default value is ‘stdafx.h’ and this will satisfy us most of the time. Lastly, Precompiled Header Output File stores the path to the actual precompiled header file, which allows for faster compilation. I’ve set it to the ‘$(SolutionDir)$(TargetName).pch’ which won’t be a good idea in case of multiple projects in solution but is OK for now. (I won't comment the names chosen by Microsoft for these settings, no.)
- If we try to build the project after completing this setup, there will be a little surprise – Studio will spit out something like this: “error C1010: unexpected end of file while looking for precompiled header. Did you forget to add '#include "stdafx.h"' to your source?” What this tells us is that we have to include our "stdafx.h" header in the files with source code to use PCH. Well, let’s include one. (Note: the #include directive corresponding to the PCH must be the first #include in the source file.) Since I had only one .cpp, this stage was easy, although upon doing this I arrived at 2 new errors: “error C1083: Cannot open source file: 'stdafx.cpp': No such file or directory” and “IntelliSense: cannot open source file "stdafx.h"” Whoa, there is something about Intellisense, so we must be on the right way. Neither of the errors is surprising actually: we told VS to use PCH when it sees “stdafx.h”, but we didn’t provide the file itself.
- So the next step is to add an “stdafx.h” header to the project (right click the ‘Header Files’ folder under the project, click ‘Add’, select ‘Header file’ and specify “stdafx.h” as its name – the same as with any other header). The reasonable question would be, what to put in it. Basically, there should go include directives which make life difficult for both Intellisense and compiler – these will determine what the environment would include into the precompiled header. Below are the includes which give me the required Cinder stuff and those, which actually make Intellisense blow up, because there is quite a lot things in there – I placed them into "stdafx.h":
#include "cinder/app/AppNative.h"
#include "cinder/app/RendererDx.h"
#include "cinder/dx/dx.h" - So after adding the header I try to build the project once more and get another error: “error C1083: Cannot open precompiled header file: '{your path here}': No such file or directory”. This way it tries to communicate to me the idea that I asked it to use a PCH, but didn’t explain how should it get it. Frustrating? Maybe a bit, but be patient – we are almost there. To actually produce a precompiled header from our regular “stdafx.h”, we should do some magic. First of all we add an “stdafx.cpp” file containing a single line: #include “stdafx.h”. Then we have to tell VS to produce a PCH from it: we go to the properties of this file (right click on “stdafx.cpp” and chose 'Properties') and navigate to the familiar tab Configuration Properties -> C/C++ -> Precompiled Headers. There we should see the same settings, which we entered for our project on the step 1. Here it is: change the value of the Precompiled Header flag from ‘Use’ to ‘Create’, save and build the project. Normally, this should bring an end to the nightmare – in many cases it will.
- I have faced an additional issue in
the form of the following errors: “error C1076: compiler limit : internal heap
limit reached; use /Zm to specify a higher limit” and “error C3859: virtual
memory range for PCH exceeded; please recompile with a command line option of
'-Zm119' or greater”. Well, at least this time it says what’s the issue and
what can one do about it. It turns out that I have tried to push too much into the
environment’s memory. To fix this we can do precisely what VS suggests:
navigate to the project's Configuration Properties -> C/C++
-> Command Line. There is an Additional Options box at the bottom, where
we can add the -Zm120 option.
After
performing these 5 steps I was finally able to build the project and find a HelloCinder.pch
file in my solution folder. Moreover, this worked like a charm (quite complex
one, in fact) making Intellisense fast and usable through creating its own precompiled headers in the ipch folder. That’s what happiness is!
Concluding,
let’s overview what we have done. First, we instructed Visual Studio to use a
precompiled header for our project in its preferences. Then we told it what
should get included in the PCH by means of the “stdafx.h” header and included
it into our source files to let Studio know that the PCH should actually be
leveraged for them. Finally we had to specify that the environment should
create the precompiled header through setting proper options for the fresh “stdafx.cpp”
file. In the end we had to deal with virtual memory limit using the -Zm command
line option.
I must
admit that there is much that I don’t understand about both precompiled headers
and the way Intellisense uses them. This said, in the above guide I might
have misrepresented something and given advice which is far from best practice.
Still, I pursued the goal of describing a way to deal with Intellisense
performance issues in detail, because I have found it quite difficult to figure
out, despite the presence of the mentioned quite informative article by Andy
Rich and other materials. Hope this helps. I would be glad to see any comments
pointing out the places where I am wrong.
Edit: soon after finishing the post I have found this thorough description of the matter on stackoverflow.
Edit: soon after finishing the post I have found this thorough description of the matter on stackoverflow.
Комментариев нет:
Отправить комментарий