Pull to refresh

Comments 167

А ещё в природе есть такая вещь, как i386 в корпусе PQFP. У нас под него писали на ассемблере, потому что nasm может сразу выдать Intel hex, содержащий только нашу программу, а возиться для получения аналогичного результата на C никому не хотелось.

UFO just landed and posted this here

На работе у меня Windows (7, WSL нету), там не ELF’ы и objcopy есть разве что в cygwin (у меня тоже установлен). И вряд ли всё так просто: нужно разместить безусловный переход по строго определённому адресу (вектора прерываний), а сама программа должна быть по нулевому адресу. И ещё к аргументам нужно добавить, как минимум, -march=i386 -static -Os. И нужно понять как записывать и считывать регистры специального назначения. Разобраться можно, но зачем?


Помимо этого написание программы на ассемблере позволило использовать «память» в режиме «только чтение», обошедшись без стека и без собственно реализации записи в память — самой памяти в системе никогда не было, была имитация на FPGA, которую тоже нужно написать.

Fasm тоже прекрасно выдает чистый бинарник.

Не понимаю, к чему этот комментарий. Если какой‐то *asm не способен выдавать «чистый бинарник», то зачем он вообще нужен?

К тому что из ряда ассемблеров вы не можете получить ничего, кроме, скажем, .exe без дополнительных утилит.
На Fasm много проще создать код «вне формата».

Конечно, на то он и FASM. ;)

Говоря про Ирвина, я бы «обновил» издание до 7-го, пусть оно и на английском языке
Добавил в скобки факт его существования. Благодарю.
Хех… Я был уверен, что НИКОГДА уже не увижу на Хабре (да и любом другом сайте) статьи про ассемблер.
Вспомнил молодость, ассемблер, Clipper, с чего начинается Родина… В смысле, первые символы dos-овского .exe-файла и первые символы .exe-файла под Windows, как ставили Windows 95 поверх MS-DOS и Windows 3.1, а потом пытались откатиться при помощи установки Windows 3.1… Эх…
UFO just landed and posted this here
Одно дело ассемблер для этих микроконтроллеров, другое дело x64
UFO just landed and posted this here
UFO just landed and posted this here
Аналогично. Управляем установкой, МК — Atmega какая-то, пишем на С. Наверное можно и на асме, но зачем? На ассемблере написал «Жизнь» — когда на курсах по нему учился и всё.
В мире, где железо стремительно дешевеет, а программиста — стремительно дорожают ваше утверждение выглядит сомнительным.
Дешевеет уже давно не так стремительно как раньше, и со временем будет дешеветь всё меньше.

Впрочем соглашусь, что за исключением очень редких случаев, писать прямо на асме нет смысла.
Ну да, поскольку мы, кажется, уже упёрлись в стоимость пластика и рамки с выводами.
UFO just landed and posted this here

Я на ассемблере не пишу, пишу на C++, но знать и понимать его стоит. Как минимум для целей отладки или понимания происходящего под капотом: иногда после этого сильно по другому смотришь на более высокий уровень. Обязательно нужно знакомится с соглашениями по вызовам для вашей платформы и так далее. Особенно это актуально для всякого embedded.

Может быть у вас есть пример из реального проекта, когда вставки на ассемблере существенно дали выигрыш в скорости для десктоп приложения? Или использование ассемблера все таки ограничивается драйверами и узкоспециализированным ПО, где действительно нужно быть уверенным в том, что происходит на уровне процессора? Я не пытаюсь приуменьшить достоинства ассемблера, просто мне кажется, что современные компиляторы оптимизируют код лучше, чем среднестатистический программист сможет написать ассемблере.

Знание ассемблера безусловно полезное, например в случае отладки, когда есть только бинарный код, но насколько целесообразно в прикладном ПО делать вставки на ассемблере?
Ну, здесь на Хабре была серия статей — человек написал на языке ассемблера сервер и форум на его основе, поищите.

Да, и выяснилось, что он не самый быстрый. Например, фаза рендеринга markdown в html там не кешируется.

Ну, я просто вспомнил пример достаточно большого проекта. Да и кеширование/его отсутствие — это не к языку претензия.
А к чему? Если написание остального съело время, которое требовалось для осмысления необходимости кэша и его реализации.
Оно могло изначально не предполагаться по каким-то причинам. Или необходимость его не была осмыслена в силу неопытности автора, при том что времени у него был вагон.
Ну так это хороший пример premature optimization. Оптимизации не того, не там и не тогда, когда нужно.

Кстати, кешируется:


create table Posts (
  id          integer primary key autoincrement,
  threadID    integer references Threads(id) on delete cascade,
  userID      integer references Users(id) on delete cascade,

  postTime    integer, 
  ReadCount   integer,
  Content     text,
  Rendered    text  -- здесь
);
Если говорить про ассемблерные вставки, а не про написание программы полностью на ассемблере, то они в высокопроизводительных программах используются повсеместно: в рендерерах 3D, в видеокодеках, в программах для изменения размеров изображений.

