[Перевод] Threadripper 3990X: компилируем 1 миллиард строк C++ на 64 ядрах

e1uakuvav56a-zbouc0pgheebha.jpeg

RAD Studio состоит из Delphi и C++Builder. Компилятор Object Pascal в Delphi является однопроходным компилятором, и сам компилятор не является параллельным, однако при компиляции нескольких проектов параллельно он оказался способен скомпилировать 1 миллиард строк кода Object Pascal за 5 минут на машине с 16-ядерным AMD Ryzen 9 5950x. Я хотел выяснить, возможно ли сделать что-то подобное для C++. Этот пост является частью серии статей, в которой мы исследуем значительный прирост производительности, которого можно достичь на самых быстрых на начало 2021 года процессорах. Сколько это — 1 миллиард строк кода? Взгляните сюда.

Параллельная компиляция в C++Builder


C++Builder имеет несколько различных компиляторов, в том числе классический компилятор Borland и современные компиляторы на основе Clang для нескольких платформ. Кроме того, Embarcadero спонсирует среду Dev-C++ с открытыми исходниками, содержащую в комплекте компилятор TDM-GCC 9.2.0. GCC 9.2.0 содержит MAKE, поддерживающий параллельную компиляцию при помощи параметра командной строки -j (Jobs). У C++Builder есть аддон под названием TwineCompile, реализующий в C++Builder параллельное компилирование. И C++Builder, и Dev-C++ созданы с помощью Delphi.

Насколько я понял из своих исследований, TwineCompile обеспечивает более широкую функциональность, чем MAKE Jobs, потому что TwineCompile поддерживает компиляцию в фоновом режиме и другие функции улучшения производительности. Поддержка дополнительных функций типа компиляции в фоновом режиме зависит от IDE: Dev-C++ его не поддерживает, а C++Builder поддерживает с помощью TwineCompile. Dev-C++ — это отличная нативная IDE C++ для разработки под Windows, а C++Builder повышает производительность работы благодаря визуальному конструктору, мощному встроенному VCL RTL и расширенных функций параллельной компиляции. Кроме того, они основаны на разных компиляторах C++, поэтому это не совсем прямое сравнение; на самом деле, они дополняют друг друга.

Сторонние бенчмарки (не относящиеся к проекту из поста) для 3990X с TwineCompile:


  • Параметры машины: AMD Ryzen Threadripper 3990X (2,9 ГГц, 64 ядра, 128 потоков)
  • Конфигурация: IDE Compile.
  • Результаты: без TwineCompile, с TwineCompile
  • 3:35:02, 0:05:44

Параллельная компиляция в Dev-C++


В начале нашего квеста Dev-C++ не поддерживал флаг -j MAKE, поэтому эту задачу нужно было решить в первую очередь. Мне удалось обновить Dev-C++ и выпустить новую версию v6.3 со встроенным флагом -j для параллельной компиляции. Кроме того, теперь он по умолчанию используется для релизных сборок, что должно значительно снизить время компиляции для пользователей Dev-C++. Обновление необходимо было выпустить, потому что флаг командной строки нужно было добавить в MAKE, а не в командную строку компилятора. На реализацию потребовалось несколько дней, после чего была выпущена новая версия v6.3. В комплекте с этим релизом были выпущены все исправления ошибок за последние два месяца и вторая новая функций выбора произвольных встроенных консольных приложений. Вот примечания к версии Dev-C++ v6.3:

Версия 6.3 — 30 января 2021 года

  • Добавлено: по умолчанию для релизных сборок включена параллельная компиляция при помощи MAKE Jobs.
  • Добавлено: 3 кнопки для настройки пользовательских вкладок командной строки.
  • Обновлено: завершение кода и меню для тёмных тем.
  • Обновлено: обтекание выбора вкладки редактора по CTRL-TAB.
  • Исправлено: проблема с удалением файла Make clean.
  • Исправлено: в строке состояния не отображается весь текст.
  • Исправлено: проблема столбца hex окна Debug/CPU.
  • Исправлено: закрытие вкладок в редакторе в режиме side by side.

Получив IDE Dev-C++, способную параллельно компилировать 1 миллиард строк на C++, мне нужно было добыть сам AMD Threadripper 3990X с 64 ядрами и 128 потоками. Threadripper имеет меньшие оценки на каждое ядро по PassMark, чем 5950X, но поскольку он имеет больше ядер, в сумме оценка выше. Показанный ниже скриншот сделан в PassMark и демонстрирует сравнение двух процессоров. Как видите, бенчмарк одного ядра 5950x равен 3491, а у 3990x он равен 2553. Однако общий многоядерный бенчмарк 3990x равен 80752, а у 5950x — всего 46045.
p5i717ujuvy0olvx2nih4vjug5u.png


