Методы распараллеливания компиляции
Сборка больших проектов Qt, таких как Qt Creator, например,
может занимать значительное время. Существенно сократить это время
на компьютере с многоядерным процессором позволяет распараллеливание
компиляции. Данная статья посвящена этой задаче применительно к
MinGW на платформе Windows. Существует два способа решения проблемы.
Далее для определенности будем полагать, что MinGW установлен в
папку C:\Qt\mingw-4.6, а библиотека Qt
в папку C:\Qt\qt-4.8.2.
1) Пересборка qmake
Утилита mingw32-make распознает ключ -jX,
где X число одновременно выполняемых
потоков компиляции, которое зависит от количества ядер процессора
N. Обычно рекомендуется брать X = N + 1.
Если процессор поддерживает технологию Hyper-trheading, то число
потоков X = 2 · N + 1.
Таким образом, если выполнить команду
mingw32-make -j9 -f Makefile.Release
будет выполнен сценарий сборки из файла Makefile.Release
(в конфигурации release), и при этом будет
создано до 9-ти потоков. Казалось бы проблема распараллеливания
компиляции решена, так полагает, например, автор поста
на Хабрахабре. Но не все так просто.
Если посмотреть проект Qt Creator 2.5.0, то можно убедиться,
что qmake генерирует в нем 71 файл Makefile.Release.
Выполнение сценариев этих файлов вызывается из файлов Makefile,
которых еще больше. Они также сгенерированы qmake,
и вызов выполнения сценариев в них осуществляется без ключа -jX
в каком-либо виде. Напрашивается вывод единственный способ
кардинально решить проблему это заставить qmake
генерировать файлы Makefile таким образом,
чтобы желаемый ключ в них присутствовал. А это означает, что его
нужно пересобрать с некоторыми изменениями. Эту идею выдвинул участник
Russian Qt Forum, зарегистрированный под логином lesav, в комментариях
к одному
из постов.
Рассмотрим подробно порядок действий.
1. Открываем для редактирования файл
C:\Qt\qt-4.8.2\qmake\generators\makefile.cpp
правим в нем строку
QString makefilein = " -f " + subtarget->makefile;
которая встречается в этом файле 2 раза. Новая редакция
строки выглядит, например, так
QString makefilein = " -j9 -f " + subtarget->makefile;
Число потоков компиляции в ключе -jX
в Вашем случае Вы определяете, пользуясь соотношениями, приведенными
выше.
Возможно, что для достижения большей эффективности
число потоков компиляции надо увеличить еще на 1 или 2.
2. Делаем, на всякий случай, бэкап файла C:\Qt\qt-4.8.2\bin\qmake.exe
3. Запускаем терминал Qt 4.8.2 Command Prompt
(см. группу ярлыков библиотеки Qt в меню Пуск). При запуске автоматически
будут определены переменные
QTDIR = C:\Qt\qt-4.8.2
PATH = C:\Qt\qt-4.8.2\bin;C:\Qt\mingw-4.6\bin;C:\Windows\System32
QMAKESPEC = win32-g++
и в качестве текущего будет установлен каталог C:\Qt\qt-4.8.2.
Вводя следующие команды, дополнительно модифицируем переменные
set MAKEFLAGS=
set INCLUDE=%INCLUDE%;%QTDIR%/include
4. Далее вводим
configure.exe -opensource -release -shared -debug-and-release
-phonon -phonon-backend
и, когда будет предложено,
соглашаемся с лицензией, напечатав y и нажав
Enter. Начнется процесс сборки qmake,
о чем будет свидетельствовать самое первое сообщение
Creating qmake...
и многочисленные вызовы компиляции файлов типа
g++ -c ...
Об окончании сборки qmake
нам поведают сообщения
copy qmake.exe C:\Qt\qt-4.8.2\bin\qmake.exe
Скоприровано файлов: 1.
Это, собственно, то, чего мы добивались. Далее команда
configure.exe займется генерацией сценариев
сборки всей библиотеки Qt, о чем будут свидетельствовать строки
Creating makefiles in src...
Generating MakeFiles...
и многочисленные сообщения вида
Reading C:/Qt/qt-4.8.2/
...
Поскольку данный процесс нам уже не интересен, можно
прервать выполнение команды комбинацией клавиш <Ctrl-C>.
Более того, прерывание процесса генерации сценариев сборки позволит
сэкономить около 200 MB дискового пространства.
Чтобы убедиться, что все получилось, смотрим дату создания файлов
C:\Qt\qt-4.8.2\qmake\qmake.exe и C:\Qt\qt-4.8.2\bin\qmake.exe.
Она должна обновиться, и размер файлов, скорее всего, изменится.
Теперь можно для пробы пересобрать какой-либо проект и заглянуть
в Makefile. Там где раньше было
$(MAKE) -f $(MAKEFILE)
должно стать
$(MAKE) -j9 -f $(MAKEFILE)
(или ваше значение в ключе -jX).
2) Использование jom
Добиться эффекта распараллеливания компиляции можно путем использования
утилиты jom вместо mingw32-make.
Она умеет автоматически определять количество ядер процессора и
создает нужное число потоков компиляции для достижения максимальной
эффективности.
Рассмотрим порядок действий.
1. Скачиваем с ресурса
архив
jom.zip 615 KB
(размер архива на момент написания статьи)
2. Распаковываем содержимое архива в папку
C:\Qt\mingw-4.6\bin
Предполагается, что данный путь прописан в переменной
среды Path.
Для сборки проекта из командной строки с использованием jom
следует вводить, например, следущее
qmake project.pro -r -spec win32-g++
jom jom install
где project.pro собираемый
проект. Таким образом утилита jom просто
заменяет mingw32-make.
Для использования jom в Qt Creator
необходимо вносить изменения в настройки каждого проекта так, как
показано на рисунке.
Результаты тестирования
Тестирование производительности сборки проекта Qt Creator 2.5.0
осуществлялось на 3-х компьютерах со следующими характеристиками
1. Desktop, i7-2600K
CPU Intel Core i7-2600K 3.4 GHz (разогнан до 3.7
GHz), DDR3-1333 8Gb
Windows 7 64bit, Avast 7.0.1426
2. Desktop, i5-750
CPU Intel Core i5-750 2.66 GHz (разогнан до 3.2
GHz), DDR3-1333 2Gb
Windows XP, Avast 6.0.1203
3. Notebook, T2330
CPU Intel Pentium Dual T2330 1.60 GHz, DDR2-667 2Gb
Windows 7 32bit, Avast 6.0.1203
Можно догадаться, что работающий антивирус оказывает влияние на
скорость сборки. Результаты тестирования приведены в таблице
Конфигурация
|
Оригинальный
qmake и mingw32-make |
Пересобранный
qmake и mingw32-make |
Оригинальный
qmake и jom |
|
|
8 мин 35 сек |
8 мин 25 сек |
8 мин 25 сек |
8 мин 20 сек |
8 мин 25 сек |
8 мин 30 сек |
8 мин 30 сек |
|
-j6 |
-j7 |
-j8 |
-j9 |
-j10 |
-j11 |
-j12 |
|
|
|
|
33 мин 50 сек |
33мин 20 сек |
33мин 0 сек |
33мин 20 сек |
33мин 20 сек |
|
|
|
|
|
1 час 24 мин 20 сек |
1 час 15 мин
40 сек |
1 час 28 мин 50 сек |
|
|
|
Как видно из таблицы, на каждом компьютере было произведено тестирование
скорости сборки проекта с несколькими значениями количества потоков
в ключе -jX. Для того чтобы каждый раз не
пересобирать qmake, был использован код,
позволяющий считывать значение, присутствующее в ключе, из текстового
файла. Итак, открываем для редактирования файл C:\Qt\qt-4.8.2\qmake\generators\makefile.cpp
и находим в нем реализацию функции
void MakefileGenerator::writeSubTargets( ... )
В самом начале тела этой функции помещаем следующий
код
QString jstr; //
ключа пока нет
// полное имя qmake
QString jfnm(var("QMAKE_QMAKE"));
// ищем последний разделитель в имени
int jk = jfnm.lastIndexOf(QChar('\\'));
// заменяем имя файла
jfnm = jfnm.left(++jk) + QString("jnum.txt");
QFile file(jfnm); //
инициализируем файл
if (file.exists()) //
если файл существует, то
{
// открывем его только
для чтения как текстовый
file.open(QIODevice::ReadOnly|QIODevice::Text);
QTextStream jin(&file);
// организуем входной поток
QString str;
jin >> str; //
получаем из файла строку
// убираем из нее пробелы
str.replace(QChar('
'),QString(""));
// в строке только цифры?
jk = str.indexOf(QRegExp("\\d+"));
// если да, то
if (jk==0)
{
//
получаем значение
jk = str.toInt();
//
если оно > 1, то добавляем ключ -jX
if
(jk>1) jstr = QString("
-j") + str;
}
file.close(); //
закрываем файл
}
В теле этой же функции дважды встречается упоминаемая
выше строка, в которую необходимо вставить ключ -jX.
Новая редакция этой строки теперь выглядит так
QString makefilein = jstr
+ " -f " + subtarget->makefile;
Данный код определяет, в какой папке расположен qmake
и ищет в этой же папке текстовый файл jnum.txt,
внем прописано количество потоков. В строковой переменной jstr
формируется ключ в виде " -jX",
где X значение, считанное из файла
и подвергнутое минимальной проверке. Если файл не существует, или
значение в нем не поддается расшифровке, или оно равно 1, то в переменной
jstr пустая строка, и, соответственно, ключ
в Makefile добавлен не будет.
Таким образом, требуется пересобрать qmake,
сборка отличается только изменениями, вносимыми в файл makefile.cpp,
а в остальном производится также, как описано выше. Далее, необходимо
создать файл C:\Qt\qt-4.8.2\bin\jnum.txt,
в котором прописать количество потоков. Теперь чтобы изменить ключ
-jX, достаточно отредактировать файл jnum.txt.
Чтобы производить сборку без распараллеливания компиляции, надо
прописать в файл 1.
По результатам тестирования можно отметить следущее
1. При сборке проекта с помощью mingw32-make
проявляет высокую активность антивирус. Возможно это связано с многочисленными
обращениями к динамическим библиотекам. Это, безусловно, могло повлиять
на полученный результат тестирования.
2. Использование ключа -jX
при вызове mingw32-make существенно сокращает
время сборки проекта даже на процессоре с двумя ядрами. Приведенные
выше формулы для определения числа потоков сборки X
в ключе достаточно оптимальны.
3. Утилита jom успешно
заменяет собой mingw32-make и даже показывает
наилучший результат. Возможно это связано с тем, что антивирус довольно
"вяло" реагирует на ее работу.
Противники использования jom вместо mingw32-make
могут возразить: "А казачок-то засланный!" И будут абсолютно
правы. Утилита jom разрабатывалась для замены
nmake из Microsoft Visual Studio. И если
уж Вы выбрали версию библиотеки Qt с MinGW, то использовать jom
будет "не по Фэн-шуй". Но, в конце концов, эту проблему
эстетического характера каждый разрешает для себя сам.
|