Есть еще противоположная сторона оптимизации: когда нужно уместить код в минимальное количество байт, тогда приходится использовать ассемблер и всякие трюки. Например, декомпрессор в паковщике исполняемых файлов upx написан на ассемблере.
UFO just landed and posted this here
Если прям реальный бытовой проект из моего опыта, в системе документооборота были переписаны на асм функции подсчета контрольных сумм файлов и хеширования паролей, что в обоих случаях достаточно заметно снизило время ожидания клиента.

Ну, не буду сильно спорить..


Возьмем достаточно большой файл.


time cat 700mb.iso > /dev/null 

real    0m13.280s
user    0m0.024s
sys 0m0.632s

time cat 700mb.iso > /dev/null 

real    0m0.275s
user    0m0.004s
sys 0m0.220s

Из диска он читается 13с, из кеша — меньше секунды.


time cat 700mb.iso | md5sum -
73fe07300ad456cff2f7b9524c295fd1  -

real    0m1.518s
user    0m1.336s
sys 0m0.544s

Хеш-сумма этих 700 мб считается около 1 с.


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

Возможно, в высокоуровневом варианте был какой-то алгоритмический просчет, вроде лишних выделений памяти, чтения из файла по байту и т.д?

Вот сгенерированный С++ компилятором код умножения на 5 и суммирования 1024 байт из буффера, и я с трудом представляю, каких сил будет стоить написание и сопровождение подобного кода вручную:

godbolt.org/g/2QW1Qx

Так на ассемблере писать никто не будет. С этим замечательно справится компилятор.
Цикл из 1024 сложений и одно умножение на 5 (2 сдвига и сложение) — это как-то более логично.

А что ж тогда писать на ассемблере в контексте оптимизации?

А это смотря что нужно оптимизировать. В смысле написания и поддержки — 2 строки на C++ конечно же, оптимальное решение.
В смысле быстродействия… Напрашивающийся цикл из 1024 сложений выглядит лучше… На 8086 это бы прокатило ;) Но компилятор лучше знает архитектуру процессора, и, подозреваю, что эти странные 128-битные операции неспроста..

Напрашивающийся цикл из 1024 сложений выглядит лучше…
Вряд ли он лучше. Работать будет в лучшем случае так же, но скорее всего — медленнее. И дело не только в странных и не очень операциях, а еще и в ветвлениях и затратах на сам цикл.

Да. Если написать коротко и понятно — это будет, скорее всего, неоптимально. А если написать оптимально — будет сложно (а то и невозможно) поддерживать.
Так, в вашем примере, если заменить int на short (или long) — компилятор использует совершенно другие инструкции. Получается, для разных типов данных написанный на ассемблере алгоритм нужно переписывать.

Вдогонку к предыдущему сообщению:

… а С++ реализация cosine similarity между одним float-вектором размерностью 192 и 1.7 гигабайтами других таких же векторов с использованием SIMD-intrinsics вычисляется за 0.3 секунды на процессоре Celeron J1900 @ 1.99GHz, на NAS-е, внутри виртуалки.

Не то, чтобы сильно быстро, но это даже не ноутбучный процессор, не самая быстрая память, еще и виртуалка.
Обработка изображения в моем/нашем FastRawViewer — это SSSE3 (и AVX2 на тех процессорах, где оно есть).
Это не ассемблер в чистом виде, а вот как-то вот так:

xlo = _mm256_unpacklo_epi16(row0, zero);
xhi = _mm256_unpackhi_epi16(row0, zero);
pix0_0_7 = _mm256_cvtepi32_ps(xlo);
pix0_8_f = _mm256_cvtepi32_ps(xhi);
row1 = _mm256_permutevar8x32_epi32(_mm256_loadu_si256((const _m256i*)&srcrow2[scol]), b256m);

Относительно очень близкой по логике обработки dcraw/LibRaw, выигрыш на SSSE3 — примерно порядок, от AVX2 — еще раза в полтора (плюс dcraw/LibRaw — целочисленные, а FRV — внутри в плавучке, что несколько улучшает качество теней)

Так в том-то и посыл, что по-моему, чаще всего ассемблер для задач оптимизиции нужен только для того, чтобы читать код. А писать и проще и лучше на С/С++, используя интринсики там, где это нужно. Главное отличие в том, что программисту не нужно следить за регистрами и адресами.

Возможно, бывают какие-то хитрые алгоритмы, которые очень сложно выразить в C и проще — на языке ассемблера, но я пока не смог представить ничего подобного.

Конечно, бывают задачи, которые проще решаются на ассемблере: первое, что приходит в голову из того, что писал сам — загрузчик в MBR. Но когда речь идет об оптимизации, я подозреваю баг компилятора или ошибку в высокоуровневом коде.
Так в том-то и посыл...

Полностью согласен.


что приходит в голову из того, что писал сам — загрузчик в MBR

Что удивительно, беглый поиск не показал мне ни одного pureC бутлодера в MBR на Си… Хотя вру: https://www.codeproject.com/Articles/664165/Writing-a-boot-loader-in-Assembly-and-C-Part хотя и тут без вставок никак.

Более новые архитектуры, чем x86, изначально разрабатываются с учётом того, чтобы загрузчик можно было писать на чистом Си с интринсиками.

Говорить «для загрузчика необходим ассемблер», глядя на x86 — всё равно, что говорить «для завода двигателя необходим кривой стартёр», глядя на 2101.