Примечание: в видео не упоминается более мощный 64-ядерный Threadripper 3990X, использованный в этом посте.

У ReliableSite.net есть облачные машины на основе AMD Threadripper 3990X с 256 ГБ ОЗУ, удовлетворяющие требованиям нашего проекта. Можно выбрать два варианта конфигурации Windows: Windows Standard 2016 и Windows Standard 2019. Я выбрал Windows 2016 и сервис попытался установить на машину эту ОС, но не смог этого сделать ни на одном релизе; вероятно, это связано с проблемой лицензирования Microsoft процессоров и ядер в Windows Standard 2016. Как бы то ни было, ОС сменили на Windows Standard 2019 и всё установилось нормально.

q88xscmmqe0ltuwmlh0k9m4pngy.jpeg

Итак, у нас есть работающая машина с Windows 2019 на Threadripper и C++Builder с TwineCompile, плюс Dev-C++ v6.3 со встроенной поддержкой параллельной компиляции. Всё тестируется и работает замечательно. C++Builder смог скомпилировать 1 миллион строк на C++ из предыдущего поста в четыре раза быстрее, чем на 5950x, а Delphi смогла скомпилировать 1 миллиард строк проектов Object Pascal в 2,5 раза быстрее. Эти два сравнения мы оставим на следующие посты.

Одним из инструментов, используемых для замера использования процессорных ресурсов, стал Task Manager DeLuxe компании MiTeC. Task Manager DeLuxe удивляет объёмом предоставляемой о Windows информации. В TMX есть тёмный (актуальный для 2021 года) и светлый режим. TMX производит компания MiTeC, также создающая широкий ассортимент компонентов Delphi, предоставляющих доступ к большому объёму информации, которую можно найти в TMX. Вероятно, бОльшую часть информации из TMX можно использовать в своём приложении при помощи MiTeC System Information Component Suite.

Когда я впервые запустил Task Manager DeLuxe на машине с 64-ядерным Threadripper 3990x, он не смог отобразить графики для отдельных процессоров и выдал ошибку. У меня коммерческая лицензия на Task Manager DeLuxe, поэтому я отправил письмо Михалу из MiTeC и ему удалось быстро решить проблему. Он выпустил новую версию Task Manager DeLuxe, которая теперь отлично запускается и работает на машине с 64 ядрами.

qwho0hl-ngv0rbn5r1fllj9l5pa.png

Следующей задачей стало непосредственное создание проекта из 1 миллиарда строк на C++, чтобы его можно было скомпилировать. Я начал с этого проекта Scimark2 для Dev-C++ и разработал приложение на Delphi для быстрой генерации нужного количества строк кода. В конечном итоге я хотел запустить приложение, созданное из 1 миллиардов строк C++. Delphi-приложение берёт файлы LU.c и LU.h и дублирует последнюю функцию LU_factor () столько раз, чтобы получилось нужное количество строк. Сама функция состоит из 69 строк, и чтобы избежать коллизий имён, каждая сгенерированная функция имеет имя файла и номер итерации.
rgos4vrjoqvvuwzge0jij1qmuxi.png

Я попробовал несколько разных способов нарезки файлов проекта на C++, чтобы получить больше файлов и меньше строк или больше строк и меньше файлов. В проекте Delphi я создал 4 миллиона строк, разбросанных по 250 проектам. Для проекта на C++ один из способов нарезки заключался в создании 32 000 файлов из 31 250 строк на файл. Я пришёл к этому числу после тестирования, потому что мне показалось, что Dev-C++ лучше работает с мелкими файлами, большим количеством файлов для большого количества ядер, и что большое количество мелких файлов лучше имитирует реальный проект. Второй способ — 10 666 файлов по 93 750 строк на файл. Третий способ — 1000 файлов и 1000000 строк C++ на файл. Список файлов добавляется в файл проекта Dev-C++ после их генерации, то есть среде Dev-C++ нужно будет загрузить этот список файлов в свой список проекта.

Я обнаружил здесь узкое место — в Dev-C++ есть функции автодополнения кода и символов. Эти функции парсят файлы в проекте при его открытии, и достаточно сказать, что этот процесс ещё не распараллелен. Рано или поздно Dev-C++ загружается, но на обработку 32 тысяч файлов уходит довольно много времени (и даже 10 666 файлов). После того, как я с этим разобрался, я отключил автодополнения кода и символов, что позволило быстро загрузить проект с 1 миллиардом строк кода на C++. Похоже, Dev-C++ не испытывает никаких проблем с редактированием файла из 1 миллиона строк и работа ощущается довольно плавной.

