[cmake-developers] Setting includes, defines and other usage requirements with one command

Stephen Kelly steveire at gmail.com
Sat Feb 2 08:19:54 EST 2013


Brad King wrote:
> Meanwhile, I've noticed the test run times on some dashboard machines
> have doubled since two weeks ago.  Can you please run some timing
> tests comparing current 'next' to 2.8.10.2 for some existing projects?

Actually they seem to have doubled overnight when tll-includes-defines was 
merged:

 http://open.cdash.org/viewTest.php?buildid=2743648
 http://open.cdash.org/viewTest.php?buildid=2795799

I pushed a patch which is needed for using the new interfaces in boost, and 
which speeds up the cmake time in kde frameworks branch a bit:

 before tll-includes-defines
 real    0m27.528s
 user    0m16.721s
 sys     0m6.732s

 after tll-includes-defines
 real    0m30.767s
 user    0m19.965s
 sys     0m6.624s

 After optimize:
 real    0m27.246s
 user    0m16.661s
 sys     0m6.388s


Before the optimization, callgrind shows that a lot of time is spent in 
GetIncludeDirectories, in particular in executing 
cmSystemTools::ExpandListArgument with the result of genex evaluation.

Also, after optimization and with kde frameworks updated to populate and 
rely on target interfaces, I get this:

 real    0m26.922s
 user    0m16.401s
 sys     0m6.404s

It will be interesting to see if the optimization has any affect on the 
dashboards. The effect of GetIncludeDirectories taking a long time is 
multiplied several times there for the multiconfig generators. However, the 
optimization should only have any effect at all if several targets are used 
which have dependencies between them, and where there are multiple paths to 
the same dependencies.

I considered also doing some caching in GetIncludeDirectories  similar to 
the caching done in the linking related methods, but it didn't seem to have 
a noticable difference. At least for the makefile generator, 
GetIncludeDirectories is called only twice with the same arguments. 

The optimization only works because we know that includes and defines are 
uniq'd after the generator expressions are evaluated. 


Looking into the future a bit, I think that as new transitive interfaces are 
added in the future (COMPILE_OPTIONS and LINK_OPTIONS), it might make sense 
to keep that invariant with those too. Otherwise the performance problem 
would likely re-appear.

In the case of COMPILE_OPTIONS at least, order can be relevant in some 
cases. 

 http://gcc.gnu.org/onlinedocs/gcc-3.4.4/gcc/Optimize-Options.html

> If you use multiple -O options, with or without level numbers, the last 
> such option is the one that is effective. 

That would mean that if we have something like this:

 target_compile_options(foo INTERFACE -O3)
 target_compile_options(bar INTERFACE -O2)
 target_link_libraries(foo PRIVATE bar)

 target_link_libraries(user PRIVATE bar foo)

then the un-optimized result would look like:

 -O2 -O3 -O2

because foo has INTERFACE_COMPILE_OPTIONS "-O3;$<LINKED:bar>" and user would 
end up with COMPILE_OPTIONS "$<LINKED:bar>;$<LINKED:foo>" which expands 
first to "-O2;-O3;$<LINKED:bar>" and then "-O2;-O3;-O2".

However the 'optimized' result would look like:

 -O2 -O3

because the first expansion would be "-O2;-O3;$<LINKED:bar>", but the 
repeated 'bar' would be ignored.

If instead of 

 target_link_libraries(user PRIVATE bar foo)
 
it was 

 target_link_libraries(user PRIVATE foo bar)

then the optimized result would be:

 -O3 -O2

and the unoptimized result would be 

 -O3 -O2 -O2 


The example of -O flags is not a particularly good one as it's not likely 
something suitable for putting into an INTERFACE. However, other compile 
options which would be suitable for the INTERFACE in theory might have 
unexpected results with this kind of thing.

Solutions I can think of include declaring that it is not supported to rely 
on ordering in COMPILE_OPTIONS, and adding a $<NO_SKIP:...> expression to 
temporarily turn off the optimization. 

For example, if we had this:

 target_link_libraries(foo PRIVATE bar)
 target_compile_options(foo INTERFACE -O3)
 target_compile_options(bar INTERFACE -O2)

 target_link_libraries(user PRIVATE foo bar)

(note the order changed)

the optimized result would be "-O2;-O3" even though bar is listed last in 
'user', because that is skipped. This could then be used instead where 
needed:

 target_link_libraries(user PRIVATE foo $<NO_SKIP:bar>)

Alternatively, in the case of COMPILE_OPTIONS, we could make 'top-level' 
items always NO_SKIP.

Just some food for thought for when we get around to this stuff.

Thanks,

Steve.





More information about the cmake-developers mailing list