如何优化SCons脚本的启动时间?

时间:2009-08-23 15:53:45

标签: build-process build-automation scons

我有一个SCons脚本需要大约10秒才发现没有什么需要重建,这对于本质上相当小的项目感觉非常长。阅读SConscript本身只花了一两秒钟,大部分时间花在:

scons: Building targets ...

步骤。

如何确定scons目前究竟做了什么?还有什么其他一般建议可以写快速SCons脚本?

7 个答案:

答案 0 :(得分:26)

(直接从 http://www.scons.org/wiki/GoFastButton被盗)

命令'scons --max-drift = 1 --implicit-deps-unchanged'将尽快执行你的构建。

OR:

  • env.Decider('MD5-timestamp'):从SCons 0.98开始,您可以在环境中设置Decider功能。 MD5时间戳表示如果时间戳匹配,请不要再重新MD5文件。这可以带来巨大的加速。有关信息,请参见手册页。
  • - max-drift:默认情况下,SCons会在每次运行时计算构建中每个源文件的MD5校验和,并且只会在文件为2天后缓存校验和。默认值为2天是为了防止来自NFS或修订控制系统的时钟偏差。您可以使用--max-drift = SECONDS来调整此延迟,其中SECONDS是几秒钟。减少SECONDS可以通过消除多余的MD5校验和计算来提高构建速度。您可以使用“SetOption('max_drift',SECONDS)”在SConstruct或SConscript文件中设置此选项,而不是在每次运行的命令行中指定此选项。
  • - implicit-deps-unchanged:默认情况下,SCons将重新扫描所有源文件以获取隐式依赖项(例如C / C ++标头#includes),这可能是一个昂贵的过程。您可以告诉SCons使用--implicit-deps-unchanged来缓存调用之间的隐式依赖关系。通过使用此选项,您向SCons承诺,自上次运行以来,您没有更改任何隐式依赖项。如果确实更改了隐式依赖项,那么使用--implicit-deps-changed将导致它们被重新缓存和缓存。 (您无法在SConstruct或SConscript文件中设置此选项。)
  • - implicit-cache:此选项告诉SCons智能地缓存隐式依赖项。它尝试确定自上次构建以来隐式依赖项是否已更改,如果是,则将重新计算它们。这通常比使用--implicit-deps-unchanged慢,但也更准确。您可以使用“SetOption('implicit_cache',1)”在SConstruct或SConscript文件中设置此选项,而不是在每次运行的命令行中指定此选项。
  • CPPPATH:通常通过设置CPPPATH构造变量告诉Scons包含目录,这会导致SCons在执行隐式依赖性扫描时搜索这些目录,并且还包括编译命令行中的那些目录。如果您的头文件从不或很少更改(例如系统头或C运行时头),那么您可以将它们从CPPPATH中排除并将它们包含在CCFLAGS构造变量中,这会导致SCons在扫描时忽略这些包含目录用于隐式依赖。以这种方式仔细调整包含目录通常可以显着提高速度,同时几乎没有精度损失。
  • 使用env.SourceCode(“。”,None)避免RCS和SCCS扫描 - 如果您在程序中使用大量的c或c ++标头并且文件系统是远程的(nfs,samba),这一点尤其有用
  • 使用“BuildDir”时,使用“duplicate”设置为0:“BuildDir(dir1,dir2,duplicate = 0”。这将导致scons使用src_dir中源文件的路径名和路径调用Builders build_dir中派生文件的名称。但是,如果在构建期间生成源文件,如果任何调用的工具被硬编码以将派生文件放在与源文件相同的目录中,则这可能导致构建问题。
  • 在多处理器计算机上,一次运行多个作业可能会有所帮助 - 使用--jobs N(其中N是计算机上的处理器数)或“SetOption('num_jobs',N)”在您的SConstruct或SConscript中。在Windows计算机上,环境变量“NUMBER_OF_PROCESSORS”中提供了处理器数量。
  • 如果您有超过几十个预处理器定义(“-DFOO1 -DFOO2”),您可以通过使用--profile找到SCons在subst()函数中花费大量时间,通常只需添加-D字符串一遍又一遍地定义。这实际上可以减慢没有任何改变的构建。有100多个定义,我看到使用本页其他地方“缓存CPPDEFINES”中描述的概念,从35s到20s的无操作构建时间下降。

使事情变得更快的另一个技巧是避免在修改共享库但不重建共享库时重新链接程序。见SharedLibrarySignatureOverride

答案 1 :(得分:9)

scons md5 - 汇总文件以确定它们已经发生了变化,所以它几乎都是md5sums所有文件。

你可以告诉它只使用时间戳来决定重建什么,而不是每次都要MD5sum所有文件,就像'make'那样,这应该可以加快速度。它可能更脆弱。例如如果文件在最后一次构建的1秒内发生了变化,那么scons就不会注意到这一点。使用

env.Decider('timestamp-newer')

还有MD5时间戳,它将首先检查时间戳,然后使用Md5比较内容,如果时间戳更新则实际更改。

env.Decider('MD5-timestamp')

另一个加快速度的简单方法是使用-j参数运行并行构建。

scons -j 2

在我的2核盒子上,-j 3通常可以提供最快的速度。

scons正在做的一些输出可以通过调用scons的--debug参数完成,请参阅各种选项的联机帮助页。

答案 2 :(得分:8)

为了弄清楚为什么SCons很慢而做了一些试验和错误,到目前为止的一些发现(确切的结果当然会因SCons脚本的结构和复杂性而异):

  • CacheDir()没有明显的负面影响。
  • Decider()只有非常小的影响,不值得打扰。
  • 使用variant_dir / VariantDir()可将构建时间增加约10%。
  • 阅读SConstruct文件本身需要大约10%的完整scons调用。
  • 迄今为止最大的影响似乎是库依赖项,项目中的Gtkmm使我的构建时间翻了一倍。

可能的解决方案:

  • 不要进行完全重建,而只是重建您正在处理的目录/模块(scons -u而不是scons -D)。
  • 制作SConscript可选的部分,因此只能在手动调用时重建。
  • 对于库包含而不是-isystem使用编译器标志-I,这个更改单独将构建时间从10.5秒降低到6秒,对于我来说,它可以很容易地实现sed电话:

    env.ParseConfig('pkg-config --cflags --libs gtkmm-2.4 | sed "s/-I/-isystem/g"')

    不确定为什么会这样,我认为它会减少gcc输出的依赖关系,从而减少scons跟踪的依赖关系。

当然也强烈建议使用CacheDir()scons -j N,但这只会加快实际构建速度,而不是评估SCons脚本本身。

答案 3 :(得分:7)

移动第三方包括CPPPATH和进入CCFLAGS产生了巨大的差异。对于我们的项目有12个外部包含目录(包括boost和python),无所事事的编译从30s减少到3s - 加速10倍。

答案 4 :(得分:5)

当SCons首先创建Environment时,它会进行一系列查找以查看可用的工具。在创建第一个DefaultEnvironment之前,您可以通过在env中明确选择工具来避免不必要的检查和加速SCons。

DefaultEnvironment(tools=[])

答案 5 :(得分:2)

将SConscript添加到

if 'explain' in GetOption("debug"):
    Progress('Evaluating $TARGET\n')

并使用--debug=explain运行。您将看到SCons花费时间评估

答案 6 :(得分:1)

scons --profile + snakeviz

这种组合向我展示了到底是什么。

--profilecProfile格式输出二进制文件,即present in the stdlib

snakeviz是一个很棒的可视化工具,可以在GUI中快速查看该文件:

scons --profile f.prof
pip install -u snakeviz
snakeviz f.prof

输出看起来像这样:

enter image description here

,您可以将鼠标悬停在每个框上,以查看包含该功能的文件的完整路径。

在更一般的Python上下文中的问题:Is there any simple way to benchmark python script?

--debug + ts -s

这不能解决我的特定问题,但通常可以给您一些想法:

time scons --debug=count,duplicate,explain,findlibs,includes,memoizer,memory,objects,prepare,presub,stacktrace,time |
  ts -s | tee f

样本输出摘录显示了我在2到10秒之间有一个巨大的时间间隔,这是我试图关注的地方:

00:00:02 SConscript:/data/gem5/master3/build/ARM/sim/power/SConscript  took 1.556 ms                                       
00:00:02 dup: relinking variant 'build/ARM/sim/probe/SConscript' from 'src/sim/probe/SConscript'                              
00:00:02 Building build/ARM/sim/probe/SConscript with action:                                                                                                                
00:00:02   UnlinkFunc(target, source, env)                                                                                      
00:00:02 Building build/ARM/sim/probe/SConscript with action:                                                                  
00:00:02   LinkFunc(target, source, env)                                                                                                            
00:00:02 SConscript:/data/gem5/master3/build/ARM/sim/probe/SConscript  took 0.401 ms       
00:00:10 SConscript:/data/gem5/master3/build/ARM/tests/opt/SConscript  took 98.225 ms                                               
00:00:10 SConscript:/data/gem5/master3/build/ARM/SConscript  took 8885.387 ms            
00:00:10 SConscript:/data/gem5/master3/SConstruct  took 9409.641 ms                         
00:00:10 scons: done reading SConscript files.                                                                                    
00:00:10 scons: Building targets ...

在scons 3.0.1,Ubuntu 18.04中进行了测试。

另请参见