Я столкнулся со второй проблемой — процедура Delphi System.CPUCount сообщает, что есть не 128, а 32 потока. Вероятно, когда писали процедуру System.CPUCount, казалось, что 32 ядер будет достаточно, но мы уже давно миновали этот рубеж. В случае 5950X, имеющего 16 ядер и 32 потоков, процедура работает отлично, но в случае 3990X ошибается. Я сообщил об этой проблеме на портале Embarcadero Quality, однако уже существует сторонняя библиотека NumCPULib4Pascal, которая должна сообщать правильное значение. А пока я создал свою сборку исполняемого файла Dev-C++ и жёстко прописал в нём 128 потоков.

Мы уже почти готовы приступать к компиляции 1 миллиарда строк кода! У нас есть оборудование, есть IDE, готовые компиляторы и проекты (нарезанные разным образом). В течение всего процесса я компилировал версии проекта на 1 миллиард строк C++ с разными размерами, чтобы выявить и устранить вышеупомянутые проблемы.

rgmvcfleepcrtixirqae0drhowi.png

Давайте начнём с проекта на 1 миллиард строк, разделённого на 32 000 по 31 250 строк. Этот проект компилируется. Как и следует, он использует все ядра, но когда дело доходит до компоновки 32 000 файлов в один исполняемый, он начинает простаивать. Существует ограничение командную строки, не позволяющее передать компоновщику 32 000 файлов. Максимальная длина командной строки Windows составляет 32768 байт, то есть USHORT в Windows API. Второй проект с 10 666 файлами и 93 750 строками на файл тоже компилируется, но простаивает по той же причине.

Третий проект с 1000 файлами и 1 000 000 строк на файл компилируется, но более медленно. В процессе компиляции он не использует все 128 ядер. При выборе -j64, -j128 и -j (автоматический выбор) в MAKE видно, что реально работают только примерно 34 из 64 ядер, хоть и исполняют 64 процесса g++. Во время этого процесса задействуется 81 ГБ ОЗУ, так что хорошо, что машина имеет 256 ГБ. Хотя после исполнения командной строки все файлы компилируются, сам компоновщик вылетает с ошибкой, пытаясь объединить все объектные файлы в исполняемый. Пока все найденные на StackOverflow советы по использованию различных аргументов командной строки не позволили решить проблему.