Тут конкретно про MBR речь была. Так то да, взять тот же UEFI.

Кстати, вспомнилось: мне однажды нужно было сделать инъекцию: был закрытый код, который вызывался в контексте ISR и ошибочно вызывал функцию логирования (косвенно, через общий код), которая аллоцировала большой буффер на стеке, чем срывала его, будучи запущенной из ISR.


Инъекция заключалась в том, что имя функции логирования подменялось (средствами GCC и линкера) и вместо неё вызывался враппер, который дополнительно делал проверку. Вот врапер оказалось проще написать на ассемблере, используя знание о соглашениях на вызовы, не портя стек (функция была с переменным числом аргументов).


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

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

Но смысл то один — пишу ровно ассемблерные куски правда на немного птичьем языке
MacIn, acyp спасибо за указания на статьи. Но я имел ввиду пример, когда целесообразно сделать ассемблерные вставки. Вот пишу я некоторое десктопное приложение на C++ и в какой-то момент понимаю, что вот здесь лучше написать на ассемблере. Есть ли такое в современное практике у кого-нибудь? И если да, то какой прирост получился? Понятно, что если захотеть, то можно написать сколь угодно большое приложение на ассемблере, включая многопоточность, работу с сетью и т.д. и т.п. Но хочется не доказательства того что это возможно, а примера когда это было целесообразно. Не холивара ради, а просто из любопытства. Так как я вряд ли смогу написать на ассемблере эффективнее компилятора, но вполне допускаю, что такие люди есть и такие ситуации тоже.
У меня есть вполне обширный опыт по написанию asm-функций для вычислительно-интенсивных частей алгоритмов, с применением simd-ов. Прирост для конкретно этого кода в разы, иногда в десятки раз. Для всего приложения включение оптимизаций даёт прирост в несколько раз. Правда этих оптимизаций в приложении уже довольно много накопилось.
Но это именно отдельные функции, а не вставки asm-а в С-код.
Спасибо. Тут еще выше привели пример с инструкциями SSE3, не совсем про ассемблер, но то же интересно.
Интринсики (intrinsics) — неплохая штука, когда нужно быстро объяснить глупому компилятору, как можно сделать при помощи simd-ов что-то относительно простое. Но к сожалению они не дают контроля над регистрами. Даже в том примере, на который вы обратили внимание, нет никаких гарантий, что, например, переменная «xlo» не будет запихнута в стек в промежутке между первой и третьей строчками кода (а это, на минуточку, 32 байта). И такие ситуации частенько возникают, когда количество одновременно используемых simd-регистров (переменных) становится больше 6-8 (при том, что всего их доступно 16 в режиме х86-64).
С другой стороны хорошие компиляторы могут и самостоятельно использовать эффективные simd-инструкции при правильно написанном С-коде и высоком уровне оптимизации. И временами это бывает не менее эффективно, чем использование интринсиков.
С intrinsics ещё такая штука, если поддержку того или иного instruction set надо проверять в runtime, приходится generic/optimized версии функций компилировать отдельно. Или же можно использовать явный вызов через asm. Кстати, о SSE… Не зря например в протоколе Kafka используют CRC32C. Потому что для него есть инструкции в SSE4.2.
UFO just landed and posted this here
Да, по описанию всё так, только не работало. Оказывается починили в gcc 4.9:
It is now possible to call x86 intrinsics from select functions in a file that are tagged with the corresponding target attribute without having to compile the entire file with the -mxxx option. This improves the usability of x86 intrinsics and is particularly useful when doing Function Multiversioning.
UFO just landed and posted this here
Использование знания ассемблера в C/C++ разработке не ограничивается только переписыванием кусков кода на ассемблере. У меня лично самый частый сценарий использования ассемблера — это смотреть, в какой вообще ассемблерный код компилятор превратил критичный кусок C++ кода. Например заинлайнил ли вызов функции, или статическую константу. Если обнаружен неоптимальный вызов — значит можно пытаться выиграть ещё, причём косвенно (меняя высокоуровневый код), кстати то ещё развлечение по сравнению с переписыванием напрямую на асме.
Лет ~20 назад в бытность школьником писал свою реализацию графических примитивов для VESA-режимов (с высоким разрешением) на Borland Pascal (я тогда не знал что есть родные BGI драйверы для этих режимов). Вывод работал заметно медленно, особенно линии и окружности. После переписывания этих методов (и еще нескольких оптимизаций) на inline-ассемблер скорость возросла на порядок минимум. Но сейчас компиляторы гораздо умнее.
В драйверах к серьезным ОС, к счастью, почти не встречается ассемблер — все абстрагируется самой ОС и HALами. Там и так все на шаманстве зачастую, из-за специфики работы с железом, а если еще и писать опкодами, то это шаманство еще и станет нечитабельно.
Может быть у вас есть пример из реального проекта, когда вставки на ассемблере существенно дали выигрыш в скорости для десктоп приложения?


  1. Битовый массив. Ассемблерные инструкции позволили напрямую ставить/снимать/проверять бит. То же самое чрез маску — работало прилично медленней.
  2. Критическая секция для однопроцессорных машин. Работала без переключения в режим ядра для случая, когда вход в критсекцию свободен. Применялась в серверном приложении при одновременной работе 20 тредов. Проблема была в том, что нужно было обращение к регистру FS, которое компилятор не поддерживал

но насколько целесообразно в прикладном ПО делать вставки на ассемблере?
В прикладном не знаю, а в библиотеках такого очень много. Можете полюбоваться на ассемблерную версию memchr из glibc.
Битовый массив. Ассемблерные инструкции позволили напрямую ставить/снимать/проверять бит. То же самое чрез маску — работало прилично медленней.
Хм, мне казалось, что компиляторы умеют генерировать битовые инструкции из обращений к битовым полям.

На моем опыте под битовыми операциями понимаются следующие конструкции: https://godbolt.org/g/1Ayj7e
В то время как это вполне себе описывается:
mov eax, dword ptr [x]
sub eax, 5
BTR argc, eax
add eax, 7
BTS argc, eax

Указанный gcc8.0 все еще в разработке, а текущие компиляторы не умеют оптимизировать работу с битами.
Конкретно Delphi 5 — не умел. Компиляторы вообще не умеют использовать наборы инструкций, получившие распространение после их написания. :-)
Напомню, что здесь обсуждают "как зачем писать на ассемблере в 2018 году", а не «зачем надо было писать на ассемблере в 1999 году, когда компиляторы были глупее, а системы команд — проще».
А чем 2018ый год отличается? Вы или ждете, пока выйдет релиз компилятора с поддержкой нужных вам команд — или пишете ассемблерные вставки. Если не боитесь, то на опенсорсных компиляторах можно ещё и нестабильные ночные билды попробовать.

Лично мне больше нравится стабильный проверенный компилятор + ассемблерные вставки, чем экспериментальные версии.

Есть пример проекта, когда использование ассемблера дало прирост в энергоэффективности устройств.
В студенческие годы разрабатывал с товарищем АСКУЭ (автоматическая система контроля и учёта электроэнергии). Использование ассемблера и знание режимов работы микроконтроллера позволило запитать 7 кучек (по кучке на подъезд) из 30+ устройств (по устройству на квартиру) очень небольшим блоком питания. Не помню точно потребления, но та что-то в районе микроампер в режиме ожидания и раз в 15 минут при опросе — милиамперы для каждого устройства) Что дало преимущество, когда понадобилось поставить ИБП для всех устройств.

Есть такие примеры (это десктопные игры): ассемблерные реализации работы с растром и геометрией (особенно на базе MMX/SSE) зачастую дают существенный выигрыш в скорости. Беда правда в том, что такой код привязан к платформе, и не работает на ARM :(
Документация FASM слабая? Простите, но базовая документация, которая идет в стандартной поставке — исчерпывающая.

Я не знаю откуда это пошло, что у FASM документация слабая???


Это не так! FASM ассемблер простой и та документация которая идет в стандартной поставке и вправду исчерпывающая.

У нас с коллегой немного противоречивое мнение. Когда он появиться (а время у него GMT+8) мы придем к общей позиции и обязательно ответим вам, так же в случае чего подредактируем этот момент в статье. Благодарю.
Возможно это и известно вам, но все же, уж коли зашла речь: в избранное можно заносить не только статьи, но и комменты. Сверхполезная вешь, ибо в комментах часто бывает много полезной информации помимо статей. Ну и плюс можно вернуться к обсуждению позднее.
Нет не замечал, спасибо что сказали. FASM кстати поправили.
Вот-вот) Иконка закладки, аналогичная иконке закладки у статей. Просто подумал, что ситуациях подобного рода будет очень удобно добавлять комменты в избранное, дабы в дальнейшем обсудить их с коллегами.

За FASM спасибо, у них ман действительно неплохой.
Обсудили и поправили. Спасибо.
Статья посвящена языку ассемблер с учетом актуальных реалий.

К сожалению про язык ассемблера в статье ничего. Да и озаглавить бы её стоило, наверное, «Сравнение ассемблеров», чтобы не вводить людей в заблуждение. По моему мнению, (не могу судить о большинстве) заголовок «Как писать на ассемблере в 2018 году» предполагает, что в статье пойдёт речь о там как писать код на языке ассемблера с учётом современных наборов команд или технологий.


А кто-то реально сейчас пишет на TASM да ещё и для DOS? Устаревшие вузовские курсы не в счёт.

Вводная статья, думаю многим хотелось бы больше практики, в следующий раз мы сосредоточимся именно на высказанных вами вопросах. На тасме только поддерживают старые проекты.
UFO just landed and posted this here
Реквестирую разработку на асм под х64.
Спасибо, что хоть кто-то ещё пишет про ассемблер. Очень мало про ассемблер статей
Есть такое дело. Добавили. Благодарю.

Кстати, для MacOS X тоже:


https://board.flatassembler.net/topic.php?t=13413


И конечно для Menuet/KolibriOS и всяких экспериментальных ОС. Вообще, FASM, несмотря на то, что написан на ассемблере, переносится очень легко на всяких ОС. Намного легче чем тот же NASM.

