Русский
Русский
English
Статистика
Реклама

Компиляция

Тривиальная и неправильная облачная компиляция

28.01.2021 00:21:03 | Автор: admin


Введение


Данная статья не история успеха, а скорее руководство как не надо делать. Весной 2020 для поддержания спортивного тонуса участвовал в студенческом хакатоне (спойлер: заняли 2-е место). Удивительно, но задача из полуфинала оказалась более интересной и сложной чем финальная. Как вы поняли, о ней и своём решении расскажу под катом.


Задача


Данный кейс был предложен Deutsche Bank в направлении WEB-разработка.
Необходимо было разработать онлайн-редактор для проекта Алгосимулятор тестового стенда для проверки работы алгоритмов электронной торговли на языке Java. Каждый алгоритм реализуется в виде наследника класса AbstractTradingAlgorythm.


AbstractTradingAlgorythm.java
public abstract class AbstractTradingAlgorithm {    abstract void handleTicker(Ticker ticker) throws Exception;    public void receiveTick(String tick) throws Exception {        handleTicker(Ticker.parse(tick));    }    static class Ticker {        String pair;        double price;       static Ticker parse(String tick) {           Ticker ticker = new Ticker();           String[] tickerSplit = tick.split(",");           ticker.pair = tickerSplit[0];           ticker.price = Double.valueOf(tickerSplit[1]);           return ticker;       }    }}

Сам же редактор во время работы говорит тебе три вещи:


  1. Наследуешь ли ты правильный класс
  2. Будут ли ошибки на этапе компиляции
  3. Успешен ли тестовый прогон алгоритма. В данном случае подразумевается, что "В результате вызова new <ClassName>().receiveTick(RUBHGD,100.1) отсутствуют runtime exceptions".


Ну окей, скелет веб-сервиса через spring накидать дело на 5-10 минут. Пункт 1 работа для регулярных выражений, поэтому даже думать об этом сейчас не буду. Для пункта 2 можно конечно написать синтаксический анализатор, но зачем, когда это уже сделали за меня. Может и пункт 3 получится сделать, использовав наработки по пункту 2. В общем, дело за малым, уместить в один метод, ну например, компиляцию исходного кода программы на Java, переданного в контроллер строкой.


Решение


Здесь и начинается самое интересное. Забегая вперёд, как сделали другие ребята: установили на машину джаву, отдавали команды на ось и грепали stdout. Конечно, это более универсальный метод, но во-первых, нам сказали слово Java, а во-вторых...



у каждого свой путь.


Естественно, Java окружение устанавливать и настраивать всё же придётся. Правда компилировать и исполнять код мы будем не в терминале, а, как бы это ни звучало, в коде. Начиная с 6 версии, в Java SE присутствует пакет javax.tools, добавленный в стандартный API для компиляции исходного кода Java.
Теперь привычные понятия такие, как файлы с исходным кодом, параметры компилятора, каталоги с выходными файлами, сообщения компилятора, превратились в абстракции, используемые при работе с интерфейсом JavaCompiler, через реализации которого ведётся основная работа с задачами компиляции. Подробней о нём можно прочитать в официальной документации. Главное, что оттуда сейчас перейдёт моментально в текст статьи, это класс JavaSourceFromString. Дело в том, что, по умолчанию, исходный код загружается из файловой системы. В нашем же случае исходный код будет приходить строкой извне.


JavaSourceFromString.java
import javax.tools.SimpleJavaFileObject;import java.net.URI;public class JavaSourceFromString extends SimpleJavaFileObject {    final String code;    public JavaSourceFromString(String name, String code) {        super(URI.create("string:///" + name.replace('.', '/') + Kind.SOURCE.extension), Kind.SOURCE);        this.code = code;    }    @Override    public CharSequence getCharContent(boolean ignoreEncodingErrors) {        return code;    }}