g++.exe scimark2.o FFT.o LU.o MonteCarlo.o SOR.o SparseCompRow.o Stopwatch.o Random.o kernel.o array.o LU0.o LU1.o LU2.o LU3.o LU4.o LU5.o LU6.o LU7.o LU8.o LU9.o LU10.o LU11.o LU12.o LU13.o LU14.o LU15.o LU16.o LU17.o LU18.o LU19.o LU20.o LU21.o LU22.o LU23.o LU24.o LU25.o LU26.o LU27.o LU28.o LU29.o LU30.o LU31.o LU32.o LU33.o LU34.o LU35.o LU36.o LU37.o LU38.o LU39.o LU40.o LU41.o LU42.o LU43.o LU44.o LU45.o LU46.o LU47.o LU48.o LU49.o LU50.o LU51.o LU52.o LU53.o LU54.o LU55.o LU56.o LU57.o LU58.o LU59.o LU60.o LU61.o LU62.o LU63.o LU64.o LU65.o LU66.o LU67.o LU68.o LU69.o LU70.o LU71.o LU72.o LU73.o LU74.o LU75.o LU76.o LU77.o LU78.o LU79.o LU80.o LU81.o LU82.o LU83.o LU84.o LU85.o LU86.o LU87.o LU88.o LU89.o LU90.o LU91.o LU92.o LU93.o LU94.o LU95.o LU96.o LU97.o LU98.o LU99.o LU100.o LU101.o LU102.o LU103.o LU104.o LU105.o LU106.o LU107.o LU108.o LU109.o LU110.o LU111.o LU112.o LU113.o LU114.o LU115.o LU116.o LU117.o LU118.o LU119.o LU120.o LU121.o LU122.o LU123.o LU124.o LU125.o LU126.o LU127.o LU128.o LU129.o LU130.o LU131.o LU132.o LU133.o LU134.o LU135.o LU136.o LU137.o LU138.o LU139.o LU140.o LU141.o LU142.o LU143.o LU144.o LU145.o LU146.o LU147.o LU148.o LU149.o LU150.o LU151.o LU152.o LU153.o LU154.o LU155.o LU156.o LU157.o LU158.o LU159.o LU160.o LU161.o LU162.o LU163.o LU164.o LU165.o LU166.o LU167.o LU168.o LU169.o LU170.o LU171.o LU172.o LU173.o LU174.o LU175.o LU176.o LU177.o LU178.o LU179.o LU180.o LU181.o LU182.o LU183.o LU184.o LU185.o LU186.o LU187.o LU188.o LU189.o LU190.o LU191.o LU192.o LU193.o LU194.o LU195.o LU196.o LU197.o LU198.o LU199.o LU200.o LU201.o LU202.o LU203.o LU204.o LU205.o LU206.o LU207.o LU208.o LU209.o LU210.o LU211.o LU212.o LU213.o LU214.o LU215.o LU216.o LU217.o LU218.o LU219.o LU220.o LU221.o LU222.o LU223.o LU224.o LU225.o LU226.o LU227.o LU228.o LU229.o LU230.o LU231.o LU232.o LU233.o LU234.o LU235.o LU236.o LU237.o LU238.o LU239.o LU240.o LU241.o LU242.o LU243.o LU244.o LU245.o LU246.o LU247.o LU248.o LU249.o LU250.o LU251.o LU252.o LU253.o LU254.o LU255.o LU256.o LU257.o LU258.o LU259.o LU260.o LU261.o LU262.o LU263.o LU264.o LU265.o LU266.o LU267.o LU268.o LU269.o LU270.o LU271.o LU272.o LU273.o LU274.o LU275.o LU276.o LU277.o LU278.o LU279.o LU280.o LU281.o LU282.o LU283.o LU284.o LU285.o LU286.o LU287.o LU288.o LU289.o LU290.o LU291.o LU292.o LU293.o LU294.o LU295.o LU296.o LU297.o LU298.o LU299.o LU300.o LU301.o LU302.o LU303.o LU304.o LU305.o LU306.o LU307.o LU308.o LU309.o LU310.o LU311.o LU312.o LU313.o LU314.o LU315.o LU316.o LU317.o LU318.o LU319.o LU320.o LU321.o LU322.o LU323.o LU324.o LU325.o LU326.o LU327.o LU328.o LU329.o LU330.o LU331.o LU332.o LU333.o LU334.o LU335.o LU336.o LU337.o LU338.o LU339.o LU340.o LU341.o LU342.o LU343.o LU344.o LU345.o LU346.o LU347.o LU348.o LU349.o LU350.o LU351.o LU352.o LU353.o LU354.o LU355.o LU356.o LU357.o LU358.o LU359.o LU360.o LU361.o LU362.o LU363.o LU364.o LU365.o LU366.o LU367.o LU368.o LU369.o LU370.o LU371.o LU372.o LU373.o LU374.o LU375.o LU376.o LU377.o LU378.o LU379.o LU380.o LU381.o LU382.o LU383.o LU384.o LU385.o LU386.o LU387.o LU388.o LU389.o LU390.o LU391.o LU392.o LU393.o LU394.o LU395.o LU396.o LU397.o LU398.o LU399.o LU400.o LU401.o LU402.o LU403.o LU404.o LU405.o LU406.o LU407.o LU408.o LU409.o LU410.o LU411.o LU412.o LU413.o LU414.o LU415.o LU416.o LU417.o LU418.o LU419.o LU420.o LU421.o LU422.o LU423.o LU424.o LU425.o LU426.o LU427.o LU428.o LU429.o LU430.o LU431.o LU432.o LU433.o LU434.o LU435.o LU436.o LU437.o LU438.o LU439.o LU440.o LU441.o LU442.o LU443.o LU444.o LU445.o LU446.o LU447.o LU448.o LU449.o LU450.o LU451.o LU452.o LU453.o LU454.o LU455.o LU456.o LU457.o LU458.o LU459.o LU460.o LU461.o LU462.o LU463.o LU464.o LU465.o LU466.o LU467.o LU468.o LU469.o LU470.o LU471.o LU472.o LU473.o LU474.o LU475.o LU476.o LU477.o LU478.o LU479.o LU480.o LU481.o LU482.o LU483.o LU484.o LU485.o LU486.o LU487.o LU488.o LU489.o LU490.o LU491.o LU492.o LU493.o LU494.o LU495.o LU496.o LU497.o LU498.o LU499.o LU500.o LU501.o LU502.o LU503.o LU504.o LU505.o LU506.o LU507.o LU508.o LU509.o LU510.o LU511.o LU512.o LU513.o LU514.o LU515.o LU516.o LU517.o LU518.o LU519.o LU520.o LU521.o LU522.o LU523.o LU524.o LU525.o LU526.o LU527.o LU528.o LU529.o LU530.o LU531.o LU532.o LU533.o LU534.o LU535.o LU536.o LU537.o LU538.o LU539.o LU540.o LU541.o LU542.o LU543.o LU544.o LU545.o LU546.o LU547.o LU548.o LU549.o LU550.o LU551.o LU552.o LU553.o LU554.o LU555.o LU556.o LU557.o LU558.o LU559.o LU560.o LU561.o LU562.o LU563.o LU564.o LU565.o LU566.o LU567.o LU568.o LU569.o LU570.o LU571.o LU572.o LU573.o LU574.o LU575.o LU576.o LU577.o LU578.o LU579.o LU580.o LU581.o LU582.o LU583.o LU584.o LU585.o LU586.o LU587.o LU588.o LU589.o LU590.o LU591.o LU592.o LU593.o LU594.o LU595.o LU596.o LU597.o LU598.o LU599.o LU600.o LU601.o LU602.o LU603.o LU604.o LU605.o LU606.o LU607.o LU608.o LU609.o LU610.o LU611.o LU612.o LU613.o LU614.o LU615.o LU616.o LU617.o LU618.o LU619.o LU620.o LU621.o LU622.o LU623.o LU624.o LU625.o LU626.o LU627.o LU628.o LU629.o LU630.o LU631.o LU632.o LU633.o LU634.o LU635.o LU636.o LU637.o LU638.o LU639.o LU640.o LU641.o LU642.o LU643.o LU644.o LU645.o LU646.o LU647.o LU648.o LU649.o LU650.o LU651.o LU652.o LU653.o LU654.o LU655.o LU656.o LU657.o LU658.o LU659.o LU660.o LU661.o LU662.o LU663.o LU664.o LU665.o LU666.o LU667.o LU668.o LU669.o LU670.o LU671.o LU672.o LU673.o LU674.o LU675.o LU676.o LU677.o LU678.o LU679.o LU680.o LU681.o LU682.o LU683.o LU684.o LU685.o LU686.o LU687.o LU688.o LU689.o LU690.o LU691.o LU692.o LU693.o LU694.o LU695.o LU696.o LU697.o LU698.o LU699.o LU700.o LU701.o LU702.o LU703.o LU704.o LU705.o LU706.o LU707.o LU708.o LU709.o LU710.o LU711.o LU712.o LU713.o LU714.o LU715.o LU716.o LU717.o LU718.o LU719.o LU720.o LU721.o LU722.o LU723.o LU724.o LU725.o LU726.o LU727.o LU728.o LU729.o LU730.o LU731.o LU732.o LU733.o LU734.o LU735.o LU736.o LU737.o LU738.o LU739.o LU740.o LU741.o LU742.o LU743.o LU744.o LU745.o LU746.o LU747.o LU748.o LU749.o LU750.o LU751.o LU752.o LU753.o LU754.o LU755.o LU756.o LU757.o LU758.o LU759.o LU760.o LU761.o LU762.o LU763.o LU764.o LU765.o LU766.o LU767.o LU768.o LU769.o LU770.o LU771.o LU772.o LU773.o LU774.o LU775.o LU776.o LU777.o LU778.o LU779.o LU780.o LU781.o LU782.o LU783.o LU784.o LU785.o LU786.o LU787.o LU788.o LU789.o LU790.o LU791.o LU792.o LU793.o LU794.o LU795.o LU796.o LU797.o LU798.o LU799.o LU800.o LU801.o LU802.o LU803.o LU804.o LU805.o LU806.o LU807.o LU808.o LU809.o LU810.o LU811.o LU812.o LU813.o LU814.o LU815.o LU816.o LU817.o LU818.o LU819.o LU820.o LU821.o LU822.o LU823.o LU824.o LU825.o LU826.o LU827.o LU828.o LU829.o LU830.o LU831.o LU832.o LU833.o LU834.o LU835.o LU836.o LU837.o LU838.o LU839.o LU840.o LU841.o LU842.o LU843.o LU844.o LU845.o LU846.o LU847.o LU848.o LU849.o LU850.o LU851.o LU852.o LU853.o LU854.o LU855.o LU856.o LU857.o LU858.o LU859.o LU860.o LU861.o LU862.o LU863.o LU864.o LU865.o LU866.o LU867.o LU868.o LU869.o LU870.o LU871.o LU872.o LU873.o LU874.o LU875.o LU876.o LU877.o LU878.o LU879.o LU880.o LU881.o LU882.o LU883.o LU884.o LU885.o LU886.o LU887.o LU888.o LU889.o LU890.o LU891.o LU892.o LU893.o LU894.o LU895.o LU896.o LU897.o LU898.o LU899.o LU900.o LU901.o LU902.o LU903.o LU904.o LU905.o LU906.o LU907.o LU908.o LU909.o LU910.o LU911.o LU912.o LU913.o LU914.o LU915.o LU916.o LU917.o LU918.o LU919.o LU920.o LU921.o LU922.o LU923.o LU924.o LU925.o LU926.o LU927.o LU928.o LU929.o LU930.o LU931.o LU932.o LU933.o LU934.o LU935.o LU936.o LU937.o LU938.o LU939.o LU940.o LU941.o LU942.o LU943.o LU944.o LU945.o LU946.o LU947.o LU948.o LU949.o LU950.o LU951.o LU952.o LU953.o LU954.o LU955.o LU956.o LU957.o LU958.o LU959.o LU960.o LU961.o LU962.o LU963.o LU964.o LU965.o LU966.o LU967.o LU968.o LU969.o LU970.o LU971.o LU972.o LU973.o LU974.o LU975.o LU976.o LU977.o LU978.o LU979.o LU980.o LU981.o LU982.o LU983.o LU984.o LU985.o LU986.o LU987.o LU988.o LU989.o LU990.o LU991.o LU992.o LU993.o LU994.o LU995.o LU996.o LU997.o LU998.o LU999.o -o Scimark2.exe -L"C:/Program Files (x86)/Embarcadero/Dev-Cpp/TDM-GCC-64/lib" -L"C:/Program Files (x86)/Embarcadero/Dev-Cpp/TDM-GCC-64/x86_64-w64-mingw32/lib" -static-libgcc -mcmodel=large -fPIC -Wl,--image-base -Wl,0x10000000

