Как оптимизировать время запуска скрипта SCons?

У меня есть сценарий SCons, который занимает около 10 секунд только для того, чтобы выяснить, что ничего не нужно перестраивать, что кажется ужасно долгим для того, что по сути является довольно небольшим проектом. Чтение самого SConscript занимает всего секунду или две, большую часть времени тратится на:

scons: Building targets ...

шаг.

Как я могу узнать, что именно scons делает в этот момент? И какой еще общий совет можно дать по написанию быстрых SCons-скриптов?


person Grumbel    schedule 23.08.2009    source источник


Ответы (7)


scons --profile + snakeviz

Эта комбинация показала мне, в чем именно заключалось узкое место.

--profile выводит двоичный файл в формате cProfile, который присутствует в стандартной библиотеке.

snakeviz — отличный инструмент визуализации для быстрого просмотра этого файла в графическом интерфейсе:

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

Вывод выглядел так:

введите здесь описание изображения

и вы можете навести курсор на каждое поле, чтобы увидеть полный путь к файлу, содержащему функцию.

Вопрос в более общем контексте Python: любой простой способ сравнить скрипт Python?

--debug + ts -s

Это не решило мою конкретную проблему, но часто может дать вам некоторые идеи:

  • включить все флаги --debug или все, что покажется интересным
  • передать его через ts -s, который показывает, когда каждая строка появляется на стандартном выводе: -stdout-was-the-last-output-line-in/49797547#49797547">Как отслеживать, сколько времени каждая строка stdout была последней строкой вывода в Bash для бенчмаркинга?
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.

См. также

person Ciro Santilli 新疆再教育营六四事件ۍ    schedule 16.01.2020