Далее, в принципе уже ничего сложного нет. Получаем строку, имя класса и преобразуем их в объект JavaFileObject. Этот объект передаём в компилятор, компилируем и собираем вывод, который и возвращаем на клиент.
Сделаем класс Validator, в котором инкапсулируем процесс компиляции и тестового прогона некоторого исходника.


public class Validator {    private JavaSourceFromString sourceObject;    public Validator(String className, String source) {        sourceObject = new JavaSourceFromString(className, source);    }}

Далее добавим компиляцию.


public class Validator {    ...    public List<Diagnostic<? extends JavaFileObject>> compile() {        // получаем компилятор, установленный в системе        var compiler = ToolProvider.getSystemJavaCompiler();        // компилируем        var compilationUnits = Collections.singletonList(sourceObject);        var diagnostics = new DiagnosticCollector<JavaFileObject>();        compiler.getTask(null, null, diagnostics, null, null, compilationUnits).call();        // возворащаем диагностику        return diagnostics.getDiagnostics();    }}

Пользоваться этим можно как-то так.


public void MyMethod() {        var className = "TradeAlgo";        var sourceString = "public class TradeAlgo extends AbstractTradingAlgorithm{\n" +                "@Override\n" +                "    void handleTicker(Ticker ticker) throws Exception {\n" +                "       System.out.println(\"TradeAlgo::handleTicker\");\n" +                "    }\n" +                "}\n";        var validator = new Validator(className, sourceString);        for (var message : validator.compile()) {            System.out.println(message);        }    }

При этом, если компиляция прошла успешно, то возвращённый методом compile список будет пуст. Что интересно? А вот что.

На приведённом изображении вы можете видеть директорию проекта после завершения выполнения программы, во время выполнения которой была осуществлена компиляция. Красным прямоугольником обведены .class файлы, сгенерированные компилятором. Куда их девать, и как это чистить, не знаю жду в комментариях. Но что это значит? Что скомпилированные классы присоединяются в runtime, и там их можно использовать. А значит, следующий пункт задачи решается тривиально с помощью средств рефлексии.
Создадим вспомогательный POJO для хранения результата прогона.


TestResult.java
public class TestResult {    private boolean success;    private String comment;    public TestResult(boolean success, String comment) {        this.success = success;        this.comment = comment;    }    public boolean success() {        return success;    }    public String getComment() {        return comment;    }}

Теперь модифицируем класс Validator с учётом новых обстоятельств.


public class Validator {    ...    private String className;    private boolean compiled = false;    public Validator(String className, String source) {        this.className = className;        ...    }    ...    public TestResult testRun(String arg) {        var result = new TestResult(false, "Failed to compile");        if (compiled) {            try {                // загружаем класс                var classLoader = URLClassLoader.newInstance(new URL[]{new File("").toURI().toURL()});                var c = Class.forName(className, true, classLoader);                // создаём объект класса                var constructor = c.getConstructor();                var instance = constructor.newInstance();                // выполняем целевой метод                c.getDeclaredMethod("receiveTick", String.class).invoke(instance, arg);                result = new TestResult(true, "Success");            } catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException | ClassNotFoundException | RuntimeException | MalformedURLException | InstantiationException e) {                var sw = new StringWriter();                e.printStackTrace(new PrintWriter(sw));                result = new TestResult(false, sw.toString());            }        }        return result;    }}

Возвращаясь к предыдущему примеру использования, можно дописать туда такие строчки.


public void MyMethod() {        ...        var result = validator.testRun("RUBHGD,100.1");        System.out.println(result.success() + " " + result.getComment());    }

Вставить этот код в реализацию API контроллера задача нетрудная, поэтому подробности её решения можно опустить.


Какие проблемы?


  1. Ещё раз напомню про кучу .class файлов.


  2. Поскольку опять же идёт работа с компиляцией некоторых классов, есть риск отказа в записи любого непредвиденного .class файла.