C:/Program Files (x86)/Embarcadero/Dev-Cpp/TDM-GCC-64/bin/../lib/gcc/x86_64-w64-mingw32/9.2.0/../../../../x86_64-w64-mingw32/lib/../lib/crt2.o: in function `check_managed_app':

C:/crossdev/src/mingw-w64-v7-git20191109/mingw-w64-crt/crt/crtexe.c:364:(.text+0x17): 
relocation truncated to fit: R_X86_64_PC32 against symbol `.refptr.mingw_initltsdrot_force' defined in .rdata$.refptr.mingw_initltsdrot_force[.refptr.mingw_initltsdrot_force] section in C:/Program Files (x86)/Embarcadero/Dev-Cpp/TDM-GCC-64/bin/../lib/gcc/x86_64-w64-mingw32/9.2.0/../../../../x86_64-w64-mingw32/lib/../lib/crt2.o
C:/crossdev/src/mingw-w64-v7-git20191109/mingw-w64-crt/crt/crtexe.c:365:(.text+0x26): 
relocation truncated to fit: R_X86_64_PC32 against symbol `.refptr.mingw_initltsdyn_force' defined in .rdata$.refptr.mingw_initltsdyn_force[.refptr.mingw_initltsdyn_force] section in C:/Program Files (x86)/Embarcadero/Dev-Cpp/TDM-GCC-64/bin/../lib/gcc/x86_64-w64-mingw32/9.2.0/../../../../x86_64-w64-mingw32/lib/../lib/crt2.o
C:/crossdev/src/mingw-w64-v7-git20191109/mingw-w64-crt/crt/crtexe.c:366:(.text+0x33): 
relocation truncated to fit: R_X86_64_PC32 against symbol `.refptr.mingw_initltssuo_force' defined in .rdata$.refptr.mingw_initltssuo_force[.refptr.mingw_initltssuo_force] section in C:/Program Files (x86)/Embarcadero/Dev-Cpp/TDM-GCC-64/bin/../lib/gcc/x86_64-w64-mingw32/9.2.0/../../../../x86_64-w64-mingw32/lib/../lib/crt2.o

