Pull to refresh

Comments 35

В проекте Chromium недавно пошли по пути Single Compilation Unit с использованием jumbo, так как из параллельной компиляции все соки уже выжали.
Разумеется сразу возникли проблемы с коллизиями имен, но их постепенно решают. Оказалось, что подход хорошо работает для сборки в 4-8 потока (ускорение в 2 раза), и дает меньший эффект на многоядерных системах. Настоятельно рекомендуется всем использовать use_jumbo_build = true
Стоит ли ожидать появления соответствующих диагностик в PVS-Studio (например, для уменьшения зависимостей использованием указателя вместо объекта (способ 2, вариант №1))?
Мы про это не думали. Сейчас основное наше направление, это поиск ошибок и контроль качества кода.
Но это отличная фича и прекрасный задел на будущее.
у вас уже есть такое правило, выдает предупреждение о снижении производительности и предлагает использовать константные ссылки. номер сейчас не вспомню.
«Для уменьшения зависимостей» (оптимизация скорости сборки) и микрооптимизации кода, это всё-таки разные направления.
В заголовочных файлах не используйте объекты там, где можно воспользоваться ссылками или указателями. Для ссылок и указателей достаточно опережающего объявления, поскольку компилятор знает размер ссылки/указателя (4 или 8 байт в зависимости от платформы), а размер передаваемых объектов не имеет значения.
Тут, хотелось бы уточнить, что в случае объявления можно возвращать объект по значению. Об этом мало кто помнит, но это также помогает избежать ненужных include-ов. Т.е. такой код скомпилируется:
/ Bar.h
#pragma once
class Foo;     // <= Опережающее объявление класса Foo
class Bar
{
  Foo get_foo() const;  // Размер возвращаемого значения не важен
  ....
};
Дополнительно уточню, что в случае опережающего объявления вы можете не только возвращать объект по значению, но даже и принимать объект по значению в качестве параметра! Единственное, что от вас требует компилятор — предоставить определение класса на момент вызова функции. Этот пример тоже скомпилируется:
// Foo.h
class Foo { .... };

// Bar.h
class Foo;                    // <= Все также опережающее объявление
class Bar
{
  Foo get_foo(Foo obj) const; // <= И принимаем параметр по значению,
                              //    и также возвращаем
  ....
};
А как компилятор узнает где разместить параметр, переданный по значению, не зная его размер?
Согласитесь, данная информация никак не влияет на размер класса Bar и его интерфейс может быть объявлен без неё. В .c/.cpp файле определение естественно понадобится. Как только вы попытаетесь вызвать данный метод или вернуть класс по значению, вот тут, вам и понадобится определение Foo и компилятор вам подскажет об этом.
Вот это новость. 10 лет программирую на С++ (из них 6 — профессионально), и был уверен, что так нельзя. Спасибо.
А разве не будет создан лишний экземпляр и лишний копирующий конструктор не будет вызван на c<11?
При возврате по значению, если из функции один return, то, почти всегда, будет RVO или RNVO — зависит от компилятора. А это даже быстрее чем std::move.
При передаче по значению, конечно будет. Но это не всегда будет медленнее. Зависит от размера класса и его структуры.
При возврате класса по значению, место под него будет выделено в стеке вызывающей, а не возвращающей функции — т.к. экземпляр класса должен продолжать жить после её возврата. Таким образом, по факту обычно вообще ничего физически не копируется. Т.е.
class Foo;
Foo f = some_func ();
эквивалентно
class Foo;
Foo f;
some_func (&f);

В случае передачи экземпляра параметром действительно создается копия класса, и тут уже надо смотреть, что это за класс. Если это какой-нибудь allocator, возможно всё, что он по факту содержит в памяти — это один указатель, и передавать его по значению даже быстрее, чем указателем (при обращении не надо лишней операции чтения из памяти).

Если же это какой-нибудь контейнер типа std::vector, конечно будет произведено полное копирование его и всех его внутренностей, что может быть весьма медленно. Тут уже надо рассмотреть вариант передачи ссылкой или const ссылкой. А копирование std::shared_ptr, например, будет выполнять две команды процессора с префиксом lock, что медленнее обычных операций с памятью.
Вам нужно было просто продать Macbook Pro и купить Macbook Mini 2012-го года для сборки (памяти на него накатить ещё, и SSD поставить)… Опыт хабрапользователей показывает, что скорость сборки вырастает.
Еще есть сейчас поверья, что замена батареи Macbook Pro ускорит время компиляции и скорость работы системы в целом… И устаревший Mac mini покупать не придется.
убирайте неиспользуемые включения

включается во все единицы трансляции самым первым