Не знаю, как других, а меня уже давненько выбешивают такие статьи… «Как писать на ассемблере в 2018»…
Очевидно, что имеется ввиду The Assembler x86. Не какой-то там ARM9 с его баррел-шифтами (правда, кому они нужны?), не какая-то там SH, и не о разнице между MIPS и Loongson, не об Эльбрусовском чего-то там они придумали, не о сетевых всяких там — об их величестве x86.
Ну, ладно, допустим… Об x86-64… Допустим… Так опять-же не об жонглированием ядрами и атомных инструкциях…
«Я писал на АСМЕ» — ага, да… Вотиманно на «АСМЕ».
правда, кому они нужны?


Гггг. Сегодня сравнивал производительность на Cortex-M3 двух реализаций AES-128 — на C и на асме, в последней как раз barrel shift используется.

Ну, асм быстрее. Меньше, чем в два раза, но быстрее. Ещё б на фоне недостатков кода на нём это кого-нибудь бы в контексте данного проекта волновало бы.
UFO just landed and posted this here
В целом всё правильно, но можно же было всё тоже самое и без приношения жертв богу сарказма. Не все верят в то что на технической площадке обязательно надо блистать остроумием.
На сайте А.В. Столярова книга по NASM, а у вас в таблице она отнесена к GAS. И если не ошибаюсь, то синтаксис AT&T там только упоминается дальше 5й страницы идет интеловский синтаксис.
Спасибо что заметили. Исправили.
Использование ассемблера в вычислительном коде скорее всего приведет к замедлению ваших программ.
«Компактный код» сейчас совершенно не значит «быстрый код».
Писать UI, или IO на ассемблере это практически убивать свое время на скучнейшую работу. Если так уж горит — можно сделать всю обвязку на плюсах или еще чем то, а потом сделать ассемблерную вставку.
«Использование ассемблера в вычислительном коде скорее всего приведет к замедлению ваших программ.»

Моих — да, любых — нет)
К слову, разработчики видеокодеков по-прежнему активно используют асм. Посмотрите исходные коды libvpx, libx265, libx265, да вообще ffmpeg директории asm.
UFO just landed and posted this here
Использование ассемблера в вычислительном коде скорее всего приведет к замедлению ваших программ.

А вы пробовали? Я да. Несколько раз. И дело в том, что нет, не получается. На ассемблере всегда получается намного быстрее.


На C/C++ можно написать быстрые программы, но дело в том, что надо ясно представлять как они должны выглядеть на ассемблере. А потом обмануть компилятора, чтобы сгенерировать нужный ассемблерский код. Для меня проще написать сразу на ассемблере. :P

UFO just landed and posted this here
Производительность IPP оставляет желать, скажу я вам. У меня куча самописных asm-функций быстрее работают, чем их аналоги в IPP.

Думаю, все хоть раз игрались с компиляцией программы, а потом с декомпиляцией, чтоб посмотреть, как компилятор выкрутился. Так вот, в последние годы он настолько всё оптимизирует, что для понимания получившегося ассемблерного кода нужно знать назубок чуть ли не все команды процессора (а для x86-64 это здоровенная такая куча), да ещё и огромную кучу трюков. Может, конечно, кто-то на такое и способен, я же просто смирился с тем, что компиляторы оптимизируют лучше человека (да и я не видел, чтобы кто-то этот тезис оспаривал).


То о чём вы говорите верно разве что для программирования AVR или MSP430, где горсть команд, и человек может ещё конкурировать с компилятором, и где без ассемблера порой никак.

Добавлю, что если в прошлом веке системы команд разрабатывались с расчётом, чтобы человеку было удобно писать эффективный ассемблерный код — то сейчас этот фактор вообще не принимается в расчёт. Единственное, что теперь важно при разработке системы команд — смогут ли генерировать эффективный код существующие компиляторы после небольшой доработки.
Может, конечно, кто-то на такое и способен, я же просто смирился с тем, что компиляторы оптимизируют лучше человека (да и я не видел, чтобы кто-то этот тезис оспаривал).

Я не только оспариваю, но и несколько раз проверял экспериментально. Получается парадоксальный результат — все верно – компилятор компилирует отлично, но все равно, программы на ассемблере получаются быстрее. Дело в том, что человек на ассемблере пишет не так как на ЯВУ (и в этом ошибаются все, которые сравнивают программирование на ассемблер и ЯВУ). Когда пишет на ассемблере, программист выбирает другие алгоритмы, другую архитектуру программы.


Потому что ленивый и выбирает то, что легче написать именно на ассемблере и то что легче будет поддерживаться. Та же самая программа, можно написать и на C/C++, только код будет смотреться совершенно дико и неестественно.


Я проверял все это несколько раз, и всегда результат одинаков. Можете попробовать сами. Надо только писать более менее реальную программу, а не "алгоритм XYZ".

«В чистую», в абстрактном соревновании человек сливает современным компиляторам по эффективности генерации и эф. генерируемого кода.
Но у человека есть преимущество — знание данных и видение картины в целом, что позволяет иногда отсекать, скажем так, «боковые ветки» в решениях, что-то использовать повторно и так далее.
Компилятор безусловно быстрее генерирует ассемблерный код из понятного ему описания алгоритма, чем это делает человек.
Зачем вы повторяете то же самое другими словами?
Просто хотелось убедиться, что мы об одном и том же. Вруг вы считаете, что бинарный код, сделаный компилятором будет быстрее.
Это зависит от того, насколько умел программист на ассемблере, который соревнуется с компилятором, разумеется. Среднего ассемблериста компилятор побьет и в скорости исполняемого кода.

Я не думаю, что это настолько однозначно. Статистики, разумеется, ни у меня ни у вас нет, но я бы скорее согласился когда ассемблеристов было больше. Сейчас их мало (рынок) и те, кто остались, как мне кажется должны быть достаточно высокой квалификации.


Ну и главное. У компилятора есть только один шанс сгенерировать код (если это не JIT). А у програмиста есть возможность написать, потестировать и переписать.


Хотя вот задумался и стало интересно есть ли сейчас компиляторы на нейросетях или ген. алгоритмах, которые могут медленно и упорото упорно перегенерировать код, пока не достигнут максимальной производительности.

Бывает есть два шанса. См. Intel compiler PGO.
Все верно — человек может итерационно делать код компактнее и бастрее. Но никто не будет этого делать для всех участков — как обычно, серьезно оптимизируется только критичное место. А остальное — используя какие-то best practises. Но и компилятор делает то же самое не хуже (а критичные места мы можем на вставках написать). При этом компилятор, оптимизируя «прочие» места может, скажем, держать в памяти хоть «три страницы» кода и эффективно распределять регистры, в долю секунды делая это лучше, чем будет далть для «прочих мест» человек. Зато человек может применить нестандартную конвенцию вызова и так далее.
UFO just landed and posted this here

Да ладно. x86 еще нормально. x64 — вот это ад.

В списке источников явно не хватает Агнера нашего Фога (Agner Fog) с его software optimization resources.
Статься начинается как писать на асм, а по сути рассматривается как обучать.
На мой взгляд учить asm сейчас стоит для всяких ARM Cortex M3 на крайний случай Broadcomm/Raspberry Pi. На PC потребности в нем минимальны. А в embedded — это требование жизни.
x86 особенно, MIPS частично, во встроееной технике проиграли маркетинговую войну ARM. Его и стоит учить.
Keil, gnu asm.
bob.cs.sonoma.edu/IntroCompOrg-RPi/intro-co-rpi.html — интересная книга.
На мой взгляд учить asm сейчас стоит для всяких ARM Cortex M3 на крайний случай Broadcomm/Raspberry Pi. На PC потребности в нем минимальны. А в embedded — это требование жизни.

Три года уже работаю в embedded'е ни разу за всё время ни мне ни кому из коллег не довелось использовать ассемблер, дизассемблер приходилось а вот ассемблер нет. Даже тем кто пишет под AVR. Так что очень преувеличено его необходимость в современном embedded'е.
Что надо это понимание как будет выполнятся программа на том чипе под который пишешь и каким ботом это может выйти.
На PICах тоже не требуется? Из-за большого разнообразия комбинаций периферии в линейке наверняка кто-то подбирает впритирку под задачу.
Вполне вероятно что где то так и делают. Но там где работаю я обычно сжатые сроки, там не до того чтобы вылизывать всё до байта. На моей памяти был только один проект в котором использовали все ресурсы на 100%, но всё равно в ассемблер не перешли. Слишком дорога получается разработка и поддержка.
На моей памяти был только один проект в котором использовали все ресурсы на 100%, но всё равно в ассемблер не перешли.

А значит, на 100%, все равно не использовали. :P

UFO just landed and posted this here
Java — не первый ЯВУ с аппаратной реализацией:
Неудивительно, что ни один из «байткод-процессоров» — ни для p-кода, ни для Java — не стал коммерчески успешным. (Сюда же можно отнести и намного более ранний процессор Intel iAPX 432 (1981) — аппаратную реализацию байткода для Ады.) <...> Для того, чтобы «в железе» отслеживать зависимости между данными, распределять их по «железным» регистрам, и переставлять обращения к ним так, чтобы уложиться в «железные» ограничения — требуются очень сложные механизмы. Получается, что при одном и том же уровне полупроводниковых технологий эффективнее создать процессор для простой ISA, и на нём реализовать трансляцию байткода, — чем выполнять этот же байткод «в железе». Вновь и вновь мечтательные айти-предприниматели убеждались, что превратить «универсальный машинный язык» в настоящий — хоть и возможно технически, но коммерчески бесперспективно.
Ну как раз ARM9EJ-S был коммерчески успешным. Мобильников на нем было много. У самого был мобильник c этой технологией. Отличались они от собратьев в 2-3 более шустрой явой.
Если бы Jazelle была коммерчески успешной, то её бы не выпилили из всех последующих чипов, я полагаю?
Автор книги «Processor Design» считает так же: «These Java-specific processors prompted some interest, but not a lot of enthusiasm.»
Увы, потребитель пропал. Вы когда в последний раз держали в руках кнопочный телефон с поддержкой java? А в смартфонах — JIT-компиляциия, своя VM, они не нуждаются в специальном процессоре. Так что вторая реинкарнация (на Cortex A8) тоже завершилась.

Так что запилили-выпилили, опять запилили — опять выпилили, запасаемся покорном и ждем третьей реинкарнации.
Что касается ЯВУ с аппаратной реализацией, то если найдете — почитайте «Как паскаль и оберон попадают на Самсон», вот краткое описание. Там сделали взаимную заточку кодогенератора под железа, а железа под кодогенератор.
Какое там обучать, если даже в заголовке «The Assembler»?
Если обучать, тогда надо брать какой нибудь восьмибитный процессор с регистровой адресной парой, чтобы студентам жизнь мёдом не казалась — КР580, Z80, MOS6510. И писать на этом многозадачную операционку с TCP/IP. Без ограничений языка. Хочешь — пиши на С, только компилятор пиши сам…
Только, боюсь, 70% студентов вылетят нафиг.
Да и 70% преподавателей тоже…
В Технионе (бывшем в двадцатке лучших CS-факультетов в мире) до сих пор учат ассемблеру PDP-11, просто потому что для него лекции и задания уже составлены и выучены наизусть поколениями преподов, а новые составлять ни у кого руки никак не дойдут.
Я у тамошних преподов спрашивал, не собираются ли они переводить курс на ARM или на ещё что-нибудь актуальное для практических (embedded) целей. Нет: говорят, что если и будут переводить, то на x86 или x64, просто по причине доступности инструментов разработки и сред выполнения.
:-(
И они молодцы (кроме шуток). У PDP-11 отличнейший высокоуровневый ассемблер, который даст нормальное понимание как оно вообще всё.
Не надо готовить ARM (Java/Pascal) программистов в ВУЗах, все будут в пролёте! Уже, в общем-то, кадровый голод. Не языкам студентов учат, и не конкретным ассемблерам, а общим принципам.
1) Неплохо было бы обучать общим принципам на примере чего-нибудь практически применимого, чтобы выигрыш был сразу по обоим фронтам. Ассемблер x86, или ARM, или MIPS какой-нибудь, для обучения общим принципам ничуть не хуже PDP-11, но конспекты по нему не отправятся в мусорку сразу же после экзамена.

2) Как раз для обучения современным архитектурам — PDP-11, с его сверх-высокоуровневыми инструкциями, довольно нерепрезентативен.
Люди обычно не понимают чему их обучают, потому, что для того, чтобы задать правильный вопрос нужно знать половину ответа.

У PDP-11 отличнейший высокоуровневый ассемблер, который даст нормальное понимание как оно вообще всё было 30 лет назад ;)

Ооо, мне на моей первой в жизни работе как юному падавану досталась задача — запилить компилятор для Z80. Было познавательно.
У нас использовали ассемблер машины VAX (2010е годы). Но наравне с х86, просто в разных курсах.
По дальнейшему размышлению, отличной платформой для обучения новичков ассемблеру/низкоуровневому программированию был бы мегапроцессор — чтобы выполнение машкода сделать буквально осязаемым.
Без такого экспоната процессор так и останется чёрным ящиком, в котором волшебные гномики понимают и выполняют инструкции.
Забавно, темой моей дипломной работы был примитивный эмулятор процессора с перегружаемым на ходу микрокодом, с доступом к нему на всех уровнях и явным параллелизмом. С фактическим доступом к некоторым вентилям на шине. Тоже формально с обучающей целью.
почему-то ожидал тут увидеть какую-нибудь модную кросплатформенную замену redasm…
В скором времени мы запустим портал с тестами (вопрос – варианты ответа) на знание ассемблера и архитектуры компьютера.

Ох всегда меня это раздражало. Знания ассемблера, блин ну вы же сами пишите:
Язык ассемблера — символьная форма записи машинного кода, использование которого упрощает написание машинных программ.

Ну что значит в таком случае знание ассемблера? Если вы имеете ввиду что это x86 так так и напишите. Но есть ведь ещё ARM, MISP, x51, AVR, Z80, Microbalze, NIOSII, PIC и прочее прочее. Так знания какого ассемблера вы хотите проверять?
Обычно (если явно не указано) подразумевается x86. Это как Default City =)
Сам писал на FASM, Очень удобная штука. Одна библиотека для x86-64, до сих пор используется в продакшине. Кстати, автору на заметку, FASM также поддерживает ARM. А еще у Томаса есть версия FASM G которая более продвинута, и способна собирать «x86, x64, 8052, AVR, or Java Virtual Machine». Думаю это стоит указать.
FASMG в принципе можно обучить собирать под любую платформу, потому что набор инструкций там — это просто макросы. То есть x86 и всё остальное там реализовано просто наборами макросов. PE-файлы и таблицы релоков тоже формируются на макросах.
И макросы FASMа — это реально круто, такой гибкости можно завидовать. Можно пересобрать файл на его макросах, например. Сделать работу за компоновщик т.е.
TASM жалко. в свое время был весьма неплох.
Он даже поддерживал ООП — с наследованием, инкапсуляцией и полиморфизмом :)
А мне несколько лет назад очень нравилось на masm32 делать мелкие утилитки. Да, возни с WinAPI немало (не то что под DOS), но зато обращение к памяти проще, не надо запариваться с типами переменных, это даже удобнее Си.
Конечно, в основном это мелкие вещи, которые один раз пишутся как шаблон, а потом нужные переменные подставляются (в памяти что-нибудь подправить, запускалку с закрытием ненужных окон сделать и т.п.). Но для этого уже давно инструменты есть и тоже вроде как смысл теряется.
Мне больше интересно, как сейчас дела обстоят (и обстоят ли) с программированием под FreeDOS или что-то подобное. Там ведь ассемблер вполне себе удобен (особенно для резидентных программ).
Мне больше интересно, как сейчас дела обстоят (и обстоят ли) с программированием под FreeDOS или что-то подобное. Там ведь ассемблер вполне себе удобен (особенно для резидентных программ).