C:/crossdev/src/mingw-w64-v7-git20191109/mingw-w64-crt/crt/crtexe.c:367:(.text+0x40): 
relocation truncated to fit: R_X86_64_PC32 against symbol `.refptr.mingw_initcharmax' defined in .rdata$.refptr.mingw_initcharmax[.refptr.mingw_initcharmax] section in C:/Program Files (x86)/Embarcadero/Dev-Cpp/TDM-GCC-64/bin/../lib/gcc/x86_64-w64-mingw32/9.2.0/../../../../x86_64-w64-mingw32/lib/../lib/crt2.o
C:/crossdev/src/mingw-w64-v7-git20191109/mingw-w64-crt/crt/crtexe.c:370:(.text+0x4d): 
relocation truncated to fit: R_X86_64_PC32 against symbol `.refptr.__image_base__' defined in .rdata$.refptr.__image_base__[.refptr.__image_base__] section in C:/Program Files (x86)/Embarcadero/Dev-Cpp/TDM-GCC-64/bin/../lib/gcc/x86_64-w64-mingw32/9.2.0/../../../../x86_64-w64-mingw32/lib/../lib/crt2.o
C:/Program Files (x86)/Embarcadero/Dev-Cpp/TDM-GCC-64/bin/../lib/gcc/x86_64-w64-mingw32/9.2.0/../../../../x86_64-w64-mingw32/lib/../lib/crt2.o: 
in function `pre_c_init':
C:/crossdev/src/mingw-w64-v7-git20191109/mingw-w64-crt/crt/crtexe.c:141:(.text+0x6a): 
relocation truncated to fit: R_X86_64_PC32 against symbol `.refptr.mingw_app_type' defined in .rdata$.refptr.mingw_app_type[.refptr.mingw_app_type] section in C:/Program Files (x86)/Embarcadero/Dev-Cpp/TDM-GCC-64/bin/../lib/gcc/x86_64-w64-mingw32/9.2.0/../../../../x86_64-w64-mingw32/lib/../lib/crt2.o

