[D, Go, Rust, Программирование] Портируем утилиту командной строки с Go/Rust на D (перевод)
Автор
Сообщение
news_bot ®
Стаж: 6 лет 9 месяцев
Сообщений: 27286
Несколько дней назад, на реддите в «программировании», Paulo Henrique Cuchi поделился своим опытом разработки утилиты командной строки на Rust и на Go (перевод на Хабре). Утилита, о которой идет речь, — это клиент для его пет-проекта Hashtrack. Hashtrack предоставляет GraphQL API, с помощью которого клиенты могут отслеживать определенные хэштэги твиттера и получать список соответствующих твитов в реальном времени. Будучи спровоцированным комментарием, я решил написать порт на D, чтобы продемонстрировать, как D может быть использован для подобных целей. Я постараюсь сохранить ту же структуру, которую он использовал в своем блогпосте.
Исходники на Гитхабе
оригинал
Видео по клику
Как я пришел к D
Основная причина заключается в том, что в оригинальном блогпосте сравнивались статически типизированные языки, такие как Go и Rust, а также делались уважительные отсылки к Nim и Crystal, но не упоминался D, который так же подпадает в эту категорию. Потому я думаю, что это сделает сравнение интересным.
Мне также нравится D как язык, и я упоминал об этом в различных других блогпостах.
Локальная среда
Руководствосодержит обширную информацию о том, как загрузить и установить эталонный компилятор, DMD. Пользователи Windows могут получить инсталлятор, в то время как пользователи MacOS могут использовать homebrew. На Ubuntu, я просто добавил apt-репозиторий и выполнил обычную установку. С помощью этого вы получите не только DMD, но и dub, менеджер пакетов.
Я установил Rust, чтобы иметь представление о том, как легко будет начать работать. Я был удивлён, насколько это просто. Мне нужно было только запустить интерактивный инсталлятор, который позаботился об остальном. Мне нужно было добавить ~/.cargo/bin в path. Следовало просто перезапустить консоль, чтобы изменения вступили в силу.
Поддержка редакторами
Я написал Hashtrack в Vim без особых затруднений, но это, наверное, потому, что у меня есть некоторое представление о том, что происходит в стандартной библиотеке. У меня всегда была открыта документация, потому что временами я использовал символ, который не импортировал из нужного пакета, или же я вызывал функцию с неверными аргументами. Заметьте, что для стандартной библиотеки вы можете просто написать «import std;» и иметь все в своем распоряжении. Для сторонних библиотек, однако, вы сами по себе.
Мне было любопытно, в каком состоянии находится инструментарий, поэтому я изучил плагины для моей любимой IDE, Intellij IDEA. Я нашел этот и установил его. Я также установил DCD и DScanner, клонируя их соответствующие репозитории и собирая их, а затем настраивая плагин IDEA, чтобы указать правильные пути. Обратитесь к автору этой заметки в блоге за разъяснениями.
Сначала я столкнулся с несколькими проблемами, но они были исправлены после обновления IDE и плагина. Одна из проблем, с которой я столкнулся, заключалась в том, что она не могла распознать мои собственные пакеты и продолжала отмечать их как «возможно, неопределенные». Позже я обнаружил, что для того, чтобы они был распознаны, я должен был поместить «module имя_модуля_пакета;» вверху файла.
Я думаю, что все еще есть ошибка, что не распознается .length, по крайней мере, на моей машине. Я открыл проблему на Github, вы можете проследить за ней здесь, если вам любопытно.
Если вы в Windows, я слышал хорошее о VisualD.
Управление пакетами
Dub является дефакто менеджером пакетов в D. Он загружает и устанавливает зависимости с code.dlang.org. Для этого проекта мне нужен был HTTP клиент, потому что я не хотел использовать cURL. В итоге я получил две зависимости, requests и его зависимость, cachetools, который не имеет собственной зависимости. Однако, по каким-то причинам, он прихватил еще двенадцать зависимостей:
Я думаю, что Dub использует их для внутренних целей, но я не уверен насчет этого.
Rust загрузил много крейтов (Прим.пер: 228), но это, вероятно, потому, что версия на Rust имеет больше возможностей, чем моя. Например, он загрузил rpassword, инструмент, который скрывает символы пароля при вводе их в терминал, подобно функции getpass от Python. Это одна из многих вещей, которых у меня нет в коде. Я добавил поддержку getpass для Linux, благодаря этой рекомендации. Я также добавил форматирование текста в терминале, благодаря экранирующим последовательностям, которые я скопировал из оригинального исходного кода Go.
Библиотеки
Имея слабое представление о graphql, я понятия не имел, с чего начать. Поиск по «graphql» на code.dlang.org привел меня к соответствующей библиотеке, метко названной "graphqld". Однако после ее изучения мне показалось, что она больше похожа на плагин vibe.d, чем на реального клиента, если таковой есть.
После изучения сетевых запросов в Firefox, я понял, что для этого проекта я могу просто имитировать graphql-запросы и преобразования, которые я буду посылать с помощью HTTP-клиента. Ответы — это просто JSON-объекты, которые я могу разобрать с помощью инструментов, предоставляемых пакетом std.json. Помня об этом, я начал искать HTTP-клиенты и остановился на requests, это простой в использовании HTTP-клиент, но, что более важно, достигший определенного уровня зрелости.
Я скопировал исходящие запросы от сетевого анализатора и вставил их в отдельные .graphql файлы, которые затем импортировал и отправил с соответствующими переменными. Большая часть функциональности была помещена в структуру GraphQLRequest, потому что я хотел вставить различные конечные точки и конфигурации в него, необходимые для проекта:
Исходник
SPL
struct GraphQLRequest
{
string operationName;
string query;
JSONValue variables;
Config configuration;
JSONValue toJson()
{
return JSONValue([
"operationName": JSONValue(operationName),
"variables": variables,
"query": JSONValue(query),
]);
}
string toString()
{
return toJson().toPrettyString();
}
Response send()
{
auto request = Request();
request.addHeaders(["Authorization": configuration.get("token", "")]);
return request.post(
configuration.get("endpoint"),
toString(),
"application/json"
);
}
}
Вот фрагмент обмена пакетами. Следующий код обрабатывает аутентификацию :
SPL
struct Session
{
Config configuration;
void login(string username, string password)
{
auto request = createSession(username, password);
auto response = request.send();
response.throwOnFailure();
string token = response.jsonBody
["data"].object
["createSession"].object
["token"].str;
configuration.put("token", token);
}
GraphQLRequest createSession(string username, string password)
{
enum query = import("createSession.graphql").lineSplitter().join("\n");
auto variables = SessionPayload(username, password).toJson();
return GraphQLRequest("createSession", query, variables, configuration);
}
}
struct SessionPayload
{
string email;
string password;
//todo : make this a template mixin or something
JSONValue toJson()
{
return JSONValue([
"email": JSONValue(email),
"password": JSONValue(password)
]);
}
string toString()
{
return toJson().toPrettyString();
}
}
Спойлер — я никогда не делал подобного ранее.
Все происходит так: функция main() создает из аргументов командной строки структуру Config и инжектирует ее в структуру Session, которая реализует функциональность команд входа, выхода из системы и статуса. Метод createSession() конструирует graphQL-запрос, читая реальный запрос из соответствующего .graphql-файла и передавая вместе с ним переменные. Я не хотел загрязнять исходный код graphQL-мутациями и запросами, поэтому переместил их в .graphql файлы, которые затем импортирую во время компиляции с помощью enum и import. Последний требует наличия флага компилятора для указания его на stringImportPaths (который по умолчанию имеет значение view/).
Что касается метода login(), его единственной обязанностью является отправка HTTP-запроса и обработка ответа. В этом случае он обрабатывает потенциальные ошибки, хотя и не очень тщательно. Затем он сохраняет токен в конфигурационном файле, который на самом деле является не более чем славным JSON-объектом.
Метод throwOnFailure не является частью основной функциональности библиотеки запросов. На самом деле это вспомогательная функция, которая делает быструю и грязную обработку ошибок:
void throwOnFailure(Response response)
{
if(!response.isSuccessful || "errors" in response.jsonBody)
{
string[] errors = response.errors;
throw new RequestException(errors.join("\n"));
}
}
Так как D поддерживает UFCS, синтаксис throwOnFailure(response) может быть переписан как response.throwOnFailure(). Это делает его легко встраиваемым в другие вызовы методов, таких как send(). Возможно, я злоупотреблял этой функциональностью на протяжении всего проекта.
Обработка ошибок
D предпочитает исключения, когда дело доходит до обработки ошибок. Обоснование подробно объяснено здесь. Одна из вещей, которая мне нравится, заключается в том, что необработанные ошибки в конце концов всплывут, если их не явно не заткнуть. Вот почему мне удалось уйти от упрощенной обработки ошибок. Например, в этих строках:
string token = response.jsonBody
["data"].object
["createSession"].object
["token"].str;
configuration.put("token", token);
Если тело ответа не содержит токен или любой из объектов, приводящих к нему, будет выброшено исключение, которое всплывет в основной функции, а затем взорвется перед лицом пользователя. Если бы я использовал Go, мне пришлось бы быть очень осторожным с ошибками на каждом этапе. И, честно говоря, так как писать если err!= null каждый раз при вызове функции раздражает, я бы очень соблазнился ошибку просто проигнорировать. Однако мое понимание Go примитивно, и я не удивлюсь, если компилятор облает вас за то, что вы ничего не делаете с возвратом ошибки, так что не стесняйтесь поправлять меня, если я ошибаюсь.
Обработка ошибок в стиле Rust, как объяснено в оригинальном блогпосте, была интересна. Я не думаю, что в стандартной библиотеке D есть что-то подобное, но были дискуссии о реализации подобного как сторонней библиотеки.
Websockets
Я просто хочу кратко отметить, что я не использовал вебсокеты для реализации команды «watch». Я пытался использовать клиент websocket из Vibe.d, но он не смог работать с бэкэндом hashtrack, потому что продолжал закрывать соединение. В конце концов, я отказался от него в пользу циклического опроса, даже несмотря на то, что это осуждается. Клиент работает с тех пор, как я протестировал его с другим веб-сервером, так что я, возможно, вернусь к этому в будущем.
Непрерывная интеграция
Для CI я настроил два сборочных задания: обычную сборку для бранчей и мастер — релиз, чтобы обеспечить загрузки оптимизированных сборок артефактов.
Прим.пер. На картинках видно время на сборку. С учетом загрузки зависимостей. Пересборка без зависимостей ~4с
Потребление памяти
Я использовал команду /usr/bin/time -v ./hashtrack --list для измерения использования памяти, как объяснялось в оригинальной блогпосте. Я не знаю, зависит ли использование памяти от хэштэгов, за которыми следит пользователь, но вот результаты программы на D, собранной с помощью dub build -b release:
Maximum resident set size (kbytes): 10036
Maximum resident set size (kbytes): 10164
Maximum resident set size (kbytes): 9940
Maximum resident set size (kbytes): 10060
Maximum resident set size (kbytes): 10008
Неплохо. Я запустил версии Go и Rust с моим пользователем hashtrack'a и получил эти результаты:
Go, собранный с go build -ldflags "-s -w":
Maximum resident set size (kbytes): 13684
Maximum resident set size (kbytes): 13820
Maximum resident set size (kbytes): 13904
Maximum resident set size (kbytes): 13796
Maximum resident set size (kbytes): 13600
Rust, собранный с cargo build --release:
Maximum resident set size (kbytes): 9224
Maximum resident set size (kbytes): 9192
Maximum resident set size (kbytes): 9384
Maximum resident set size (kbytes): 9132
Maximum resident set size (kbytes): 9168
Upd: пользователь реддита skocznymroczny рекомендовал также протестировать компиляторы LDC и GDC. Вот результаты:
LDC 1.22, собранный dub build -b release --compiler=ldc2 (уже после добавления цветного вывода и getpass)
Maximum resident set size (kbytes): 7816
Maximum resident set size (kbytes): 7912
Maximum resident set size (kbytes): 7804
Maximum resident set size (kbytes): 7832
Maximum resident set size (kbytes): 7804
В D есть сборка мусора, но также поддерживаются умные указатели и, совсем недавно, экспериментальная методология управления памятью, вдохновленная Rust. Я не совсем уверен, насколько хорошо эти функции интегрируются со стандартной библиотекой, поэтому я решил позволить GC обрабатывать память за меня. Я думаю, что результаты довольно неплохие, учитывая, что я не задумывался о потреблении памяти во время написания кода.
Размер бинарников
Rust, собранный cargo build --release: 7.0M
D, собранный dub build -b release: 5.7M
D, собранный dub build -b release --compiler=ldc2: 2.4M
Go, собранный go build: 7.1M
Go, собранный go build -ldflags "-s -w": 5.0M
Прим.пер. Здесь надо перепроверять — не очень понятно, где выполняется стрип отладочной информации, а где нет. Например у меня версия для Windows при сборке dub build -b release получается размером 2М для x64 (и 1.5M для x86-mscoff) и в них нет отладочных символов, а Rust версию на Ubuntu18 собрать не удалось из-за проблем с конфигурацией openssl, потому трудно сказать, как аукнулось огромное число зависимостей
Заключение
Я думаю, что D — надежный язык для написания подобных инструментов командной строки. Я не часто обращался к внешним зависимостям, потому что стандартная библиотека содержала большую часть того, что мне было нужно. Такие вещи, как разбор аргументов командной строки, обработка JSON, юнит-тестирование, отправка HTTP-запросов (с cURL) — все это доступно в стандартной библиотеке. Если стандартной библиотеке не хватает того, что вам нужно, то пакеты сторонних разработчиков существуют, но я думаю, что в этой области еще есть место для улучшений. С другой стороны, если у вас менталитет NIH «изобретено не здесь», или если вы хотите с легкостью оказать влияние как разработчик с открытым исходным кодом, то вам определённо понравится экосистема D.
Причины, по которым я бы использовал D
- Да
===========
Источник:
habr.com
===========
===========
Автор оригинала: Azi Hassan
===========Похожие новости:
- [Тестирование IT-систем] Jubula — от мануального тестировщика до автоматизатора один шаг
- [Assembler, UEFI, Ненормальное программирование] Вы всё ещё меряете FSB сотнями?
- [Разработка под Android, Тестирование мобильных приложений] На чем писать Android UI-тесты
- [Amazon Web Services, Облачные сервисы, Информационная безопасность, Хранение данных] 54 000 сканов водительских удостоверений австралийцев утекли в сеть
- [Программирование, Go] Вариативные функции в Go (перевод)
- [Компьютерное железо, Настольные компьютеры, Процессоры] Даунклокинг Ice Lake AVX-512 (перевод)
- [Amazon Web Services, DevOps, IT-инфраструктура, Системное программирование] Описание инфраструктуры в Terraform на будущее. Антон Бабенко (2018г)
- [Управление продуктом] Как growth hacking помогает взлетать стартапам за несколько месяцев?
- [C, C++, Open source, Программирование] Проверка QEMU с помощью PVS-Studio
- [C, C++, Open source, Программирование] Checking QEMU using PVS-Studio
Теги для поиска: #_d, #_go, #_rust, #_programmirovanie (Программирование), #_dlang, #_sravnenie_jap (сравнение ЯП), #_d, #_go, #_rust, #_programmirovanie (
Программирование
)
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 22-Ноя 14:05
Часовой пояс: UTC + 5
Автор | Сообщение |
---|---|
news_bot ®
Стаж: 6 лет 9 месяцев |
|
Несколько дней назад, на реддите в «программировании», Paulo Henrique Cuchi поделился своим опытом разработки утилиты командной строки на Rust и на Go (перевод на Хабре). Утилита, о которой идет речь, — это клиент для его пет-проекта Hashtrack. Hashtrack предоставляет GraphQL API, с помощью которого клиенты могут отслеживать определенные хэштэги твиттера и получать список соответствующих твитов в реальном времени. Будучи спровоцированным комментарием, я решил написать порт на D, чтобы продемонстрировать, как D может быть использован для подобных целей. Я постараюсь сохранить ту же структуру, которую он использовал в своем блогпосте. Исходники на Гитхабе оригинал Видео по клику Как я пришел к D Основная причина заключается в том, что в оригинальном блогпосте сравнивались статически типизированные языки, такие как Go и Rust, а также делались уважительные отсылки к Nim и Crystal, но не упоминался D, который так же подпадает в эту категорию. Потому я думаю, что это сделает сравнение интересным. Мне также нравится D как язык, и я упоминал об этом в различных других блогпостах. Локальная среда Руководствосодержит обширную информацию о том, как загрузить и установить эталонный компилятор, DMD. Пользователи Windows могут получить инсталлятор, в то время как пользователи MacOS могут использовать homebrew. На Ubuntu, я просто добавил apt-репозиторий и выполнил обычную установку. С помощью этого вы получите не только DMD, но и dub, менеджер пакетов. Я установил Rust, чтобы иметь представление о том, как легко будет начать работать. Я был удивлён, насколько это просто. Мне нужно было только запустить интерактивный инсталлятор, который позаботился об остальном. Мне нужно было добавить ~/.cargo/bin в path. Следовало просто перезапустить консоль, чтобы изменения вступили в силу. Поддержка редакторами Я написал Hashtrack в Vim без особых затруднений, но это, наверное, потому, что у меня есть некоторое представление о том, что происходит в стандартной библиотеке. У меня всегда была открыта документация, потому что временами я использовал символ, который не импортировал из нужного пакета, или же я вызывал функцию с неверными аргументами. Заметьте, что для стандартной библиотеки вы можете просто написать «import std;» и иметь все в своем распоряжении. Для сторонних библиотек, однако, вы сами по себе. Мне было любопытно, в каком состоянии находится инструментарий, поэтому я изучил плагины для моей любимой IDE, Intellij IDEA. Я нашел этот и установил его. Я также установил DCD и DScanner, клонируя их соответствующие репозитории и собирая их, а затем настраивая плагин IDEA, чтобы указать правильные пути. Обратитесь к автору этой заметки в блоге за разъяснениями. Сначала я столкнулся с несколькими проблемами, но они были исправлены после обновления IDE и плагина. Одна из проблем, с которой я столкнулся, заключалась в том, что она не могла распознать мои собственные пакеты и продолжала отмечать их как «возможно, неопределенные». Позже я обнаружил, что для того, чтобы они был распознаны, я должен был поместить «module имя_модуля_пакета;» вверху файла. Я думаю, что все еще есть ошибка, что не распознается .length, по крайней мере, на моей машине. Я открыл проблему на Github, вы можете проследить за ней здесь, если вам любопытно. Если вы в Windows, я слышал хорошее о VisualD. Управление пакетами Dub является дефакто менеджером пакетов в D. Он загружает и устанавливает зависимости с code.dlang.org. Для этого проекта мне нужен был HTTP клиент, потому что я не хотел использовать cURL. В итоге я получил две зависимости, requests и его зависимость, cachetools, который не имеет собственной зависимости. Однако, по каким-то причинам, он прихватил еще двенадцать зависимостей: Я думаю, что Dub использует их для внутренних целей, но я не уверен насчет этого. Rust загрузил много крейтов (Прим.пер: 228), но это, вероятно, потому, что версия на Rust имеет больше возможностей, чем моя. Например, он загрузил rpassword, инструмент, который скрывает символы пароля при вводе их в терминал, подобно функции getpass от Python. Это одна из многих вещей, которых у меня нет в коде. Я добавил поддержку getpass для Linux, благодаря этой рекомендации. Я также добавил форматирование текста в терминале, благодаря экранирующим последовательностям, которые я скопировал из оригинального исходного кода Go. Библиотеки Имея слабое представление о graphql, я понятия не имел, с чего начать. Поиск по «graphql» на code.dlang.org привел меня к соответствующей библиотеке, метко названной "graphqld". Однако после ее изучения мне показалось, что она больше похожа на плагин vibe.d, чем на реального клиента, если таковой есть. После изучения сетевых запросов в Firefox, я понял, что для этого проекта я могу просто имитировать graphql-запросы и преобразования, которые я буду посылать с помощью HTTP-клиента. Ответы — это просто JSON-объекты, которые я могу разобрать с помощью инструментов, предоставляемых пакетом std.json. Помня об этом, я начал искать HTTP-клиенты и остановился на requests, это простой в использовании HTTP-клиент, но, что более важно, достигший определенного уровня зрелости. Я скопировал исходящие запросы от сетевого анализатора и вставил их в отдельные .graphql файлы, которые затем импортировал и отправил с соответствующими переменными. Большая часть функциональности была помещена в структуру GraphQLRequest, потому что я хотел вставить различные конечные точки и конфигурации в него, необходимые для проекта: ИсходникSPLstruct GraphQLRequest
{ string operationName; string query; JSONValue variables; Config configuration; JSONValue toJson() { return JSONValue([ "operationName": JSONValue(operationName), "variables": variables, "query": JSONValue(query), ]); } string toString() { return toJson().toPrettyString(); } Response send() { auto request = Request(); request.addHeaders(["Authorization": configuration.get("token", "")]); return request.post( configuration.get("endpoint"), toString(), "application/json" ); } } Вот фрагмент обмена пакетами. Следующий код обрабатывает аутентификацию :SPLstruct Session
{ Config configuration; void login(string username, string password) { auto request = createSession(username, password); auto response = request.send(); response.throwOnFailure(); string token = response.jsonBody ["data"].object ["createSession"].object ["token"].str; configuration.put("token", token); } GraphQLRequest createSession(string username, string password) { enum query = import("createSession.graphql").lineSplitter().join("\n"); auto variables = SessionPayload(username, password).toJson(); return GraphQLRequest("createSession", query, variables, configuration); } } struct SessionPayload { string email; string password; //todo : make this a template mixin or something JSONValue toJson() { return JSONValue([ "email": JSONValue(email), "password": JSONValue(password) ]); } string toString() { return toJson().toPrettyString(); } } Спойлер — я никогда не делал подобного ранее. Все происходит так: функция main() создает из аргументов командной строки структуру Config и инжектирует ее в структуру Session, которая реализует функциональность команд входа, выхода из системы и статуса. Метод createSession() конструирует graphQL-запрос, читая реальный запрос из соответствующего .graphql-файла и передавая вместе с ним переменные. Я не хотел загрязнять исходный код graphQL-мутациями и запросами, поэтому переместил их в .graphql файлы, которые затем импортирую во время компиляции с помощью enum и import. Последний требует наличия флага компилятора для указания его на stringImportPaths (который по умолчанию имеет значение view/). Что касается метода login(), его единственной обязанностью является отправка HTTP-запроса и обработка ответа. В этом случае он обрабатывает потенциальные ошибки, хотя и не очень тщательно. Затем он сохраняет токен в конфигурационном файле, который на самом деле является не более чем славным JSON-объектом. Метод throwOnFailure не является частью основной функциональности библиотеки запросов. На самом деле это вспомогательная функция, которая делает быструю и грязную обработку ошибок: void throwOnFailure(Response response)
{ if(!response.isSuccessful || "errors" in response.jsonBody) { string[] errors = response.errors; throw new RequestException(errors.join("\n")); } } Так как D поддерживает UFCS, синтаксис throwOnFailure(response) может быть переписан как response.throwOnFailure(). Это делает его легко встраиваемым в другие вызовы методов, таких как send(). Возможно, я злоупотреблял этой функциональностью на протяжении всего проекта. Обработка ошибок D предпочитает исключения, когда дело доходит до обработки ошибок. Обоснование подробно объяснено здесь. Одна из вещей, которая мне нравится, заключается в том, что необработанные ошибки в конце концов всплывут, если их не явно не заткнуть. Вот почему мне удалось уйти от упрощенной обработки ошибок. Например, в этих строках: string token = response.jsonBody
["data"].object ["createSession"].object ["token"].str; configuration.put("token", token); Если тело ответа не содержит токен или любой из объектов, приводящих к нему, будет выброшено исключение, которое всплывет в основной функции, а затем взорвется перед лицом пользователя. Если бы я использовал Go, мне пришлось бы быть очень осторожным с ошибками на каждом этапе. И, честно говоря, так как писать если err!= null каждый раз при вызове функции раздражает, я бы очень соблазнился ошибку просто проигнорировать. Однако мое понимание Go примитивно, и я не удивлюсь, если компилятор облает вас за то, что вы ничего не делаете с возвратом ошибки, так что не стесняйтесь поправлять меня, если я ошибаюсь. Обработка ошибок в стиле Rust, как объяснено в оригинальном блогпосте, была интересна. Я не думаю, что в стандартной библиотеке D есть что-то подобное, но были дискуссии о реализации подобного как сторонней библиотеки. Websockets Я просто хочу кратко отметить, что я не использовал вебсокеты для реализации команды «watch». Я пытался использовать клиент websocket из Vibe.d, но он не смог работать с бэкэндом hashtrack, потому что продолжал закрывать соединение. В конце концов, я отказался от него в пользу циклического опроса, даже несмотря на то, что это осуждается. Клиент работает с тех пор, как я протестировал его с другим веб-сервером, так что я, возможно, вернусь к этому в будущем. Непрерывная интеграция Для CI я настроил два сборочных задания: обычную сборку для бранчей и мастер — релиз, чтобы обеспечить загрузки оптимизированных сборок артефактов. Прим.пер. На картинках видно время на сборку. С учетом загрузки зависимостей. Пересборка без зависимостей ~4с Потребление памяти Я использовал команду /usr/bin/time -v ./hashtrack --list для измерения использования памяти, как объяснялось в оригинальной блогпосте. Я не знаю, зависит ли использование памяти от хэштэгов, за которыми следит пользователь, но вот результаты программы на D, собранной с помощью dub build -b release: Maximum resident set size (kbytes): 10036
Maximum resident set size (kbytes): 10164 Maximum resident set size (kbytes): 9940 Maximum resident set size (kbytes): 10060 Maximum resident set size (kbytes): 10008 Неплохо. Я запустил версии Go и Rust с моим пользователем hashtrack'a и получил эти результаты: Go, собранный с go build -ldflags "-s -w": Maximum resident set size (kbytes): 13684
Maximum resident set size (kbytes): 13820 Maximum resident set size (kbytes): 13904 Maximum resident set size (kbytes): 13796 Maximum resident set size (kbytes): 13600 Maximum resident set size (kbytes): 9224
Maximum resident set size (kbytes): 9192 Maximum resident set size (kbytes): 9384 Maximum resident set size (kbytes): 9132 Maximum resident set size (kbytes): 9168 LDC 1.22, собранный dub build -b release --compiler=ldc2 (уже после добавления цветного вывода и getpass) Maximum resident set size (kbytes): 7816
Maximum resident set size (kbytes): 7912 Maximum resident set size (kbytes): 7804 Maximum resident set size (kbytes): 7832 Maximum resident set size (kbytes): 7804 В D есть сборка мусора, но также поддерживаются умные указатели и, совсем недавно, экспериментальная методология управления памятью, вдохновленная Rust. Я не совсем уверен, насколько хорошо эти функции интегрируются со стандартной библиотекой, поэтому я решил позволить GC обрабатывать память за меня. Я думаю, что результаты довольно неплохие, учитывая, что я не задумывался о потреблении памяти во время написания кода. Размер бинарников Rust, собранный cargo build --release: 7.0M
D, собранный dub build -b release: 5.7M D, собранный dub build -b release --compiler=ldc2: 2.4M Go, собранный go build: 7.1M Go, собранный go build -ldflags "-s -w": 5.0M Прим.пер. Здесь надо перепроверять — не очень понятно, где выполняется стрип отладочной информации, а где нет. Например у меня версия для Windows при сборке dub build -b release получается размером 2М для x64 (и 1.5M для x86-mscoff) и в них нет отладочных символов, а Rust версию на Ubuntu18 собрать не удалось из-за проблем с конфигурацией openssl, потому трудно сказать, как аукнулось огромное число зависимостей Заключение Я думаю, что D — надежный язык для написания подобных инструментов командной строки. Я не часто обращался к внешним зависимостям, потому что стандартная библиотека содержала большую часть того, что мне было нужно. Такие вещи, как разбор аргументов командной строки, обработка JSON, юнит-тестирование, отправка HTTP-запросов (с cURL) — все это доступно в стандартной библиотеке. Если стандартной библиотеке не хватает того, что вам нужно, то пакеты сторонних разработчиков существуют, но я думаю, что в этой области еще есть место для улучшений. С другой стороны, если у вас менталитет NIH «изобретено не здесь», или если вы хотите с легкостью оказать влияние как разработчик с открытым исходным кодом, то вам определённо понравится экосистема D. Причины, по которым я бы использовал D
=========== Источник: habr.com =========== =========== Автор оригинала: Azi Hassan ===========Похожие новости:
Программирование ) |
|
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 22-Ноя 14:05
Часовой пояс: UTC + 5