Работать под (Free)DOS не имеет никакого смысла, потому что просто нельзя использовать возможности компьютера. А вот в Linux программировать на ассемблере очень просто. Почти как в DOS.

Есть какие-нибудь полезные ресурсы на эту тему (под Linux)?
Прошу прощение, а вы не могли бы выложить код вашего форума на github?
Спасибо за тему. Хотелось бы также увидеть материалы, описывающие красоту программирования на ассемблере и для чего он, собственно говоря, нужен. Например, как в статье «Программные трюки на ассемблере» из журнала «Микропроцессорные средства и системы». Про рекурсию, реентерабельность программ и про сопрограммы, по-моему, интересно хотя бы для общего развития.
Сравнение ассемблеров/архитектур — когда-то ассемблер DEC мне казался самым лучшим (там архитектура позволяла циклы чтение-модификация-запись при работе с памятью), потом было разочарование от ассемблера для Intel x86 (тут надо делать операции через регистр-аккумулятор AX/EAX и, если уж мы читаем слева-направо, то почему надо писать MOV DX,200, а не MOV 200,DX?). Позже, на Palm III познакомился с ассемблером Pila для Motorola 68000 — вот где красивая архитектура!
Еще интересно было бы узнать про самомодифицирующийся код (и про то, что его нельзя уже изменить, когда попал в конвейер команд/данных, что может быть использовано для определения, что ваша программа прогоняется под отладчиком.
Также можно было бы включить в будущие статьи, например, проект из блога компании Intel, в котором в приложение для Android вставлен и работает программа на Ассемблере. То есть, получается, мы можем писать на Ассемблере для Android! Ну, хотя бы чуть-чуть, для native библиотек.
Еще вопрос: есть книга П.Брамм, Д.Брамм «Микропроцессор 80386 и его программирование». В ней описаны, в частности, дескрипторы страниц и перевод процессора в защищенный режим. Не понятно (простите за невежество), если ОС переводит процессор в защищенный режим и участки памяти вообще не видны для приложений пользователя, то как тогда вирусы могут туда проникнуть? Возможно, меняя код в BIOS или на загрузочном диске.
Для ARM Ассемблера хотелось бы узнать, как включать/останавливать ядра.

+1 Моторолы в сердце навсегда ))) Недостаточно кармы, но таки "стрелка вверх"!

Как методичку прочитал.
Но если человек пишет на ассемблере, он уже не имеет готовых функций типа printf(), в которых за него продумано, как «общаться» с системой, и должен делать это сам.


Писал вот такой вот код на третьем курсе (MASM):

includelib D:\masm32\lib\kernel32.lib
ExitProcess PROTO, :DWORD
GetStdHandle Proto, :DWORD
Sleep PROTO, :DWORD
WriteConsoleA PROTO, handle: DWORD, lpBuffer:PTR BYTE, nNumberOfBytesToWrite:DWORD, lpNumberOfBytesWritten:PTR DWORD, lpReserved:DWORD
ReadConsoleA PROTO :DWORD,:DWORD,:DWORD,:DWORD,:DWORD
.data
STD_OUTPUT_HANDLE EQU -11
STD_INPUT_HANDLE EQU -10
coh dd ? ;store console output handle here
cih dd ? ;store console input handle here
.code

ReadingCA proc msg:DWORD, col:DWORD
sub esp,8
push STD_INPUT_HANDLE
call GetStdHandle
mov [ebp-4],eax ;Local Variable for this subroutine
push 0
lea eax,[ebp-8]
push eax
push [ebp+12]
push [ebp+8]
push [ebp-4]
call ReadConsoleA
ret 8
ReadingCA endp

Так что WinAPI использовать возможно.
«Как» писать — это ерунда. Как и во все предыдущие годы: запасшись спеками, кофеином/никотином и парой бесконечных жизней. А вот «когда» и «зачем» — те ещё вопросы.
Благодарю за труд. Мне очень помогла статья.
Спасибо, хороший материал. Добавил.
Дополнения принимаются? Не увидел в списке литературы П. И. Рудаков, К. Г. Финогенов «Язык ассемблера: уроки программирования» М.: ДИАЛОГ-МИФИ, 2001. — 640 с. — ISBN 5-86404-160-2. А книга отличная, благодаря ей я в свое время понял этот непростой язык
Поддерживаю, была настольной книгой долгое время.
Это простейший язык, никаких сложных конструкций же :) Для тех, кто начинал с советских программируемых микрокалькуляторов (у меня МК-52 был).

А книжка эта вроде была у меня, и еще Питера Нортона (который коммандер) с недокументированными возможностями MS-DOS, там тоже асм.
Sign up to leave a comment.

Articles