  3. Самое главное, наличие уязвимости для инъекции вредосного кода на языке программирования Java. Ведь, на самом деле, пользователь может написать что угодно в теле вызываемого метода. Это может быть вызов полного перегруза системы, затирание всей файловой системы машины и тому подобное. В общем, исполняемую среду нужно изолировать, как минимум, а в рамках хакатона этим в команде никто естественно не занимался ввиду нехватки времени.



Поэтому делать в точности как я не надо)


P.S. Ссылка на гитхаб с исходным кодом из статьи.

Подробнее..

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

05.02.2021 14:16:53 | Автор: admin


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.




Примечание: в видео не упоминается более мощный 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 и всё установилось нормально.


Итак, у нас есть работающая машина с 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 ядрами.


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


Я попробовал несколько разных способов нарезки файлов проекта на 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++ с разными размерами, чтобы выявить и устранить вышеупомянутые проблемы.


Давайте начнём с проекта на 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,0x10000000C:/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.oC:/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.oC:/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.oC:/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.oC:/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.oC:/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.oC:/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.oC:/crossdev/src/mingw-w64-v7-git20191109/mingw-w64-crt/crt/crtexe.c:152:(.text+0x95): additional relocation overflows omitted from the outputcollect2.exe: error: ld returned 1 exit statusC:DScimark2-Dev-Cpp-masterMakefile.win:25: recipe for target 'Scimark2.exe' failedmingw32-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.


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 миллионами строк кода на проект. Ниже показаны результаты четверной компиляции.


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



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





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


