{"id":46,"date":"2009-09-17T23:02:59","date_gmt":"2009-09-18T06:02:59","guid":{"rendered":"http:\/\/gameangst.com\/?p=46"},"modified":"2009-09-17T23:02:08","modified_gmt":"2009-09-18T06:02:08","slug":"minimizing-code-bloat-for-faster-builds-and-smaller-executables","status":"publish","type":"post","link":"http:\/\/gameangst.com\/?p=46","title":{"rendered":"Minimizing Code Bloat for Faster Builds and Smaller Executables"},"content":{"rendered":"<p>No matter how much code you have, somebody always wants more. \u00a0That&#8217;s certainly been the case with every game and every engine I&#8217;ve worked on. \u00a0My good friend <a href=\"http:\/\/gamearchitect.net\/about\/\">Kyle<\/a>, who has for years been tracking all sorts of wonderful statistics about the development of <a href=\"http:\/\/gamearchitect.net\/category\/despair-engine\/\">Despair Engine<\/a>, recently announced that we&#8217;d surpassed 1.5 million lines of principle engine code. \u00a0That doesn&#8217;t count build systems, tools, tests, middleware, or scripts; that&#8217;s 100% Despair-original C++ code the compiler has to wade through every time we build the game.<\/p>\n<p>As if the code we write weren&#8217;t bad enough, our engine is particularly adept at <a href=\"http:\/\/gameangst.com\/?p=107\">generating code<\/a>. \u00a0Those 1.5 MLOC result in a hefty 37 megabyte executable on the Xbox 360. \u00a0Kyle estimates that we&#8217;ll exceed 2 million lines of code before the end of this console generation. \u00a0We already have an executable that can&#8217;t fit into main memory on a PS2 or a Gamecube. \u00a0By 2014 it won&#8217;t fit onto an Xbox or Wii either. \u00a0I don&#8217;t think we&#8217;re in danger of exceeding the capacity of a PS3 or Xbox 360, but the prospect is enough to keep me up at nights.<\/p>\n<p>Looking back at the graphs of our executable size and build and link times, I&#8217;m proud to see a few dips punctuating the otherwise consistent upward march. \u00a0At least a few of those dips correspond to weeks when I turned my sleepless nights into successful assaults on code bloat. \u00a0What&#8217;s particularly interesting about those dips is that they don&#8217;t correspond to any drops in our code line count. \u00a0In my biggest success I reduced the size of our executable by 25% or over 7 megabytes. \u00a0At the same time I cut our link times by 19%, and yet I did it all without removing a single line of code! \u00a0How?<\/p>\n<p>The first place to start when trimming a codebase is figuring out where to look. \u00a0In an engine of 1.5 million lines of code there are a lot of places to hide. \u00a0My strategy when dealing with problems like this is to gather as much data as possible into a format that is amenable to analysis. \u00a0For code this means extracting symbols from the compiled binaries along with their size, type, source file, and any other details you can get your hands on. \u00a0Once you have a dump of all the symbols in your compiled code, it is a pretty simple matter to write a script to collate it and generate whatever statistics you find useful.<\/p>\n<p>There are two methods I use for extracting data from compiled code. \u00a0The first is a\u00a0quick and handy utility called DumpBin. \u00a0<a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/c1h23y6c(VS.71).aspx\" target=\"_blank\">DumpBin<\/a> is part of the Visual Studio toolchain and is available for all Microsoft targets. \u00a0There are similar tools available for every compiler I&#8217;ve encountered. \u00a0The gcc equivalents, for example, are <a href=\"http:\/\/linux.die.net\/man\/1\/objdump\">objdump<\/a> and <a href=\"http:\/\/linux.die.net\/man\/1\/nm\">nm<\/a>. \u00a0These tools are useful because they can work with just about any compiled binary. \u00a0They target standard binary formats like <a href=\"http:\/\/en.wikipedia.org\/wiki\/COFF\">PE\/COFF<\/a> and <a href=\"http:\/\/en.wikipedia.org\/wiki\/Executable_and_Linkable_Format\">ELF<\/a>, and consequently they work on object files, library files, executables and DLLs. \u00a0The ability to extract information from intermediate files in the build process is essential if you&#8217;re worried as much about build times as you are about final memory usage.<\/p>\n<p>My preferred way to use DumpBin is to specify the <em>\/headers<\/em> option to extract COMDATs from object files compiled with the <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/xsa71f43(VS.80).aspx\" target=\"_blank\">\/Gy<\/a> option. \u00a0For example:<\/p>\n<p style=\"padding-left: 30px; \"><em>for \/r . %n in (*.obj) do DumpBin \/headers &#8220;%n&#8221; &gt;&gt; all_headers.txt<\/em><\/p>\n<p><em><\/em>A portion of the output from such a dump looks like this:<\/p>\n<pre>SECTION HEADER #14E\r\n   .text name\r\n       0 physical address\r\n       0 virtual address\r\n       7 size of raw data\r\n   22D6B file pointer to raw data (00022D6B to 00022D71)\r\n   22D72 file pointer to relocation table\r\n       0 file pointer to line numbers\r\n       1 number of relocations\r\n       0 number of line numbers\r\n60501020 flags\r\n         Code\r\n         COMDAT; sym= \"public: virtual __thiscall Despair::Reflection::Object::~Object(void)\" (??1Object@Reflection@Despair@@UAE@XZ)\r\n         16 byte align\r\n         Execute Read\r\n\r\nSECTION HEADER #14F\r\n   .text name\r\n       0 physical address\r\n       0 virtual address\r\n       5 size of raw data\r\n   230B1 file pointer to raw data (000230B1 to 000230B5)\r\n       0 file pointer to relocation table\r\n       0 file pointer to line numbers\r\n       0 number of relocations\r\n       0 number of line numbers\r\n60501020 flags\r\n         Code\r\n         COMDAT; sym= \"public: int __thiscall Despair::BroadcasterReceipt::AddRef(void)\" (?AddRef@BroadcasterReceipt@Despair@@QAEHXZ)\r\n         16 byte align\r\n         Execute Read<\/pre>\n<p>As you can see, the result is an easily parsable list of packaged functions with symbol names and sizes clearly specified. \u00a0As great as the <em>\/headers <\/em>dump is, it isn&#8217;t perfect. \u00a0Data symbols aren&#8217;t packaged as COMDATs and although DumpBin can extract data from executables, executables don&#8217;t contain COMDATs so there isn&#8217;t much to see.<\/p>\n<p>To analyze the contents of an executable, a second approach is needed. \u00a0My solution is to reference the debugging information provided by the linker. \u00a0With Microsoft&#8217;s tools, debugging information is stored in a <a href=\"http:\/\/en.wikipedia.org\/wiki\/Program_database\">PDB<\/a> file that accompanies the executable. \u00a0Similar to DumpBin&#8217;s <em>\/headers<\/em> dump, the PDB contains information about symbols in the compiled binary. \u00a0Unlike the <em>\/headers<\/em> dump, however, the PDB file contains data as well as code symbols, and the information it contains is post-linker optimization which is important if you&#8217;re more worried about memory consumption than compile and link times.<\/p>\n<p>There aren&#8217;t any standard tools for dumping the contents of a PDB, but there is a DLL provided with the Microsoft toolchain that makes it pretty easy to write your own. \u00a0I&#8217;ve used the <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/x93ctkx8(VS.80).aspx\">Debug Interface Access SDK<\/a> included with Microsoft Visual Studio to plumb the contents of an executable and generate symbol tables that can be analyzed alongside a COMDAT dump.<\/p>\n<p>In an upcoming series of articles I&#8217;m going to explore the most common causes of C++ code bloat and take a closer look at how you can use DumpBin and the PDB to identify and remove the worst offenders from your code. \u00a0When I&#8217;m done I&#8217;ll share some sample code which you can use to assess the level of code bloat in your own engines.<\/p>\n<p>First up, <a href=\"http:\/\/gameangst.com\/?p=226\">template overspecialization<\/a>.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>No matter how much code you have, somebody always wants more. \u00a0That&#8217;s certainly been the case with every game and every engine I&#8217;ve worked on. \u00a0My good friend Kyle, who has for years been tracking all sorts of wonderful statistics about the development of Despair Engine, recently announced that we&#8217;d surpassed 1.5 million lines of [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":[],"categories":[1],"tags":[8,14,11,7],"_links":{"self":[{"href":"http:\/\/gameangst.com\/index.php?rest_route=\/wp\/v2\/posts\/46"}],"collection":[{"href":"http:\/\/gameangst.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/gameangst.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/gameangst.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"http:\/\/gameangst.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=46"}],"version-history":[{"count":24,"href":"http:\/\/gameangst.com\/index.php?rest_route=\/wp\/v2\/posts\/46\/revisions"}],"predecessor-version":[{"id":278,"href":"http:\/\/gameangst.com\/index.php?rest_route=\/wp\/v2\/posts\/46\/revisions\/278"}],"wp:attachment":[{"href":"http:\/\/gameangst.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=46"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/gameangst.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=46"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/gameangst.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=46"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}