C:/crossdev/src/mingw-w64-v7-git20191109/mingw-w64-crt/crt/crtexe.c:140:(.text+0x70): relocation truncated to fit: R_X86_64_PC32 against `.bss'
C:/crossdev/src/mingw-w64-v7-git20191109/mingw-w64-crt/crt/crtexe.c:144:(.text+0x80): relocation truncated to fit: R_X86_64_PC32 against symbol `__set_app_type' defined in .text section in C:/Program Files (x86)/Embarcadero/Dev-Cpp/TDM-GCC-64/x86_64-w64-mingw32/lib/libmsvcrt.a(dwngs00096.o)

C:/crossdev/src/mingw-w64-v7-git20191109/mingw-w64-crt/crt/crtexe.c:146:(.text+0x85): relocation truncated to fit: R_X86_64_PC32 against symbol `__p__fmode' defined in .text section in C:/Program Files (x86)/Embarcadero/Dev-Cpp/TDM-GCC-64/x86_64-w64-mingw32/lib/libmsvcrt.a(lib64_libmsvcrt_os_a-__p__fmode.o)
C:/crossdev/src/mingw-w64-v7-git20191109/mingw-w64-crt/crt/crtexe.c:146:(.text+0x8c): relocation truncated to fit: R_X86_64_PC32 against symbol `.refptr._fmode' defined in .rdata$.refptr._fmode[.refptr._fmode] section in C:/Program Files (x86)/Embarcadero/Dev-Cpp/TDM-GCC-64/bin/../lib/gcc/x86_64-w64-mingw32/9.2.0/../../../../x86_64-w64-mingw32/lib/../lib/crt2.o

C:/crossdev/src/mingw-w64-v7-git20191109/mingw-w64-crt/crt/crtexe.c:152:(.text+0x95): additional relocation overflows omitted from the output
collect2.exe: error: ld returned 1 exit status
C:DScimark2-Dev-Cpp-masterMakefile.win:25: recipe for target 'Scimark2.exe' failed
mingw32-make.exe: *** [Scimark2.exe] Error 1

После тестирования становится очевидно, что препятствием, вызывающим данную ошибку, является ограничение в 2 ГБ на размер исполняемого файла (несмотря на использование -mcmodel=medium или -mcmodel=large). Мне удалось заставить компилироваться 100 файлов с 1 000 000 строк на файл, и сгенерировался исполняемый файл размером примерно 1,1 ГБ. Я начал использовать флаг -Os (оптимизирующий размер), и это сдвинуло проект немного вперёд. Здесь стоит заметить, что чем больше исполняемый файл, тем хуже бенчмарк Scimark2, и это интересно. Первая успешная компиляция 1 миллиарда строк из 1000 файлов по 1 000 000 строк с флагом -Os сгенерировала за 1483 секунд (24,7 минуты) исполняемый файл размером 359 МБ. Также я попробовал 500 файлов по 2 000 000 строк и компиляция заняла больше времени. Стандартный проект Scimark2 в четыре раза быстрее, чем проект с дополнительным 1 миллиардом строк, когда исполняемый файл больше и применяется флаг -Os.
02ecwdg-kcypbqs6ekevm6eppmg.png

