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

Gru*_*bel 20 build-automation build-process scons

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

scons: Building targets ...
Run Code Online (Sandbox Code Playgroud)

步.

我怎样才能知道此时究竟scons在做什么?还有什么其他一般建议可以写快速SCons脚本?

Cat*_*kul 26

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

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

要么:

  • 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是计算机上的处理器数)或SConstruct中的"SetOption('num_jobs',N)"或SConscript.在Windows计算机上,环境变量"NUMBER_OF_PROCESSORS"中提供了处理器数.
  • 如果你有超过几十个预处理器定义("-DFOO1 -DFOO2"),你可以通过使用--profile找到SCons在subst()函数中花费大量时间,通常只是将-D字符串添加到定义中一遍又一遍地.这实际上可以减慢没有任何改变的构建.有100多个定义,我看到使用本页其他地方"缓存CPPDEFINES"中描述的概念,从35s到20s的无操作构建时间下降.

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


nos*_*nos 9

scons md5-sums文件以确定它们已经改变了,所以它几乎是md5sums所有文件.

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

env.Decider('timestamp-newer')
Run Code Online (Sandbox Code Playgroud)

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

env.Decider('MD5-timestamp')
Run Code Online (Sandbox Code Playgroud)

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

scons -j 2
Run Code Online (Sandbox Code Playgroud)

在我的2核盒子上,-j 3通常提供最大的加速.

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

  • 我玩Decider()但它对构建时间(即在0.1秒范围内)的影响几乎为零.我也已经是"scons -j 2"了,但这只对构建本身有帮助,而不是依赖于处理,那部分似乎只使用单个核心. (2认同)

Gru*_*bel 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脚本本身的评估.

  • SCons不使用GCC的依赖检查器,它有自己的基于Python的正则表达式集来查找包含.使用-isystem更快,因为你隐藏了SCons中的所有头文件.如果这些标题中的任何标题发生变化,SCons就无法分辨.你正在做的是基本上剔除隐含依赖项(标题)所代表的DAG的一大块.这会以正确为代价加速遍历步骤. (13认同)

Jay*_*est 7

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


ana*_*nik 5

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

DefaultEnvironment(tools=[])
Run Code Online (Sandbox Code Playgroud)


Cir*_*四事件 1

scons --profile+snakeviz

这个组合准确地向我展示了瓶颈所在。

--profile以 stdlib 中存在的cProfile格式输出二进制文件。

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

scons --profile f.prof
pip install -u snakeviz
snakeviz f.prof
Run Code Online (Sandbox Code Playgroud)

输出如下所示:

在此输入图像描述

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

更一般的 Python 上下文中的问题:是否有任何简单的方法来对 python 脚本进行基准测试?

--debug+ts -s

这并没有解决我的具体问题,但它通常可以给你一些想法:

time scons --debug=count,duplicate,explain,findlibs,includes,memoizer,memory,objects,prepare,presub,stacktrace,time |
  ts -s | tee f
Run Code Online (Sandbox Code Playgroud)

示例输出摘录显示了我在 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 ...
Run Code Online (Sandbox Code Playgroud)

在 scons 3.0.1、Ubuntu 18.04 中测试。

也可以看看