У вас тут взаимоисключающие параграфы. Нельзя одновременно сделать PCH и IWYU.
Частично, да. Но и в stdafx можно напихать много ненужного. Иногда так происходит из-за всякого рода рефакторингов, когда нужные раньше заголовки становятся ненужными.
Судя по этой задаче, IWYU поддерживает предкомпилированные заголовки. Предлагают использовать следующие флаги:
  • --pch_in_code сообщает IWYU рассматривать первое включение как предкомпилированный заголовок. Используйте --pch_in_code, чтобы предотвратить ситуации, в которых IWYU удалит необходимые PCH включения.
  • --prefix_header_includes=<value> позволяет выбрать, что делать с включениями и опережающими объявлениями, которые встречаются как в префиксном/предкомпилированном заголовке, так и единице трансляции. Возможные значения: add (по умолчанию), keep, remove.


Также соглашусь с gasizdat, после рефакторинга у вас могут остаться куча неиспользуемых include'ов даже в предкомпилированном заголовке, а в него не стоит запихивать все и побольше.
Вы не поняли. Тут противоречие на концептуальном уровне. PCH инклюдится во все файлы. Даже в те которым он не нужен. Это нарушает принцип IWYU.
Как любят писать «я просто оставлю это здесь» www.youtube.com/watch?v=OhaEINErq6w
Вообщем electric cloud рулит и у меня лично сложилось впечатление что оно соберет не только быстро, но и правильно, правда за деньги :)
IncrediBuild лучше потому что соберет на вашем локальном простаивающем железе. Zero-config в VS, просто устанавливаешь и оно работает.
Интересно, к C++33 таки поправят эти косяки архитектуры языка, или так и будут продолжать катить этот ворох компромиссов?
Очень сомневаюсь, обратная совместимость постоянно будет тянуть обратно в трясину. Тут тот самый случай, когда сделать новое будет проще, чем починить старое. А у чего-то нового будут большие проблемы с тем чтобы взлететь.

На самом деле было бы неплохо в компиляторы включать strict режим, который бы не компилировал устаревшие конструкции. Это разумеется не сработало бы для легаси, но для написания нового кода с нуля дало бы выигрыш.

Планируют добавить модули, которые, как предполагается, должны будут со временем полностью вытеснить #includ-ы.
Unity build можно делать вручную, без автогенерации. У нас например сделано так — Cmake функции передается список исходников, в которых она ищет файлы вида unity_build.*.cpp. Из этих файлов извлекается список include и в зависимости от опции в сборке либо отключается сам unity build модуль либо все его включения.
Причем все файлы можно оставить в проекте, отключая компиляцию через свойство HEADER_FILE_ONLY.
SCU мб и хорошо использовать для сборки всего проекта, но по идее этот процесс должен быть автоматизирован. А вот скорость инкрементальных сборок, куда больше влияющую на скорость разработки, он попросту уничтожает
UFO just landed and posted this here
Тоже несколько раз пересматривал цифры и текст под картинками в поисках пояснений, т.к. не был уверен, зачем так основательно выделять эти самые 3%.

На самом деле (если говорить про сборку на Windows), её файловый кэш весьма неплох. И если в компьютере есть достаточное количество памяти (а если его нет, о каком RAMDisk'е может идти речь?), то после первой же сборки все требуемые файлы попадут в кэш и скорость обращения к ним в дальнейшем будет вполне сравнима с тем же рамдиском. Т.е. по факту при последующей сборке будет необходимо только записывать данные на диск (сохранять промежуточные и результирующие файлы), а тут снова на помощь приходит write-back cache, который прекрасно эти операции оптимизирует.

Не знаю на счет других платформ, но думаю, там ситуация точно такая же.

Статья познавательная, но есть пара замечаний:


  1. Пропущен вопрос того, как инстанциируются шаблоны в С++ (а по умолчанию они инстанциируются для каждой единицы компиляции и это вопрос можно частично решить с помощью extern template из С++11)
  2. Оптимизация процесса линковки тоже не описана, ведь как минимум в MSVS существуют разные варианты оптимизации на этом уровне, например Whole Program Optimization не позволяет делать инкрементальную линковку, что очень неприятно когда при изменении одной строки кода приходится ждать несколько минут на линковке. По моему опыту, полученному из ряда эксперименов, при включеном WPO файлы компилируются быстрее, но время с лихвой съедается за счет линковки в случае большого проекта. При выключеном — компиляция удлинняется, зато линковка почти мгновенная.

Спасибо за замечание. Давно не живу в русскоязычной среде — забыл как правильно.

Sign up to leave a comment.