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

21

我有一个 SCons 脚本,仅仅是为了发现不需要重新构建而花费了约 10 秒钟的时间,这对于一个规模相对较小的项目来说感觉非常长。读取 SConscript 本身只需要一两秒钟,大部分时间都花在了:

scons: Building targets ...

步骤。

我如何找出scons在这一点上到底在做什么?还有哪些关于编写快速SCons脚本的通用建议可以提供?

7个回答

25

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

执行命令“scons --max-drift=1 --implicit-deps-unchanged”将尽可能快地构建您的项目。

或者:

  • env.Decider('MD5-timestamp'):从SCons 0.98开始,您可以在环境中设置Decider函数。MD5-timestamp表示如果时间戳相同,则不必重新计算文件的MD5值。这可以极大地提高速度。有关详细信息,请参见man页面。
  • --max-drift:默认情况下,SCons每次运行时都会计算构建中每个源文件的MD5校验和,并且只有在文件年龄超过2天时才会缓存校验和。这个默认值为2天是为了防止NFS或版本控制系统的时钟偏差。您可以使用--max-drift = SECONDS调整此延迟,其中SECONDS是一些秒数。减少SECONDS可以通过消除多余的MD5检查来提高构建速度。您可以在SConstruct或SConscript文件内部使用“SetOption('max_drift', SECONDS)”设置此选项,而不是在每次运行时指定此选项。
  • --implicit-deps-unchanged:默认情况下,SCons将重新扫描所有源文件的隐式依赖关系(例如C / C ++头文件#include),这可能是一个昂贵的过程。您可以使用--implicit-deps-unchanged告诉SCons缓存调用之间的隐式依赖关系。通过使用此选项,您承诺自上次运行以来没有更改任何隐式依赖项。如果您更改了隐式依赖项,则使用--implicit-deps-changed将导致重新扫描并缓存它们。(您无法从SConstruct或SConscript文件中设置此选项。)
  • --implicit-cache: 这个选项告诉SCons智能缓存隐式依赖项。它会尝试确定自上次构建以来是否更改了隐式依赖项,并在需要时重新计算它们。这通常比使用--implicit-deps-unchanged慢,但也更准确。您可以在SConstruct或SConscript文件中使用“SetOption('implicit_cache', 1)”设置此选项,而无需在每次运行时在命令行上指定。
  • 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中使用源文件的路径名并在build_dir中使用导出文件的路径名来调用Builders。但是,如果在构建过程中生成源文件或任何调用的工具硬编码为将导出文件放在与源文件相同的目录中,则可能会导致构建问题。
  • 在多处理器计算机上,同时运行多个作业可能会产生效益-使用--jobs N(其中N是计算机上的处理器数量)或在SConstruct或SConscript中使用“SetOption('num_jobs', N)” 。在Windows计算机上,处理器数量可在环境变量'NUMBER_OF_PROCESSORS'中获得。
  • 如果您有超过几十个预处理器定义(如“-DFOO1 -DFOO2”),那么您可能会发现使用--profile时,SCons在subst()函数中花费了很多时间,通常只是不断地将-D字符串添加到定义中。这可能会使没有更改的构建变得非常缓慢。当有100个以上的定义时,我使用本页面其他地方描述的"Caching the CPPDEFINES"的想法,使一个无操作的构建时间从35秒降至20秒。
  • 另一个加快速度的技巧是在共享库已修改但未重建时避免重新链接程序。请参阅 SharedLibrarySignatureOverride


    9

    scons会通过md5-sums计算文件是否已更改,因此它几乎会对所有文件进行md5sums。

    您可以告诉它只使用时间戳来决定何时重新构建,而不必每次都对所有文件进行MD5sums,就像“make”一样,这应该可以加快速度。但是它可能更加脆弱。例如,如果一个文件在上次构建的1秒内发生了更改,scons将不会注意到。使用

    env.Decider('timestamp-newer')
    

    还有一种MD5-timestamp方法,它会先检查时间戳,然后使用Md5比较内容是否已经改变,如果时间戳更新了,就会执行比较操作。

    env.Decider('MD5-timestamp')
    

    另一个简单的加速选项是使用“-j”参数运行并行构建。
    scons -j 2
    

    在我的双核电脑上,使用-j 3参数通常可以获得最大的加速效果。

    通过在调用scons时使用--debug参数,可以获得关于scons正在执行的一些输出信息,有关各种选项,请参阅man页面。


    2
    我尝试使用Decider(),但它对构建时间几乎没有影响(即在0.1秒范围内)。我已经使用"scons -j 2",但这只有助于构建本身,而不是处理依赖项,该部分似乎只使用单个核心。 - Grumbel
    我需要在每个SConscript文件中设置Decider,还是只需要在初始的SConstruct文件中设置,就可以在整个构建过程中使用它? - Tor Klingberg
    顶层的SConstruct文件就足够了。(但是,如果您在其他地方创建了不从调用Decider()的环境中复制/克隆的单独环境,则需要再次为该环境调用它。- 如果您没有实例化任何环境,只需调用Decider()而不是env.Decider()) - nos

    8

    为了找出SCons为什么慢,我进行了一些试验和错误,目前发现以下结果(具体结果当然会因SCons脚本的结构和复杂性而异):

    • CacheDir()没有明显的负面影响。
    • Decider()只有非常微小的影响,不值得费心。
    • 使用variant_dir/VariantDir()会使构建时间增加约10%。
    • 读取SConstruct文件本身占完整scons调用的约10%。
    • 远远最大的影响似乎是库依赖项,将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脚本的评估。


    13
    SCons不使用GCC的依赖性检查器,它有自己基于Python的一套正则表达式来查找包含文件。使用-isystem只是因为你正在从SCons中隐藏所有这些标头,更快。如果其中任何一个标头发生更改,SCons将无法检测到。你所做的实质上是削减了由隐式依赖项(标头)表示的DAG的大块内容。这加速了遍历步骤,但会牺牲正确性。 - BenG

    7
    将第三方包含文件从CPPPATH移动到CCFLAGS中,效果显著。对于我们的项目,其中包括12个外部包含目录(包括boost和python),一个不进行任何操作的编译时间从30秒缩短到3秒——加速了10倍。

    1
    对我来说也是一样的经历。在CPPPATH中添加Boost路径会使依赖树增加多个因素。这与Boost库有关,可能是因为.hpp文件包含了许多头文件。 - Evgen

    5

    SCons 创建第一个 Environment 时会进行一系列的查找以确定有哪些工具可用。在创建第一个 env 之前,在 DefaultEnvironment 中明确选择工具,可以避免不必要的检查并加快 SCons 的速度。

    DefaultEnvironment(tools=[])
    

    2

    在您的SConscript中添加以下内容:

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

    并加上 --debug=explain,运行程序。您将了解SCons花费时间评估的内容。


    1

    scons --profile + snakeviz

    这个组合向我展示了瓶颈在哪里。

    --profilecProfile格式输出一个二进制文件,该文件可以在标准库中找到。

    snakeviz 是一个非常棒的可视化工具,可以通过GUI快速查看该文件:

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

    输出结果如下所示:

    enter image description here

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

    在更一般的Python环境中提出的问题:有没有简单的方法来对Python脚本进行基准测试?

    --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中进行了测试。

    另请参阅


    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接