500 файлов по 2 000 000 строк использовало до 156 ГБ, но не все 64 ядра.

Мне не кажется, что это время компиляции точно характеризует Threadripper 3990x, поскольку при 1 миллионе и 2 миллионах строк кода на файл использовались не все ядра. Не знаю, проблема ли это MAKE и G++, или параметра -j, при котором количество ядер выбирается автоматически. Возможно, даже существует узкое место ввода-вывода машины, не позволяющее ей справляться с нагрузкой. Чем мельче файлы, тем больше ядер использует комбинация из MAKE/G++ и -j. Я также попробовал сравнить работу с флагом -pipe и без него (он позволяет использовать во время компиляции вместо файлов конвейеры). Любопытно здесь и то, что TwineCompile в C++Builder, похоже, не имеет такого ограничения. При использовании его при параллельной компиляции мгновенно запускаются все ядра.

Четверная компиляция


Попытавшись ускорить компиляцию 1 миллиарда строк кода на C++, я загрузил 4 экземпляра Dev-C++ с 250 файлами по 1 000 000 строк в проекте и скомпилировал все четыре проекта одновременно. Это похоже на проект с 1 миллиардом строк на Object Pascal, потому что в нём компилировалось 250 проектов с 4 миллионами строк кода на проект. Ниже показаны результаты четверной компиляции.
7qw5gm9k3lg7b7-pfh4di9hsnlo.png

Четыре экземпляра Dev-C++
lupzqahqm1zvh48xmq2ohgu3a2k.png

iz2noafolmvtjl7t9nbb6crqr8y.png

Примечание: на этом скриншоте есть баг — отображается всего 32 ядра и 64 потока, хотя на самом деле должно быть 64 ядра и 128 потоков.
mfeuqmxhlq6ieobvznz9frekdrm.png

oszgapvupb4kfjnhtdohe9xsscm.png

6ystvblvmwm3gzlygrh9jsu1-ys.png

5abyt8qepg8duoatyk0p4cwrmoo.png

Результаты компиляции…


  • Ошибок: 0
  • Предупреждений: 0
  • Файл вывода: C: DScimark2-Dev-Cpp-master_250_1m_DScimark2.exe
  • Размер вывода: 90,0009765625 МиБ
  • Время компиляции: 906,58 с

Результаты компиляции…


  • Ошибки: 0
  • Предупреждения: 0
  • Файл вывода: C: DScimark2-Dev-Cpp-master_250_1m_CScimark2.exe
  • Размер вывода: 90,0009765625 МиБ
  • Время компиляции: 909,45 с

Результаты компиляции…


  • Ошибки: 0
  • Предупреждения: 0
  • Файл вывода: C: DScimark2-Dev-Cpp-master_250_1m_AScimark2.exe
  • Размер вывода: 90,0009765625 МиБ
  • Время компиляции: 915,17 с

Результаты компиляции…


  • Ошибки: 0
  • Предупреждения: 0
  • Файл вывода: C: DScimark2-Dev-Cpp-master_250_1m_BScimark2.exe
  • Размер вывода: 90,0009765625 МиБ
  • Время компиляции: 918,05 с

kvijfroubtp4s2zrbviczfsi82s.png

1 миллиард строк кода на C++ за 15 минут на AMD Threadripper 3990X


Этот проект был очень интересным. Есть целая куча флагов C++ для компилятора TDM-GCC наподобие -mtune=native, -mtune=znver2 и -mtune=znver3, которые я в этой конфигурации не пробовал. Как мы увидели из поста, программная поддержка современной машины с 64 ядрами и 128 потоками всё ещё требует совершенствования, но в целом работает и обеспечивает довольно серьёзную вычислительную мощь.

На правах рекламы


Прямо сейчас вы можете заказать мощные серверы, которые используют новейшие процессоры AMD Epyc. Гибкие тарифы — от 1 ядра CPU до безумных 128 ядер CPU, 512 ГБ RAM, 4000 ГБ NVMe.

8p3vz47nluspfyc0axlkx88gdua.png

© Habrahabr.ru