2

I am writing a compiler that generates C++ code at the end. I am currently wondering what is compiling faster.

First notes about the compiler:

  1. I don't have any classes\structs, they are optimized inside the functions.
  2. I don't include anything like #include <vector>, when I have to use functions such as printf from libraries then I manually put prototype. (The compiler do it manually.)

I have two options:

Option #1:

//.h
#include "A.h"
#include "B.h"

int function( /* parameters */ );
//.cpp
int function( /* parameters */ ) {
    // code
}

Every function has it's own source and header. Advantages:

  1. I can make the compiler comment out the includes that includes file that are included before it. For example if #include "B.h"'s content is included in #include "A.h" then I will can make it comment out the line #include "B.h". (Saves file reads.)
  2. I can recognize unchanged methods/functions/files (When I regenerate my code and it can find exact files from before.) and recycle their object files. (Saves object compiling.)

Option #2:

int function( /* parameters */ );
int function2( /* parameters */ );
int function3( /* parameters */ );
// ...
int function( /* parameters */ ) {
    // code
}
// ...

All functions are once defined (those prototypes at the top) and compiled in that single file.

Advantages:

  1. Single sequential read from the disk. (No hierarchy of including and multiple including from different objects.)
  2. Single object to compile, excluding libraries.

At single glance the looks like option #1 is faster, but some folks said they tried the second and it gave their project a boost in compiling time. They didn't compare both options, and didn't gave any proof for it.

Can I get explanation for which one is faster rather than benchmarks?

LyingOnTheSky
  • 2,844
  • 1
  • 14
  • 33
  • 2
    Benchmark and find out. Anything else is mostly speculation. – Colonel Thirty Two May 10 '15 at 18:28
  • @ColonelThirtyTwo I am sure someone can "give word" but why option is faster than the other, and not just benchmark. – LyingOnTheSky May 10 '15 at 18:29
  • option 3: implement [modules](http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2014/n4214.pdf) – bolov May 10 '15 at 18:31
  • @Bolov: you might be right, but then it is not (yet) standard C++ – Basile Starynkevitch May 10 '15 at 18:32
  • @BasileStarynkevitch It won't be, I think some knowledge about compiling non-CFG code can give a lot of hints about which one is faster. I know abit about it, but I still can't decide. (Both of them almost equally for me in theory.) – LyingOnTheSky May 10 '15 at 18:33
  • _"I don't have any classes\structs, they are optimized inside the functions."_ Eh? Can you explain how refusing to use data types constitutes "optimisation"? – Lightness Races in Orbit May 10 '15 at 18:42
  • _"I don't include anything like #include , when I have to use functions such as printf from libraries then I manually put prototype"_ Heavens why?!?! – Lightness Races in Orbit May 10 '15 at 18:43
  • @LightnessRacesinOrbit This is off-topic :), but whatever. I have things such as `string` and sometimes they don't come in allocation of memory of `struct { uint c; char *d; }`, they sometimes come as only `char *d` of that struct, or sometimes they are inlined in some way and etc. (It's optimizations across functions without inlining them.) – LyingOnTheSky May 10 '15 at 18:45
  • @LightnessRacesinOrbit I am compiling CUDA in it, I don't need `` and etc, and so I want to improve compiling time I need to sit and write my own APIs. (Which is not a lot of work I think) – LyingOnTheSky May 10 '15 at 18:46
  • 1
    @LyingOnTheSky: At least include the C headers if you use the functions from the C headers. Declaring that stuff yourself is the path to ruin. – Lightness Races in Orbit May 10 '15 at 19:07
  • @LightnessRacesinOrbit NVCC Compiler is quiet slow. I just do it once anyway, the compiler manages it by itself. (Define a prototype for functions it uses.) My compiler is mostly for computing, a lot of functions are redundant for me. – LyingOnTheSky May 10 '15 at 19:41
  • @LyingOnTheSky: What kind of compiler are you coding? What kind of source language? Please edit your question to improve it! – Basile Starynkevitch May 10 '15 at 19:55
  • @BasileStarynkevitch I don't want to make my question too specific, I want it to help future readers too. I am not sure what to add. My compiler is optimizating sequential code to parallel and then compile it with CUDA (helps?). It takes C# code parses and generating code. You I think I should add those? – LyingOnTheSky May 11 '15 at 21:33

2 Answers2

2

C++ is known to be slow to be compiled, notably because standard C++ headers (e.g. <vector>, <map> or other standard containers) are bringing a lot of C++ code (and tokens) thru internal headers.

Are your #include-d headers machine generated, or are they all common to your runtime? You might consider having a single header and pre-compile it (assuming a recent GCC or Clang/LLVM compiler), see this. Then you need to have only one single #include at top of every generated C++ file.

BTW, I am not sure that C++ is a good target language for some compiler. Generating C code (perhaps using Boehm's GC, like Bigloo does) is probably more relevant. Generating C++ code makes sense when you want to fit into some existing API, like I am doing in MELT fitting into GCC internals, and then I don't generate much code using C++ standard templates.

At last, when you generate C (and even more when generating genuine C++) you really want the C or C++ compiler to optimize the generated C or C++ code. Parsing time of the generated code does not matter than much (you might try -ftime-report option to measure where is g++ taking time). Newest GCC 5.1 has libgccjit which might interest you.

Basile Starynkevitch
  • 223,805
  • 18
  • 296
  • 547
  • It would still eliminate the re-including of headers across different `.cpp`s, but it doesn't eliminate the need to link large number of objects. – LyingOnTheSky May 10 '15 at 18:53
2

One of the most important factors is the ability to compile in parallel. As each Translation Unit is compiled in a sequential fashion (at least logically), there is limited opportunity for parallelization if you feed the C++ compiler just one big file.

The balancing force, as pointed out, is the startup cost of each compilation. You've got multiple CPU cores incurring the same startup cost.

As a result, parallelization stops being a win when adding an extra Translation Unit incurs more overhead cost than you save by using that extra core.

MSalters
  • 173,980
  • 10
  • 155
  • 350
  • But after that you have to link all of those objects that you compiled, which you can't do in parallel. I think one big object will make the linkage faster. (?) I am starting to think that only benchmark will help here. – LyingOnTheSky May 11 '15 at 21:37
  • @LyingOnTheSky: Modern linkers use function-level linking anyway, one big object still contains as many functions. – MSalters May 12 '15 at 07:43