Как писать на ассемблере в 2021 году
Несмотря на наличие множества языков различной степени высокоуровневости, сегодня ассемблер не потерял своей актуальности и в индексе TIOBE находится на почётном 10-ом месте (на февраль 2021), обогнав такие модные языки как Go и Rust. Одна из причин его привлекательности – в простоте и максимальной близости к железу; с другой стороны, программирование на ассемблере всё ещё может рассматриваться как искусство и даёт совершенно особые эмоции.
Однако писать программы целиком и полностью на ассемблере — не просто долго, муторно и сложно — но ещё и несколько глупо — ведь высокоуровневые абстракции для того и были придуманы, чтобы сократить время разработки и упростить процесс программирования. Поэтому чаще всего на ассемблере пишут отдельно взятые хорошо оптимизированные функции, которые затем вызываются из языков более высокого уровня, таких как с++ и c#.
Исходя из этого, наиболее удобной средой для программирования будет Visual Studio, в состав которой уже входит MASM. Подключить его к проекту на с/c++ можно через контекстное меню проекта Build Dependencies – Build Customizations…, поставив галочку напротив masm, а сами программы на ассемблере будут располагаться в файлах с расширением .asm (в свойствах которого Item Type должно иметь значение Microsoft Macro Assembler). Это позволит не просто компилировать и вызывать программы на ассемблере без лишних телодвижений – но и осуществлять сквозную отладку, «проваливаясь» в ассемблерный исходник непосредственно из c++ или c# (в том числе и по точке останова внутри ассемблерного листинга), а также отслеживать состояния регистров наряду с обычными переменными в окне Watch.
Подсветка синтаксиса
B Visual Studio нет встроенной подсветки синтаксиса для ассемблера и прочих достижений современного IDE-строения; но её можно обеспечить с помощью сторонних расширений.
AsmHighlighter — исторически первый с минимальным функционалом и неполным набором команд — отсутсвуют не только AVX, но и некоторые из стандартных, в частности fsqrt. Именно этот факт побудил к написанию собственного расширения —
ASM Advanced Editor. В нём, помимо подсветки и сворачивания участков кода (с использованием комментариев «;[«, «;[+» и «;]») реализована привязка подсказок к регистрам, всплывающих по наведению курсора ниже по коду (также через комментарии). Выглядит это так:
;rdx=ссылка на текущий элемент входного массива
mov rcx, 8;=счётчик цикла
Подсказки на команды также присутствуют, но скорее в экспериментальном виде – оказалось, что для их полноценного наполнения времени потребуется больше, чем на написание самого расширения.
Также внезапно выяснилось, что привычные кнопки рас/комментирования выделенного участка кода перестали работать. Поэтому пришлось писать ещё одно расширение, в котором эта функциональность была повешена на одну и ту же кнопку, а необходимость того или иного действия выбирается автоматически.
Asm Dude — обнаружился чуть позже. В нём автор пошёл другим путём и сфокусировал силы на встроенном справочнике команд и автодополнении, в том числе и с отслеживанием меток. Сворачивание кода там также присутствует (по «#region / #end region»), но привязки комментариев к регистрам вроде ещё нет.
32 vs. 64
С тех пор, как появилась 64-битная платформа, стало нормой писать по 2 варианта приложений. Пора завязывать с этим! Сколько можно тянуть легаси. Это же касается и расширений — найти процессор без SSE2 можно разве что в музее – к тому же, без SSE2 64-битные приложения и не заработают. Никакого удовольствия от программирования не будет, если писать по 4 варианта оптимизированных функций для каждой платформы. Только 64 бит/AVX, только хардкор! Хотя может быть и прямо противоположный взгляд — новые процессоры и так работают быстро, и оптимизацию стоит делать под старые. В общем, всё зависит от конкретной задачи.
Преимущество 64-битной платформе вовсе не в «широких» регистрах – а в том, что этих самых регистров стало в 2 раза больше – по 16 штук как общего назначения, так и XMM/YMM. Это не только упрощает программирование, но и позволяет значительно сократить обращения к памяти.
FPU
Если ранее без FPU было никуда, т.к. функции с вещественными числами оставляли результат на вершине стека, то на 64-битной платформе обмен проходит уже без его участия с использованием регистров xmm расширения SSE2. Intel в своих руководствах также активно рекомендует отказаться от FPU в пользу SSE2. Однако есть нюанс: FPU позволяет производить вычисления с 80-битной точностью — что в некоторых случаях может оказаться критически важным. Поэтому поддержка FPU никуда не делаcь, и рассматривать её как устаревшую технологию совершенно точно не стоит. Например, вычисление гипотенузы можно делать «в лоб» без опасения переполнения,
а именно
fld x fmul st(0), st(0) fld y fmul st(0), st(0) faddp st(1), st(0) fsqrt fstp hypot
Основная сложность при программировании FPU — это его стековая организация. Для упрощения была написана небольшая утилитка, автоматически генерирующей комментарии с текущим состоянием стека (планировалось добавить подобную функциональность непосредственно в основное расширение для подсветки синтаксиса — но до этого руки так и не дошли)
Пример оптимизации: преобразование Хартли
Современные компиляторы с++ достаточно умны, чтобы автоматически векторизировать код на простых задачах типа суммирования чисел в массиве или поворота векторов, распознавая соответствующие паттерны в коде. Поэтому получить значительный прирост производительности на примитивных задачах не то что не получится — а наоборот, может оказаться, что ваша супер-оптимизированная программа работает медленнее того, что сгенерировал компилятор. Но и далеко идущие выводы из этого делать тоже не стоит — как только алгоритмы становятся чуточку сложнее и неочевиднее для оптимизации — всё волшебство оптимизирующих компиляторов пропадает. Получить десятикратный прирост производительности путём ручной оптимизации в 2021 году по-прежнему ещё возможно.
Итак, в качестве задачи возьмём алгоритм (медленного) преобразования Хартли:
static void ht_csharp(double[] data, double[] result) < int n = data.Length; double phi = 2.0 * Math.PI / n; for (int i = 0; i < n; ++i) < double sum = 0.0; for (int j = 0; j < n; ++j) < double w = phi * i * j; sum += data[j] * (Math.Cos(w) + Math.Sin(w)); >result[i] = sum / Math.Sqrt(n); > >
Он также достаточно тривиальный для автоматической векторизации (убедимся позже), но даёт чуть больше пространства для оптимизации. Ну а наш оптимизированный вариант будет выглядеть так:
код (комментарии удалены)
ht_asm PROC local sqrtn:REAL10 local _2pin:REAL10 local k:DWORD local n:DWORD and r8, 0ffffffffh mov n, r8d mov r11, rcx xor rcx, rcx mov r9, r8 dec r9 shr r9, 1 mov r10, r8 sub r10, 2 shl r10, 3 finit fld _2pi fild n fdivp st(1), st fstp _2pin fld1 fild n fsqrt fdivp st(1), st ; mov rax, r11 mov rcx, r8 fldz @loop0: fadd QWORD PTR [rax] add rax, 8 loop @loop0 fmul st, st(1) fstp QWORD PTR [rdx] fstp sqrtn add rdx, 8 mov k, 1 @loop1: mov rax, r11 fld QWORD PTR [rax] fld st(0) add rax, 8 fld _2pin fild k fmulp st(1),st fsincos fld1;=u fldz;=v mov rcx, r8 dec rcx @loop2: fld st(1) fmul st(0),st(4) fld st(1) fmul st,st(4) faddp st(1),st fxch st(1) fmul st, st(4) fxch st(2) fmul st,st(3) fsubrp st(2),st fld st(0) fadd st, st(2) fmul QWORD PTR [rax] faddp st(5), st fld st(0) fsubr st, st(2) fmul QWORD PTR [rax] faddp st(6), st add rax, 8 loop @loop2 fcompp fcompp fld sqrtn fmul st(1), st fxch st(1) fstp QWORD PTR [rdx] fmulp st(1), st fstp QWORD PTR [rdx+r10] add rdx,8 sub r10, 16 inc k dec r9 jnz @loop1 test r10, r10 jnz @exit mov rax, r11 fldz mov rcx, r8 shr rcx, 1 @loop3:;[ fadd QWORD PTR [rax] fsub QWORD PTR [rax+8] add rax, 16 loop @loop3;] fld sqrtn fmulp st(1), st fstp QWORD PTR [rdx] @exit: ret ht_asm ENDP
Обратите внимание: тут нет ни разворачивания цикла, ни SSE/AVX, ни косинусных таблиц, ни снижения сложности за счёт «быстрого» алгоритма преобразования. Единственная явная оптимизация — это итеративное вычисление синуса/косинуса во внутреннем цикле алгоритма непосредственно в регистрах FPU.
Поскольку речь идёт об интегральном преобразовании, то помимо скорости, нас ещё интересует точность вычисления и уровень накопленных погрешностей. В данном случае посчитать её очень просто — делая два преобразования подряд, мы должны получить (в теории) исходные данные. На практике они будут слегка отличаться, и можно будет посчитать ошибку через среднеквадратическое отклонения полученного результата от аналитического.
Результаты авто-оптимизации программы на c++ также могут сильно зависеть от настроек параметров компилятора и выбора допустимого расширенного набора инструкций (SSE/AVX/etc). При этом есть два нюанса:
- Современные компиляторы склонны всё возможное вычислять на этапе компиляции – поэтому вполне возможно в откомпилированном коде вместо алгоритма увидеть заранее посчитанное значение, что при замере производительности даст преимущество компилятору в 100500 раз. Во избежание этого в моих замерах используется внешняя функция zero(), добавляющая неопределённости входным параметрам.
- Если компилятору указать «не использовать AVX» — это ещё не значит, что в полученном коде будет отсутствовать AVX. Внешние библиотеки сами проверяют доступный набор команд на текущей платформе и выбирают соответствующую реализацию. Поэтому единственно надёжный способ сравнения производительности в таком случае – испытывать код на платформе, где AVX отсутствует в принципе.
Итак, компилятор Visual Studio 2019, целевая платформа AVX2, Floating Point Model=Precise. Чтобы было ещё интереснее — будет измерять из проекта на c# на массиве из 10000 элементов:
C# ожидаемо оказался медленнее с++, а функция на ассемблере оказалась быстрее в 9 раз! Однако ещё рано радоваться — установим Floating Point Model=Fast:
Как видно, это помогло значительно ускорить код и отставание от ручной оптимизации составило всего лишь в 1.8 раз. Но вот что не изменилось – так это погрешность. Что тот, что другой вариант дал ошибку в 4 значащих цифры – а это немаловажно при математических вычислениях.
В данном случае наш вариант оказался и быстрее, и точнее. Но так бывает не всегда – и выбирая FPU для хранения результатов мы неизбежно будем терять в возможности оптимизации векторизацией. Также никто не запрещает комбинировать FPU и SSE2 в тех случаях, когда это имеет смысл (в частности, такой подход я использовал в реализации double-double арифметики, получив 10-кратное ускорение при умножении).
Дальнейшая оптимизация преобразования Хартли лежит уже в другой плоскости и (для произвольного размера) требует алгоритма Блюстейна, который также критичен к точности промежуточных вычислений. Ну а этот проект можно скачать на GitHub, и в качестве бонуса там также можно найти реализацию функций для суммирования/масштабирования массивов на FPU/SSE2/AVX.
Что почитать
Литературы по ассемблеру навалом. Но можно выделить несколько ключевых источников:
1. Официальная документация от Intel. Ничего лишнего, вероятность опечаток минимальна (кои в печатной литературе встречаются повсеместно).
2. Официальная документация от Microsoft.
3. Онлайн справочник, спарсенный из официальной документации.
4. Сайт Агнера Фога, признанного эксперта по оптимизации. Также содержит образцы оптимизированного кода на C++ с использованием интринсиков.
5. SIMPLY FPU.
6. 40 Basic Practices in Assembly Language Programming.
7. Все, что нужно знать, чтобы начать программировать для 64-разрядных версий Windows.
Appendix: Почему бы просто не использовать интринсики (Intrinsics)?
Скрытый текст
С тех пор, как были придуманы интринсики, этот вопрос возникает каждый раз, как только заходит речь о какой-либо оптимизации на ассемблере — ведь они для того и были придуманы, чтобы иметь возможность использовать SIMD-инструкции без необходимости программирования на ассемблере. Поэтому короткий ответ — используйте.
- Не вся оптимизация делается векторизацией. Если во времена DOS оптимизация делалась за счёт экономии тактов и количества обращений к памяти, то сейчас основной инструмент оптимизации – это организация оптимальной работы с памятью во избежание промахов кеша.
- Интринсики к новым инструкциям появляются с некоторым запаздыванием. Недостающие интринсики нельзя добавить простым включением заголовочного файла – их поддержка должна быть реализована на уровне компилятора.
- Не на все инструкции возможно сделать интринсики. Когда оптимизация касается точности математических выражений, можно добиться значительных улучшений, программируя непосредственно на математическом сопроцессоре (FPU). Для команд сопроцессора же интринсики отсутствуют. Они также отсутствуют для команд, на результат которых влияют флаги переноса/переполнения/etc.
- Интринсики не представляют из себя какой-либо высокоуровневый фреймворк – по факту, это не более чем лишь ещё один уровень мнемоник над ассемблерными инструкциями, мало чем отличающийся от ассемблерных вставок. Что автоматически означает, что код вовсе не становится более читаемым, а при его написании так или иначе нужно придерживаться ассемблерного стиля мышления.
- Также, из C++ кода вовсе не очевидно, что существуют какие-то ограничения на количество одновременно используемых SIMD-регистров. В 32-х битном ассемблере вы не можете использовать XMM8 наравне с XMM0 / XMM7 – код просто не откомпилируется. А интринсиками вы можете определить хоть тысячу одновременно используемых регистров — и компилятор вынужден будет сам решать, что хранить в регистрах, что в памяти, и как между собой их оптимально комбинировать. В таком случае применение интринсиков не даёт никаких преимуществ в плане увеличения производительности – компилятор работает с ними так же, как и с обычными структурами на C++.
- Программировать на ассемблере не так уж и сложно, особенно если не пытаться делать на нём хитрые высокоуровневые конструкции. Многое можно почерпнуть, изучая ассемблерный листинг простых алгоритмов в режиме отладки и с различными уровнями оптимизации – компилятор от Microsoft производит достаточно лёгкий для понимания и в то же время эффективный код, комментируя его исходным кодом на C++.
- Программирование
- Assembler
Уроки Ассемблера. Язык Ассемблера
Данный туториал предназначен для тех, кто хочет изучить основы программирования на Ассемблере с нуля. Из этого абсолютно бесплатного учебника вы получите достаточное представление о программировании на Ассемблере, благодаря чему сможете подняться на дополнительный уровень в своих знаниях и навыках.
Язык Ассемблера — это низкоуровневый язык программирования, используемый для компьютера или другого программируемого устройства, специфичный под конкретную компьютерную архитектуру (в отличие от большинства высокоуровневых языков программирования, которые являются кроссплатформенными). Язык Ассемблера конвертируется в исполняемый машинный код с помощью программ Ассемблера, таких как NASM, MASM и т.д.
Прежде чем приступить к данным урокам, вы должны иметь базовые знания по терминологии программирования, а также базовое понимание любого из языков программирования (например, изучить уроки по C++), которые помогут вам быстрее разобраться с базовыми концепциями программирования на Ассемблере и быстрее прогрессировать.
Архитектура компьютера
- Постановка задачи. Написать программу, которая выводит на экран строчку «Привет!».
- Разработка алгоритма программы. Алгоритм линейный, разработки не требует.
- Формализация (запись) алгоритма
В текстовом редакторе создаем файл privet.asm и записываем в него следующий код (без номеров строк) :
Описание программы privet.asm
Строки 1 — 3 программы privet.asm содержат описание сегмента данных. Сегмент данных — область память, в которой будут храниться данные для наших программ.
Строки 5 — 17 — это код программы, её исполняемая часть.
В 8 и 9 строках выполняется настройка сегмента данных программы.
Строки 11 — 13 — вывод строки на экран при помощи функции №9 прерывания 21h (подробнее о функциях и работе с ними на следующей лабораторной работе).
15 и 16 строки — стандартное завершение программы.
После символа ‘;’ пишутся комментарии, они не обрабатываются компилятором.
Переход на новую строку
Для организации перехода на новую строку достаточно вывести на экран символы перевода строки и возврата каретки (CR/LF). Эти символы имеют коды 10 и 13. Если в нашей программе необходимо после вывода строки перейти на новую, то для этого достаточно переписать вторую строку программы:
mes2 db ‘Выводим строку и переходим на новую. ‘, 10 , 13 , ‘$’
Переход на новую строку можно выполнить и до вывода сообщения на экран:
mes3 db 10 , 13 , ‘Выводим с новой строки. $’
Задание для выполнения
Написать программу, которая выводит одно под другим следующие сообщения:
Привет!
Меня зовут компьютер!
До свидания!
Ассемблер в 2k21: кто и зачем продолжает писать на машинно-ориентированном языке
Жизнь слишком коротка, чтобы кодить на ассемблере. И всё же в некоторых задачах он до сих пор незаменим.
National Cancer Institute / Unsplash
Мария Даровская
Журналист, коммерческий автор и редактор. Пишет про IT, цифровой маркетинг и бизнес.
Сайт: darovska.com.
В этом году ассемблер ворвался в топ-10 языков программирования по версии TIOBE, а значит, старичка ещё рано отправлять на пенсию. Мы пообщались с разработчиками, которые пишут код на ассемблере, а ещё изучили мнения зарубежных девелоперов на Quora и Stack Overflow.
Где используют ассемблер в 2021 году
Сегодня ассемблер используют как минимум в четырёх направлениях.
Операционные системы и компиляторы. Современные операционные системы, например дистрибутивы Linux, пишут на C/C++, но там есть фрагменты на ассемблере. Некоторые фрагменты g++ и компиляторы для BASIC и Fortran тоже написаны на машинно-ориентированном языке.
Встроенные системы и драйверы. На ассемблере пишут драйверы под Windows для архитектуры процессоров x86 и программы для AVR-микроконтроллеров и Arduino.
Кибербезопасность и хакинг. С помощью ассемблера хакеры взламывают ПО, а разработчики пишут на нём защиту от взлома. Иногда встречаются и вирусы на ассемблере. Их тяжелее обнаружить, и они куда эффективнее высокоуровневых.
Виртуальные машины / эмуляторы. Виртуальные машины тесно взаимодействуют с ОС, поэтому частично написаны на ассемблере. Как, например, LLVM или Surface Duo Emulator.
Есть мнение, что худо-бедно знать ассемблер нужно всем разработчикам. Он помогает отлаживать программы, когда обычные способы не работают, исправлять ошибки в стандартной библиотеке или функциях ОС. Разработчику проще оптимизировать код, если он понимает, как процессор обрабатывает инструкции.
«Считается, что веб-разработчику ассемблер знать ни к чему. Правда, потом страница с текстом потребляет 100% ресурсов процессора и 2 гигабайта ОЗУ. Если честно, всем, кто пишет прикладные программы, полезно знать, как работает компьютер. Хочется, чтобы все эти люди думали не в стиле „запустилось на моём сверхсовременном компе, и ладно“, а хоть немного пытались оптимизировать свои поделки и уж тем более не использовали всякие извращения вроде Electron».
Юрий Иваник,
администратор Telegram-чата про ассемблер pro.asm
Операционные системы и низкоуровневое ПО
Ассемблеры — это целая группа машинно-ориентированных языков программирования. Набор команд ( ISA ) и архитектура конкретного ассемблера зависит от типа процессора. Поэтому для разных процессоров команды тоже будут разными — единого стандарта языка не существует.
«Ассемблер — это лёгкая мнемоническая обёртка для машинных команд процессора, в которой человеку их легче понять и запомнить. Чтобы писать на ассемблере, нужно понимать архитектуру процессора, набор его команд и адресные пространства. Сейчас ассемблер используют в основном разработчики микроконтроллеров, драйверов и чипов — в общем, те, кто непосредственно работает с железом».
Басист Аксель,
team lead, full stack ASP .NET. Комментарий для Skillbox Media
«Иногда ассемблер называют машинным языком, но это не совсем корректно. Машинный язык — это нули и единицы, понятные процессору. А программа на ассемблере проходит фазу компиляции — преобразования кода, понятного человеку, в набор байтов, понятный процессору.
Ассемблер используют там, где важен каждый такт работы исполнительного ядра, высокая производительность или ресурсы крайне ограничены. На языке ассемблера можно писать программы, драйвера, да хоть саму ОС! И такая даже есть — KolibriOS».
Юрий Иваник,
администратор Telegram-чата про ассемблер pro.asm. Комментарий для Skillbox Media
«На ассемблере, например, пишут код начальной загрузки компьютера, который запускается перед средой выполнения языка более высокого уровня».
«Ассемблер незаменим для загрузчиков, демосцен (маленькие демки — интро до 512 байт, как правило), оптимизации кода. В ситуациях, когда крайне важна производительность и мало памяти для кода».
Встроенные системы
«Встроенные системы» — это микрокомпьютеры, которые решают определённые задачи. Они встречаются в автомобилях, телевизорах, цифровых камерах, IoT-устройствах и мобильных телефонах. Для встроенных систем исторически использовали именно ассемблер.
«Когда компиляторы и языки программирования были относительно примитивными, многие программы и даже операционные системы полностью писали на ассемблере. На машинном языке почти не кодили, а Fortran и COBOL плоховато подходили для встроенных систем.
Кросс-ассемблеры и кросс-компиляторы встречались редко — последние работали на мэйнфреймах и с дискет. Поставщики микропроцессоров или микроконтроллеров редко писали хорошие компиляторы, потому что не разбирались в программном обеспечении».
«Несколько лет назад я был разработчиком в компании, которая устанавливала видеодомофоны. Мы закупили гигантскую партию домофонов в Китае, а к ним прилагалось ПО, написанное на С++, документация — всё как положено.
На внутренних тестах выяснилось, что аппаратура страшно тормозит, и клиенты, очевидно, будут недовольны. А так как я был опытным разработчиком, мне поставили задачу ускорить работу системы. Классическая история, когда ошибку менеджеров и руководителей пытаются заткнуть техническими специалистами.
При анализе оборудования мы выяснили, что оно построено на микроконтроллерах с открытой архитектурой, у которых есть очень толковая документация, а вот ПО для них написали китайцы — причём чуть ли не перед отправкой к нам.
В итоге оно получилось страшно сырым и держалось на стандартных функциях из библиотек, написанных сообществом любителей этих микроконтроллеров. Я вооружился документацией и переписал кучу этих низкоуровневых функций на ассемблере. Это заметно повысило скорость работы — в некоторых случаях до 15 раз.
После этого было ещё несколько подобных задач. К программированию на ассемблере мы в основном обращались, когда нужно было «пошаманить» со скоростью выполнения низкоуровневых запросов к железу. Кстати, забавная история: спустя некоторое время я обнаружил в китайских драйверах к экшн-камере кусок своего кода на ассемблере!»
Василий Сысоев,
ведущий разработчик ООО «Встроенные системы». Комментарий для Skillbox Media
Со временем микроконтроллеры и процессоры стали быстрее и умнее, поэтому всё больше популярности набирали языки высокого уровня.
«Ассемблер до сих пор используют во встроенных системах, когда ресурсов мало или нужна жёсткая оптимизация. Но чаще всего код для встраиваемых систем пишут на С».
Евгений Красников,
участник сообщества pro.asm. Комментарий для Skillbox Media
Кибербезопасность и хакинг
«Некоторые важные части микропрограмм для смарт-карт написаны на ассемблере — это помогает защитить программное обеспечение от атак. Ассемблер — единственный язык, на котором вы полностью контролируете двоичный код программы. Это важно, ведь компилятор может оставить „дыру“ в прошивке, через которую злоумышленник взломает аппаратуру».
Любые задачи, с которыми не справляются С/С++
Сейчас встраиваемые системы чаще всего программируют на C — это стандарт. Также популярность набирает C++. А на ассемблере в основном пишут специфические программы, например для цифровой обработки сигналов, или когда высокоуровневые языки уже не справляются:
- не хватает возможностей стандартных библиотек;
- компиляторы не поддерживают нужные функции;
- объектный код генерируется некорректно;
- нужно написать чувствительный к стеку код — например, при программировании драйверов и операционных систем, чтобы работать со специальными регистрами и командами.
Но даже в этих случаях основную часть программы пишут на C или C++, а код ассемблера встраивают с помощью механизма asm. С другой стороны, в некоторых системах код на С работает слишком медленно и переписать его на ассемблере может быть вполне практичным решением.
«Пока в России не запретили квадрокоптеры, я собирал многовинтовые летательные аппараты. Мне пришлось с нуля писать софт на ассемблере для управления периферическим оборудованием. Оно подключается к основному контроллеру коптера. Аналогов не было — они появились позже, у производителей беспилотников типа DJI.
Сначала я писал весь софт на С, но код работал медленно и непредсказуемо. Тогда решил программировать на ассемблере. В общем, сейчас на ассемблере пишут ПО для встроенных систем, носимой электроники, драйверов устройств — когда нужно обеспечить идеальную точность и скорость работы с микропроцессором. Микропроцессоры „думают“ на языке, очень похожем на ассемблер».
Василий Сысоев,
ведущий разработчик в ООО «Встроенные системы»
«Есть несколько случаев, когда оптимизированный вручную язык ассемблера будет эффективнее языка ассемблера, сгенерированного компилятором из исходного кода C. Да и разработчику, привыкшему к ассемблеру, некоторые вещи проще написать на нём. В этих случаях многие компиляторы C допускают встроенную сборку.
Но компиляторы C становятся всё лучше, так что ручная оптимизация почти не нужна. А ещё большинство платформ налагает ограничения на некоторые низкоуровневые программы».
«Сейчас на ассемблере пишут специфичные команды процессора, которые не поддерживаются языком C. Но это уже редкость».
Алан Меллор,
бывший старший разработчик на С в Siemens
Когда ассемблер не нужен
Больше всего затрат уходит на проектирование платы и разработку программного обеспечения. С мощным железом не нужно экономить на памяти и скорости работы программ.
«Чем проще разработать и поддерживать ПО, тем лучше, поэтому на ассемблере с нуля пишут всё реже — в наши дни большинство использует C/C ++ или даже Python. Выигрыш от ассемблера обычно невелик — есть инструменты получше, если вы разрабатываете на языке более высокого уровня.
Вообще, ассемблер нужен довольно редко. Разве что для низкоуровневых процедур установки, базового начального загрузчика и доступа к низкоуровневому оборудованию».
«Сейчас экономия памяти, особенно дисковой, уже не так актуальна, да и скорости процессоров для выполнения повседневных задач вполне хватает. А современные компиляторы зачастую оптимизируют код по скорости даже лучше человека. Поэтому острой необходимости в языке ассемблера уже нет».
Евгений Красников,
участник сообщества pro.asm. Комментарий для Skillbox Media
«Ассемблер активно вытесняется другими языками, потому что программы на них проще писать, поддерживать, структурировать и понимать. Ассемблер оставляют в частях, где нужна чёткая последовательность действий, или когда работают напрямую с железом. Например, инициализируют микроконтроллер».
@smth_new_stalker,
участник сообщества pro.asm. Комментарий для Skillbox Media
Почему в 2k21 круто знать ассемблер
Процессор не выполняет напрямую код на языках высокого уровня. Например, инструкции на интерпретируемых языках вроде Python сначала преобразуются в байт-код. А в некоторых компиляторах С/С++ команды сначала переводятся на язык ассемблера, а уже потом в машинный код объектного файла.
И хотя в большинстве компиляторов уже нет промежуточного перевода, ассемблер часто выступает промежуточным звеном между человеко-ориентированным и машинным языком.
«Нужен ли ассемблер всем разработчикам и программистам? Нет, но знание машинного языка по крайней мере одного процессора CISC, например Intel x86, и одного процессора RISC, желательно семейства ARM, даст вам хорошее представление о том, на что способен и не способен процессор».
Однако с ростом IoT-устройств, умных камер, видеорегистраторов и других систем популярность ассемблера снова выросла. Так что, если разбираетесь в электронике и железе, учите ассемблер — без хлеба не останетесь.
На самом деле ассемблер — это низкоуровневые двоичные инструкции, которые программисты пишут словами и символами.
«Ассемблер помогает понять внутреннюю кухню, что „под капотом“ у программы: работа систем, процессоров, памяти и прочее. Для программиста на Python или JavaScript, правда, это не так важно, как, скажем, для того, кто пишет на C/C++. Причём даже программисту на C/C++ это нужно не на начальном этапе изучения языка».
Евгений Красников,
участник сообщества pro.asm. Комментарий для Skillbox Media
Читайте также:
- C++ в 2k21: где используется, какие перспективы, что надо знать для старта
- Ещё один гадкий я: 10 страшных грехов Microsoft
- Чем Rust отличается от «плюсов»: откровение ветерана С++