Efficient Computer: программируем по кафелю

Экспериментируем с компилятором для новой не Фон-Неймановской архитектуры, обещающей повышение энергоэффективности в 100 раз.
Из исходного кода в машинный
Экспериментируем с компилятором для новой не Фон-Неймановской архитектуры, обещающей повышение энергоэффективности в 100 раз.
Мы не просто пишем код. Мы строим компиляторы, которые строят код. AsmX G3 — это не обновление, это переосмысление с первых принципов. Приготовьтесь к глубокому техническому погружению в архитектуру нашего нового компилятора ZGEN, где мы вскроем каждый компонент, от ядра до сборщика ELF, и покажем инженерные решения, которые определяют будущее системного программирования.
Довольно долго я тягался с по-настоящему глупой проблемой на C++: мне не нравятся функции-члены, но я вынужден их писать, чтобы программисту было хоть немного удобнее работать. Функции-члены обеспечивают две вещи: разграничение областей видимости и обнаружимость. Разграничение областей видимости — менее актуальная из этих задач, поскольку в моём коде на C++ я и так не использую модификаторы private/public. Обнаружимость — большая проблема: я могу написать x.F, а IDE предложит x.Func(). Отлично! «Но правильные программисты пользуются только vim и скромными IDE». Что ж, привет вам, воображаемые мифические обычные программеры. Здесь вам ничего не угрожает, но, пожалуйста, уходя — надевайте сразу два беджика: «vim отстой» и «Я ненавижу emacs». Отлично помогает завязать разговор с «настоящими» программистами.
Да. Спустя наверно 3 дня я решил сделать это. Долго конечно, но что тут поделаешь.
Также если нужно, можете посмотреть на первую статью об этом лиспе.
QapDSLv2 — это язык который транслируется в обычный C++ код. Он позволяет удобно и компактно задавать грамматики/правила разбора кода программ, значительно упрощая разработку компиляторов/анализаторов/трансляторов.
QapGen — это генератор дерева_лексеров/парсеров описанных на QapDSLv2. Сама грамматика QapDSLv2 описана на QapDSLv2 на 100%. Поэтому QapGen как основной читатель этой грамматики сам генерирует часть своего кода(весь парсер QapDSLv2).
Основные фишки QapDSLv2 + QapGen — это:
1) Отсутствие этапа токенизации — дерево лексеров разбивает входной поток на лексемы и сохраняет их в строго типизированных древовидных С++ структурах пропуская этап токенизации.
2) Генерация оптимизированного кода полиморфных лексеров.
3) Полное сохранение всех лексем(даже разделители сохраняются, такие как пробелы/переходы на новую строку и комментарии) в результирующем дереве.
4) Возможность сохранить как оригинальное дерево, так и модифицированное обратно в код/текст без потери разделителей/комментариев.
5) Автоматическая генерация кода посетителей(это такой паттерн проектирования).
А теперь пример самой сочной части(рекурсивно самоописывающийся код):
struct
t_target_struct:i_target_item{
struct
t_keyword{
string
kw=any_str_from_vec(split("struct,class",","));
" "? // optional separator
};
struct
t_body_semicolon:i_struct_impl{";"};
struct
t_body_impl:i_struct_impl{
"{" // жрём скобочку
vector<TAutoPtr<i_target_item>>
nested?; //
рекурсия!
" "?
vector<TAutoPtr<i_struct_field>>
arr?; // парсим поля
" "?
TAutoPtr<t_cpp_code>
c?; // остальной С++ код
" "?
"}"
};
struct
t_parent{
string
a_or_c=any_str_from_vec(split("=>,:",","));
" "?
t_name
name;
};
//точка входа в парсер:
TAutoPtr<t_keyword>
kw?; //парсим
struct/class
t_name
name; //парсим имя
" "?
TAutoPtr<t_parent>
parent?;
" "?
TAutoPtr<i_struct_impl>
body;
};
Всем привет! Сегодня мы с вами сделаем реализацию LISP'а. Конечно же не полного.
Возможно, когда то я доведу этот лисп до ума и напишу новую статью... Но не обещаю.
Контракты в Kotlin — это «тёмная лошадка» языка — они загадочные и чуть-чуть магические. И под капотом у них спрятано гораздо больше, чем можно найти в официальной документации.
Привет! Меня зовут Виталий. Я работаю Android‑разработчиком в Альфа‑Банке. В этой статье я делюсь пасхалками и неожиданными фичами Kotlin компилятора, связанными с Kotlin Contracts: как парсится список эффектов, как работает новый Contracts API изнутри, и почему, чёрт возьми, на уровне компилятора можно использовать контракты не только на уровне функций.
Всё просто, лампово и с примерами из исходников. Даже если вы никогда не ковырялись в кишках компилятора, гарантирую: после прочтения контракты станут чуть ближе, а компилятор — чуть менее пугающим.
Привет! На связи Антон Полухин из Техплатформы Городских сервисов Яндекса, и сейчас я расскажу о софийской встрече Международного комитета по стандартизации языка программирования C++, в которой принимал активное участие. Это была последняя встреча, на которой новые фичи языка, с предодобренным на прошлых встречах дизайном, ещё могли попасть в C++26.
И результат превзошёл все ожидания:
CLL в ISPA — переносимый язык семантических действий для генераторов парсеров. Объявление переменных, условий и циклов, генерация AST и кода на C++ без привязки к языку парсера. Пример, разбор и сравнение с ANTLR, Bison.
Разработка парсер генератора ISPA: что реализовано и какие планы на будущее.Гибкий парсер нового поколения с теми функциями, которых давно не хватает существующим решениям.
Команда Rust рада сообщить о новой версии языка — 1.88.0. Rust — это язык программирования, позволяющий каждому создавать надёжное и эффективное программное обеспечение.
Если у вас есть предыдущая версия Rust, установленная через rustup
, то для обновления до версии 1.88.0 вам достаточно выполнить команду:
$ rustup update stable
Если у вас ещё не установлен rustup
, вы можете установить его с соответствующей страницы нашего веб-сайта, а также посмотреть подробные примечания к выпуску на GitHub.
Если вы хотите помочь нам протестировать будущие выпуски, вы можете использовать канал beta (rustup default beta
) или nightly (rustup default nightly
). Пожалуйста, сообщайте обо всех встреченных вами ошибках.
В наше время разработчики уже не так беспокоятся о размере приложений. Некоторые простейшие приложения требуют под 200-300 МБ, а игра вообще может весить более 100 ГБ. Я уже не говорю про "Hello World", который иногда занимет под 180-260 КБ!
К счастью, есть возможность сократить размер приложения. О мусоре в exe'шнике и о способах его удаления написано в этой статье.
Рассмотрим следующий сценарий:
template<typename T>
struct Base
{
// Есть конструктор по умолчанию
Base() = default;
// Некопируемый
Base(Base const &) = delete;
};
template<typename T>
struct Derived : Base<T>
{
Derived() = default;
Derived(Derived const& d) : Base<T>(d) {}
};
// Это assertion выполняется?
static_assert(
std::is_copy_constructible_v<Derived<int>>);
Почему выполняется это assertion? Очевидно, что скопировать Derived<int>
нельзя, ведь при этом мы попытаемся скопировать некопируемый Base<int>
. И в самом деле, если попробовать скопировать его, то мы получим ошибку.
Историю можно начать с 1994 года, в котором Мартин Уорд (Martin Ward) на основании исследования больших проектов предложил парадигму языково-ориентированного программирования, когда процесс разработки программного обеспечения разбивается на стадии создания предметно-ориентированных языков и описания решения задачи с их использованием. Цель языково-ориентированного программирования — разделить сложности разработки: машиноориентированная часть кода (низкоуровневая функциональность) и человеко-ориентированная (решение прикладной задачи) разрабатываются независимо друг от друга.
Далее в 2003 году Эрик Эванс (Eric Evans) ввел понятие предметно-ориентированного проектирования (Domain-Driven Design, DDD) для набора программных и организационных практик, позволяющих разрабатывать сложные масштабируемые системы. Этот подход до сих пор активно используется, например, в микросервисной архитектуре и в информационной безопасности (см. Secure by Design). В этом подходе вводятся понятия: «модель», «проектирование по модели» (Model-Driven Design), «изоляция предметной области» и «изолированный контекст» (Bounded Context). Особенно интересно, что Эванс упоминает о предметно-ориентированных языках, как идеальном средстве описания модели конкретной предметной области, которая должна быть изолирована в своём контексте.
Но как обстоят дела с имитационным математическим моделированием? До сих пор мы применяем инструменты, разработанные по большей части на базе концепций из середины прошлого века. В то время, как за последнюю пару десятилетий теория и технологии развивались достаточно динамично. Возможно, в этом нет ничего страшного (об этом позже). Но вот что интересно: можно ли объединить понятия разработки математических моделей и программного обеспечения на базе представленных выше концепций?
Вот мы и добрались до моделирования в широком смысле. Далее речь пойдёт о подходе, который может быть применён, как для имитационного математического моделирования, так и для разработки сложного программного обеспечения. Начнём с имитационного моделирования, поскольку в этой области есть наиболее интересные результаты, но перспективы нового подхода в разработке сложных программных систем постараюсь тоже затронуть.
Наверно, чаще всего на Rust жалуются из-за его медленного цикла обратной связи и долгого времени компиляции. Я слышу и читаю об этом постоянно; в подкастах по Rust, в постах блогов, опросах, докладах с конференций и офлайн-обсуждениях. Я и сам, как пользователь Rust, регулярно жалуюсь на это!
Кроме того, наряду с обычными жалобами на время компиляции, я начал замечать от раздражённых разработчиков на Rust и подобные заявления: «Почему Rust Project не занимается активнее этой важной и очевидной проблемой? Почему с этим что-нибудь не сделают?». Я участник рабочей группы по производительности компилятора Rust, поэтому воспринимаю такие вопросы очень серьёзно. И, разумеется, у меня есть мнение по этому поводу. В этом посте я приведу свои размышления, способные служить ответами на эти (и похожие) вопросы.
Предупреждение: все выраженные в этом посте мнения исключительно мои и необязательно отражают точку зрения Rust Project (группы контрибьюторов и мейнтейнеров тулчейна Rust).
Усаживайтесь поудобнее, ребята! Сегодня мы с вами разберём следующий увлекательный вопрос: что будет, если заинлайнить вообще всё?
Если вы пока не знакомы с техникой встраивания (inlining) то примите к сведению, что в сообществе специалистов по разработке компиляторов многие, в том числе очень авторитетные фигуры (например, Чендлер Каррут) считают этот приём наиважнейшим при оптимизации компиляторов. Подробнее о том, как устроено встраивание, рассказано здесь — мы беззастенчиво хвалимся той презентацией, с которой выступили перед участниками конференции LLVM Developers' Meeting по межпроцедурной оптимизации. Я рассказывал о встраивании и очень рекомендую вам посмотреть хотя бы первые 6 минут. В этом видео я рассказываю, почему встраивание — очень простое преобразование, а вот тут вашему вниманию предлагается реализация встраивания, предложенная великим Крисом Латтнером уже около 20 лет назад — в ней всего около 200 строк кода. К сожалению, сегодня даже само преобразование пропорционально выросло: в качестве примера взгляните хотя бы на InlineFunction.cpp.
В вышеупомянутом видео я рассказываю, что у встраивания есть свои недостатки. Иными словами, встраивание позиционируется как супер-пупер инструмент в арсенале компиляторщика, но пользоваться этой штукой следует с осторожностью. И следует ли вообще?
QapDSL — это специализированный язык (DSL), который позволяет описывать абстрактные синтаксические деревья (AST) и правила их разбора для языков программирования, прежде всего C++. Такая формализация помогает автоматизировать построение парсеров, генерацию кода, анализ исходников и даже рефакторинг.
Рассмотрим, как описывается объявление класса C++ на QapDSL:
t_class{ string keyword; t_sep sep0; string name; t_sep sep1; TAutoPtr<t_parents> parents; t_sep sep2; TAutoPtr<t_class_body> body; t_sep sep3; { M+=go_any_str_from_vec(keyword,split("struct,class,union",",")); O+=go_auto(sep0); M+=go_str<t_name>(name); O+=go_auto(sep1); O+=go_auto(parents); O+=go_auto(sep2); O+=go_auto(body); O+=go_auto(sep3); M+=go_const(";"); } }
Мы рассмотрим, чем опасны шаблоны для проекта на C++ и как минимизировать эти риски. В оптимизации нам помогут инлайн-файлы, явные инстанциации и даже модули из C++20.
У меня возникла идея, как можно расширить синтаксис C++ операцией скалярного произведения. Если кратко, то произведение двух матриц в новых обозначениях будет выглядеть так:
C[>i][>j] = A[i][>k] * B[>k][j];
Насколько мне известно, сочетания операторов [> и [< вроде бы нигде не используются. Их можно применить для декларации индексов, которые существуют только в пределах данного выражения. Сочетание [> используется для декларации индекса, который пробегает от начала до конца массива в прямом направлении, а сочетание [< для декларации индекса, который пробегает в обратном направлении. Для повторяющихся индексов в произведении подразумевается суммирование - они аналогичны немым индексам в тензорных обозначениях.
Разберём на примерах, как это будет работать.
В своей предыдущей статье [перевод на Хабре] я говорил о множестве недостатков C++, которые, по сути, устранил Rust. Благодаря этому код теперь легко использовать правильно и сложно использовать неверно. Я не говорил о безопасности по памяти, просто привёл пример того, что пользователь функции не может случайно поменять местами аргументы количества и цены.
На написание статьи меня вдохновил доклад Мэтта Годболта о том, как можно сделать интерфейсы C++ более надёжными: Correct by Construction: APIs That Are Easy to Use and Hard to Misuse. Вам стоит его посмотреть!
В той статье я сказал, что Rust гораздо лучше помогает разработчику, возможно, благодаря тому, что у него были десятки лет, чтобы учиться. В конце концов, первая версия C++ была выпущена в начале 80-х, а Rust — в начале 2010-х. Если дать C++ несколько десятков лет для обучения, то, разумеется, появятся новые структуры, которые будут обладать высоким качеством и которые сложно использовать неправильно.
Но так ли это?