(украдено непосредственно с сайта http://www.scons.org/wiki/GoFastButton)

Команда 'scons --max-drift=1 --implicit-deps-unchanged' выполнит вашу сборку максимально быстро.

OR:

  • env.Decider('MD5-timestamp'): начиная с SCons 0.98, вы можете установить функцию Decider в среде. MD5-timestamp говорит, что если временная метка совпадает, не беспокойтесь о повторном MD5-файле. Это может дать огромное ускорение. Смотрите справочную страницу для получения информации.
  • --max-drift: по умолчанию SCons вычисляет контрольную сумму MD5 для каждого исходного файла в вашей сборке при каждом запуске и кэширует контрольную сумму только после того, как файлу исполнится 2 дня. Это значение по умолчанию, равное 2 дням, предназначено для защиты от перекоса часов из-за NFS или систем контроля версий. Вы можете настроить эту задержку, используя --max-drift=SECONDS, где SECONDS — это некоторое количество секунд. Уменьшение значения SECONDS может повысить скорость сборки за счет устранения лишних вычислений контрольной суммы MD5. Вместо того, чтобы указывать это в командной строке при каждом запуске, вы можете установить этот параметр в файле SConstruct или SConscript, используя "SetOption('max_drift', SECONDS)".
  • --implicit-deps-unchanged: по умолчанию SCons будут повторно сканировать все исходные файлы на наличие неявных зависимостей (например, #includes заголовков C/C++), что может быть дорогостоящим процессом. Вы можете указать SCons кэшировать неявные зависимости между вызовами, используя параметр --implicit-deps-unchanged. Используя эти параметры, вы даете SCons обещание, что вы не изменяли ни одну из неявных зависимостей с момента последнего запуска. Если вы измените неявные зависимости, то использование --implicit-deps-changed приведет к их повторному сканированию и кэшированию. (Вы не можете установить этот параметр в файлах SConstruct или SConscript.)
  • --implicit-cache: этот параметр указывает SCons разумно кэшировать неявные зависимости. Он пытается определить, изменились ли неявные зависимости с момента последней сборки, и если да, то пересчитывает их. Обычно это медленнее, чем использование --implicit-deps-unchanged, но также более точно. Вместо того, чтобы указывать это в командной строке при каждом запуске, вы можете установить этот параметр в файле SConstruct или SConscript, используя "SetOption('implicit_cache', 1)".
  • CPPPATH: обычно вы сообщаете Scons о включенных каталогах, устанавливая переменную построения CPPPATH, которая заставляет SCons искать эти каталоги при выполнении неявного сканирования зависимостей, а также включает эти каталоги в командную строку компиляции. Если у вас есть файлы заголовков, которые никогда или редко изменяются (например, системные заголовки или заголовки времени выполнения C), вы можете исключить их из CPPPATH и вместо этого включить их в переменную построения CCFLAGS, что заставит SCons игнорировать эти включаемые каталоги при сканировании. для неявных зависимостей. Тщательная настройка подключаемых каталогов таким образом обычно может привести к резкому увеличению скорости с очень небольшой потерей точности.
  • Избегайте сканирования RCS и SCCS с помощью env.SourceCode(".", None) - это особенно интересно, если вы используете много заголовков c или c++ в своей программе и ваша файловая система удалена (nfs, samba).
  • При использовании «BuildDir» используйте его с «дубликатом», установленным на 0: «BuildDir( dir1, dir2, дубликат = 0». Это заставит scons вызывать Builders, используя пути исходных файлов в src_dir и пути производных файлов. файлы в build_dir.Однако это может вызвать проблемы со сборкой, если исходные файлы создаются во время сборки, если какие-либо вызываемые инструменты жестко закодированы для размещения производных файлов в том же каталоге, что и исходные файлы.
  • На многопроцессорной машине может быть полезно запускать несколько заданий одновременно — используйте --jobs N (где N — количество процессоров на вашем компьютере) или «SetOption('num_jobs', N)» внутри вашего SConstruct. или SConscript. На компьютерах с Windows количество процессоров доступно в переменной среды NUMBER_OF_PROCESSORS.
  • Если у вас более нескольких десятков определений препроцессора ("-DFOO1 -DFOO2"), вы можете обнаружить, используя --profile, что SCons тратит много времени на функцию subst(), обычно просто добавляя строку -D к определениям. снова и снова. Это может сильно замедлить сборку, в которой ничего не изменилось. С 100+ определениями я увидел, что время сборки бездействия сократилось с 35 с до 20 с, используя идею, описанную в «Кэширование CPPDEFINES» в другом месте на этой странице.

Еще одна хитрость, позволяющая ускорить работу, заключается в том, чтобы избегать повторной компоновки программ, когда разделяемая библиотека была изменена, но не перестроена. См. раздел SharedLibrarySignatureOverride.

person Catskul    schedule 22.09.2009

scons файлы md5-sums, чтобы выяснить, были ли они изменены, так что md5суммирует практически все ваши файлы.

Вы можете указать ему использовать только временные метки, чтобы решить, что перестраивать, и не нужно каждый раз MD5суммировать все файлы, как это делает make, что должно ускорить процесс. Он может быть более хрупким. например если файл изменился в течение 1 секунды с момента его последней сборки, scons этого не заметит. Использовать

env.Decider('timestamp-newer')

Существует также метка времени MD5, которая сначала проверяет метку времени, а затем сравнивает содержимое с помощью Md5, если оно действительно изменилось, если метка времени новее.

env.Decider('MD5-timestamp')

Еще один простой способ ускорить процесс — запустить параллельную сборку с параметром -j.

scons -j 2

На моем 2-ядерном компьютере -j 3 обычно дает наибольшее ускорение.

Некоторый вывод о том, что делает scons, можно сделать с помощью аргумента --debug для вызова scons, см. справочную страницу для различных параметров.

person nos    schedule 23.08.2009
comment
Я играл с Decider(), но он почти не влияет на время сборки (т.е. в диапазоне 0,1 секунды). Я также уже использую scons -j 2, но это помогает только с самой сборкой, а не с обработкой зависимостей, эта часть, похоже, использует только одно ядро. - person Grumbel; 23.08.2009
comment
Нужно ли устанавливать Decider в каждом файле SConscript или только в исходном файле SConstruct, чтобы использовать его во всей сборке? - person Tor Klingberg; 22.07.2015
comment
Верхнего файла SConstruct достаточно. (хотя, если вы каким-то образом создаете отдельные среды где-то еще, которые не копируются/клонируются из среды, в которой вы вызываете Decider(), вам нужно снова вызвать ее для этой среды. - если вы не создаете экземпляры каких-либо сред, просто вызовите Decider() вместо env.Decider() ) - person nos; 22.07.2015

Сделал немного проб и ошибок, чтобы выяснить, почему 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.

person Grumbel    schedule 24.08.2009
comment
SCons не использует средство проверки зависимостей GCC, у него есть собственный набор регулярных выражений на основе Python для поиска включений. Использование -isystem быстрее просто потому, что вы скрываете все эти заголовки от SCons. Если какой-либо из этих заголовков изменится, SCons не сможет об этом сказать. То, что вы делаете, по сути, отбраковывает большой кусок DAG, представленный неявными зависимостями (заголовками). Это ускоряет шаг обхода за счет корректности. - person BenG; 16.03.2010

Перемещение сторонних включений из CPPPATH в CFLAGS имело огромное значение. Для нашего проекта с 12 внешними включаемыми каталогами (включая boost и python) компиляция бездействия сократилась с 30 до 3 с — ускорение в 10 раз.

person Jay West    schedule 30.09.2014
comment
Тот же опыт для меня. Наличие путей Boost в CPPPATH просто взрывает дерево зависимостей по многим причинам. Есть что-то в boost libs, что вызывает это. Возможно, потому что файлы .hpp имеют много включений. - person Evgen; 05.04.2017

Когда SCons создает первый Environment, он выполняет ряд запросов, чтобы узнать, какие инструменты доступны. Вы можете избежать ненужных проверок и ускорить SCon, явно выбрав инструменты в DefaultEnvironment перед созданием своего первого env.

DefaultEnvironment(tools=[])
person anatoly techtonik    schedule 02.05.2014

Добавьте в свой SConscript что-то вроде

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

и беги с --debug=explain. Вы увидите, на что SCons тратит время на оценку

person Evgen    schedule 05.04.2017