  • Ошибок: 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 с


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.

Подробнее..

DLang, Vibe.d и кросс-компиляция для RPi4

18.06.2021 10:22:12 | Автор: admin

Добрый вечер!

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

Недавно я написал сервер на DLang с использованием библиотеки Vibe.d. Для него я пишу кросс-платформенный клиент. Основной моей системой является Arch, и мне этого более чем достаточно, но для тестирования некоторых платформозависимых вещей я перезагружаюсь в Windows 10.

Отсутствие в Windows пакетного менджера и тому подобных вещей отпугнуло меня от того, чтобы собирать сервер для нее, хотя это и возможно. Поэтому мне в голову пришло очень логичное решение - запустить сервер на моем Raspberry Pi 4. Сейчас я использую его для удаленного доступа к принтеру и сканеру.

Попытка 1. Компиляция на микрокомпьютере

Первое, что пришло мне в голову - скинуть исходники на целевое устройство, скачать туда компилятор, систему сборки (в моем случае DUB) и собрать все там. Я предполагал, что это будет немного дольше, но я всегда мог оставить этот процесс выполняться на ночь, но не тут-то было...

Исходники я быстро скинул при помощи scp, после чего подключился к микрокомпьютеру по ssh и начал разбираться с компилятором.

На целевом устройстве стоит основанный на Debian Raspbian 10 Buster. Для поиска пакетов придется использовать apt... По сравнению с pacman он гораздо менее удобный, выдает кучу лишнего мусора (ИМХО).

Эталонным компилятором для D является dmd, но поиск по пакетам не выдал ничего полезного. Как оказалось : первое - dmd нет в стандартном репозитории Debian и Ubuntu, второе - он не поддерживает arm.

Чтож... Ничего страшного, ведь для D есть еще минимум 2 полноценных компилятора. Один из них, GDC, является частью GNU-Compiler-Collection и, вероятно, найдется везде, где есть Linux. Второй же, ldc, использует LLVM для генерации кода, поэтому его вы можете найти практически везде (Можно даже скачать его на Android через Termux).

Именно эти два компилятора оказались доступны для загрузки на микрокомпьютер :

sudo apt install ldc2 gdc

Оставался только DUB - de facto система сборки и менджер пакетов для D. Он есть в стандартном репозитории, но он не работает... Проекты не инициализируются, при загрузке зависимостей не может установить соединение с сервером. Сплошное расстройство!

В одной из статей про сборку D для arm было указано про проблемы с DUB. В качестве решения там предлагалось собрать его самостоятельно из исходников. Почему нет? Спускаемя к исходникам : https://github.com/dlang/dub

Для сборки в репозитории есть скрипт build.d (Код на D может быть запущен как скрипт при помощи специального интерпретатора - rdmd для dmd, ldmd для ldc, gdmd для gdc). gdmd не установился вместе с gdc, так что будем использовать ldmd. Собираем :

ldmd -run build.d

Собрать-то собрали, но лучше от этого не стало. dub работает, но очень медленно. Загрузить все зависимости для Vibe.d ему так и не удалось...

Попытка 2. Кросс-компиляция через GDC

Первая статья, которая была про компиляцию D кода под arm предлагала использовать кросс версию gdc. Почему нет?

Идем на официальный сайт и скачиваем последнюю доступную версию : https://gdcproject.org/old/downloads . Первоначально меня смутила дата сборки компилятора, но что же поделать.

После загрузки и распаковки нужно было проверить работоспособность компилятора. Для этого я написал обычный для всех Hello world :

import std.stdio;int main(string [] args) {  writeln("Hello, World!");  return 0;}

Теперь нужно это откомпилить и проверить на целевой платформе. С тулчейном это делается достаточно просто (только вместо arm-linux-gnueabihf-gdc нужно указывать полный путь) :

arm-linux-gnueabihf-gdc app.d

Получается исполняемый файл. Скидываем его на Raspberry, выдаем разрешение на исполнение и... Работает!

Оставалось только собрать vibe.d. Для работы с HTTPS он использует криптографическую библиотеку : OpenSSl или Botan. В той же статье был представлен интересный метод, позволяющий избежать их ручной компиляции : смонтировать всю файловую систему микрокомпьютера и указать компилятору путь до библиотек, хранящихся на нем.

Я сделал все по инструкции и запустил сборку. К сожалению - неудача. С момента выхода скачанной мной версии GDC в языке появились новые конструкции, которые компилятор 2016 не поддерживает... Найти более новую версию GDC я не смог, так что необходимо было искать другие варианты.

Статья, которую я использовал для этого пункта.

Попытка 3. LDC и мутки с библиотеками

Остался только один компилятор, способный откомпилить нужную мне программу для arm - ldc. Он установлен у меня на основном компьютере при помощи pacman. Какие-то дополнительные действия не нужны.

Для компиляции под другую платформу нужно лишь указать специальный флаг и дополнительные библиотеки. Первоначально необходимо было собрать стандартную библиотеку D для arm. Делается это одной простой командой :

ldc-build-runtime --ninja --dFlags="-w;-mtriple=arm-linux-gnueabihf"

Появиться папка ldc-build-runtime.tmp/lib в которой храниться уже откомпилированная стандартная библиотека для arm. Для того чтобы скормить ее компилятору указываем следующий флаг : -L=-Lldc-build-runtime.tmp/lib
Пробуем собрать Hello World - ошибка линковки (не может найти точку входа). "Приехали", - подумал я. Собрал для начала объектный файл и решил попробовать слинковать его уже на raspberry - ld послал меня куда подальше.

Подумав и погадав, я решил скачать кросс-компилятор для C, взять линковщик оттуда и собрать все на основной машине. В Ubuntu и Debian arm-linux-gnueabihf есть в стандартном репозитории, а в Arch его можно было или собрать из AUR, или скачать уже собранный.

Раз уже делать, так делать - собираем из AUR. Процесс долгий, но интересный. Компиляция происходит в 3 этапа, постепенно собирая binutils, glibc и тп.

Спустя пару часов компилятор все же собрался. Я установил путь до него и собрал тестовую программу. Заработало!

Пробуем таким же образом слинковать другие библиотеки для проекта с Vibe.d... Не получилось. Линковщик стал таскать абсолютно все библиотеки из файловой системы raspberry и потерял половину ссылок. Undefined reference на malloc я еще не видел. До этого момента.

Не будем расстраиваться. Раз без включения библиотек от Raspberry все работает, просто локально соберем OpenSSL и ZLib (так же нужен), а потом прилинкуем их.

ZLib собрался довольно просто и быстро. Прилинковать его при помощи -L=-Lzlib/lib тоже не составило труда. Но вот OpenSSL отказался собираться от слова совсем. То ему компилятор не нравиться, то ему просто я не нравлюсь.

Из решений я нашел только одно - собрать OpenSSL на малине, скачать оттуда собранную и прилинковать на основной машине. На микрокомпьютере OpenSSL собрать было нетрудно. Я скачал его и добавил флаг -L=-Lopenssl/lib.

При компиляции я увидел всего 2 ошибки : неопределенная ссылка на SSL_get_peer_certificate и на ERR_put_error. Это меня доконало, я решил оставить это дело и лечь спать. Если бы я знал, как я был близок к победе на этом моменте.

Попытка 4. Toolchain и скрипты

На следующий день я решил попробовать все сначала, учесть все предыдущие ошибки и сделать что-то наподобие toolchainа для сборки D кода под ARM.

Начать я решил с кросс-компилятора для C. Загуглив что-то наподобие "arm-linux-gnueabihf gcc" я нашел вот этот вот сайт.

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

Для проверки работоспособности компилятора я решил запустить Hello World, но уже на C :

#include <stdio.h>int main(int argc, char ** argv) {  printf("Hello, World!\n");  return 0;}

На этот раз я решил автоматизировать все, чтобы можно было просто запускать скрипты, делающие за меня всю грязную работу, поэтому появился build.sh :

#!/bin/bashgcc_arm/bin/arm-linux-gnueabihf-gcc main.c -o app.o

К счастью, все заработало с первого раза. Теперь я решил приступить к настройке компилятора для D. Как самый успешный, был выбран ldc. Я скачал последний релиз с GitHub (http://personeltest.ru/aways/github.com/ldc-developers/ldc) и также поместил рядом с C-компилятором.

Теперь было необходимо собрать стандартную библиотеку D для arm. Так появился build_ldc_runtime.sh :

#!/bin/bashexport CURRENT_DIR=$(pwd)export LDC_PATH=$CURRENT_DIR/"ldc2/bin"export GCC_PATH=$CURRENT_DIR/"gcc_arm/bin"export CC=$GCC_PATH/"arm-linux-gnueabihf-gcc"$LDC_PATH/ldc-build-runtime --ninja --dFlags="-w;-mtriple=arm-linux-gnueabihf"

Сборка прошла успешно, и я перешел к тестированию HelloWorld. Также откомпилил его и запустил на Raspberry, но получил слегка неожиданную ошибку. Исполняемый файл требовал GLIBC не ниже, чем 2.29. Погуглив, как посмотреть версию GLIBC, я понял, что на малине стоит всего-лишь 2.28...

Так как, по моему мнению, clang и llvm, а следовательно и ldc, не пропагандируют GLIBC и вроде как даже могут обходиться без нее, был сделан вывод о том, что проблема в gcc.

Я решил скачать Toolchain с той же версией gcc, что и на целевом устройстве, то есть 8.3.0. К счастью, на том же сайте был найден подходящий компилятор. Я удалил старый, распаковал новый, пересобрал ldc_runtime и все заработало.

Чудесно. Оставалось присобачить сюда ZLib и OpenSSL. Начал я, конечно, с ZLib, так как с ним меньше проблем :

#!/bin/bashexport CURRENT_DIR=$(pwd)export GCC_PATH=$CURRENT_DIR/"gcc_arm/bin"export CC=$GCC_PATH/"arm-linux-gnueabihf-gcc"mkdir tmp_zlibcd tmp_zlibgit clone https://github.com/madler/zlib.gitcd zlib./configure --prefix=$CURRENT_DIR/zlibmakemake installcd ../..rm -rf tmp_zlib

На выходе получается папка с готовой для линковки библиотекой. Потом я начал собирать OpenSSL. На этот раз сборка прошла успешно. Но вот на этапе линковки, снова вылезли неразрешенные зависимости : SSL_get_peer_certificate и ERR_put_error.

Не имею никакого желания с этим разбираться, я решил отказаться от OpenSSL и использовать Botan (Vibe.d имеет такую возможность). К слову, все собралось и слинковалось с первого раза :

#!/bin/bashexport CURRENT_DIR=$(pwd)export GCC_PATH=$CURRENT_DIR/"gcc_arm/bin"export CCX=$GCC_PATH/"arm-linux-gnueabihf-g++"mkdir tmp_botancd tmp_botangit clone https://github.com/randombit/botan.gitcd botanpython configure.py --cpu=arm --cc-bin=${CCX} --prefix=${CURRENT_DIR}/botanmake && make installcd ../..rm -rf tmp_botan

Приложение с Vibe.d И Botan успешно откомпилилось и ДАЖЕ ЗАПУСТИЛОСЬ на raspberry. Просто замечательно. Но есть одно "но" - OpenSSL. Мы так и не разобрались с ним. Проблема оказалась достаточно глупой и легко решаемой.

Поискав SSL_get_peer_certificate и ERR_put_error в репозитории OpenSSL я понял, что в последних alpha версиях они были объявлены deprecated и удалены. Vibe.d официально поддерживает OpenSSL версии 1.1, а я скачал alpha 3.x, где этих функций просто не было.

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

#!/bin/bashexport CURRENT_DIR=$(pwd)mkdir tmp_opensslcd tmp_opensslgit clone https://github.com/openssl/openssl -b OpenSSL_1_1_1-stablecd openssl./Configure linux-generic32 shared \    --prefix=$CURRENT_DIR/openssl --openssldir=$CURRENT_DIR/tmp_openssl/openssl \    --cross-compile-prefix=$CURRENT_DIR/gcc_arm/bin/arm-linux-gnueabihf-make && make installcd ../..rm -rf tmp_openssl

И вот наконец, все это заработало. Я написал небольшой скрипт, который сразу запускает компилятор с нужными флагами :

#!/bin/bashexport CURRENT_DIR=/home/test_user/Projects/rpi4_d_toolchainexport LDC_PATH=$CURRENT_DIR/ldc2/binexport GCC_PATH=$CURRENT_DIR/gcc_arm/binexport LDC_RUNTIME_PATH=$CURRENT_DIR/ldc-build-runtime.tmp/libexport OPENSSL_PATH=$CURRENT_DIR/openssl/libexport ZLIB_PATH=$CURRENT_DIR/zlib/libexport BOTAN_PATH=$CURRENT_DIR/botan/libexport CC=$GCC_PATH/"arm-linux-gnueabihf-gcc"$LDC_PATH/ldc2 -mtriple=arm-linux-gnueabihf -gcc=$CC -L=-L${LDC_RUNTIME_PATH} -L=-L${OPENSSL_PATH} -L=-L${ZLIB_PATH} -L=-L${BOTAN_PATH} $@

Теперь все проекты легко и просто собираются следующей командой :

dub build --compiler=~/ldc_rpi

Прототипом и основным источником информации послужил вот этот GitHub репозиторий.

Заключение

Если вы когда-нибудь захотите воспользоваться кросс-компиляцией, то :

  1. Лучше использовать уже готовый компилятор. Самостоятельно его собирая, вы потратите много времени

  2. Версии компиляторов основной и целевой платформы должны совпадать, иначе что-то может пойти не так

  3. Скрипты автоматизируют и упростят вашу жизнь. Используйте их

  4. Если есть возможность примонтировать файловую систему целевого устройства, то можете попробовать украсть библиотеки оттуда. Если получиться - ваша жизнь станет легче

А если вы когда-нибудь соберетесь собирать что-то для raspberry pi 4, то можно скачать отсюда тот Toolchain, который в итоге у меня получился (тут все компиляторы и библиотеки).

Только в файле ldc_rpi, поменяйте значение CURRENT_DIR на путь до папки с toolchainом.

Надеюсь, вам понравилась моя статья. Приму к сведению любые замечания и пожелания.

Подробнее..

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru