Ты решил освоить ассемблер, но перед этим хочешь понять, что тебе это даст как программисту? Стоит ли входить в мир программирования через ассемблер, или лучше начать с какого‑нибудь языка высокого уровня? И вообще, нужно ли знать ассемблер, чтобы стать полноценным программистом? Давай разберемся во всем этом по порядку.
Это вводная статья цикла «Погружение в ассемблер», которую мы публикуем в честь его завершения. Ее полный текст доступен без подписки. Прочитав ее, ты можешь переходить к другим статьям этого курса:
Стоит освоить ассемблер, если ты хочешь:
Многие изъяны безопасности проявляются только на уровне машинного кода и могут быть устранены только с этого уровня.
Не стоит осваивать ассемблер, если ты хочешь:
Современные оптимизирующие компиляторы справляются с этой задачей очень хорошо. Ты вряд ли сможешь обогнать их.
Почему обогнать компилятор практически невозможно? Смотри, для тебя же очевидно, что компьютер в шахматы не обыграть, даже если ты играешь лучше, чем создатель шахматной программы? С оптимизирующими компиляторами та же история. Только оптимизирующий компилятор играет не шахматными фигурами, а контекстными обстоятельствами.
В современных процессорах практически ничто из того, что влияет на производительность, нельзя обсуждать в отрыве от контекста. Одна и та же комбинация из десятка ассемблерных инструкций выполняется с резкими отличиями по скорости (в тысячи или даже миллионы раз), в зависимости от целой кучи самых разнообразных обстоятельств.
И все это только верхушка айсберга, малая часть того, что тебе придется учитывать и анализировать, когда будешь стараться переиграть компилятор.
Есть такой миф, что программы, написанные на ассемблере, работают в десять раз быстрее. Этот миф уходит корнями в семидесятые годы. Компиляторы в те далекие времена генерировали код настолько бездарно, что у каждого уважающего себя программиста был черный список запрещенных языковых конструкций.
Когда наши коллеги из прошлого писали программы, они либо держали в уме этот черный список и не давали своим пальцам набивать проблемные конструкции, либо настраивали специальный препроцессор, который конвертировал исходник в более низкоуровневое беспроблемное представление на том же языке. С тех пор минуло 50 лет. Компиляторы возмужали, но миф остался.
Конечно, даже сегодня можно изредка встретить уникума, который пишет более быстрый код, чем компилятор. Вот только времени у него на это уходит так много, что ни в какие ворота не лезет. Плюс для оптимизации от тебя требуется, чтобы ты назубок знал весь набор инструкций процессора.
Вдобавок, поскольку ты шлифуешь свой код вручную, никакой компилятор не подстрахует тебя, не поможет отловить баги, которые ты неизбежно плодишь, когда пишешь программу.
Кроме того, твой ассемблерный код будет непереносимым. То есть, если ты захочешь, чтобы твоя программа запускалась на другом типе процессора, тебе придется полностью переписать ее, чтобы создать модификацию, заточенную под набор инструкций этого другого процессора. Само собой, тебе эти инструкции тоже надо знать назубок.
В итоге ты потратишь в десятки и сотни раз больше времени, чем если бы доверился оптимизирующему компилятору, — но результат, скорее всего, окажется медленнее, а не быстрее.
При этом иногда оптимизирующий компилятор выплевывает ассемблерный код, логика которого ну совсем непонятна. Однако не спеши обвинять компилятор в глупости. Давай разберем пример.
Когда ты пишешь на С что‑то вроде x = a*2 + b*3
, то естественным образом ожидаешь увидеть в ассемблере инструкцию, которая умножает переменную a
на двойку. Но компилятор знает, что сложение дешевле умножения. Поэтому он не умножает a
на двойку, а складывает его с самим собой.
Больше того, глядя на b
, компилятор может счесть, что b + b + b
предпочтительнее, чем b*3
. Иногда тройное сложение быстрее умножения, иногда нет. А иногда компилятор приходит к выводу, что вместо исходного выражения быстрее будет вычислить (a + b)*2 + b
. Или даже ((a + b)<<1) + b
.
А если x
используется лишь однократно — причем в связке с парой строк последующего кода, — компилятор может вообще не вычислять x
, а просто вставить a*2 + b*3
вместо икса. Но даже если x
используется и компилятор видит что‑то вроде y = x – b*3
, он может исправить эти расчеты на y = a+a
, удивляясь твоей расточительности. Расточительности в плане вычислительной сложности.
Размышления подобного рода неизбежно заводят тебя в запутанный лабиринт альтернативных вариантов. Все их нужно просчитать, чтобы выбрать лучший. Но даже когда ты сделаешь это, вариант ассемблерного кода, сгенерированный компилятором, скорее всего, будет работать быстрее, чем твой.
Кстати, если используешь GCC или Clang, активируй опции оптимизации для SSE, AVX и всего остального, чем богат твой процессор. Затем откинься на спинку кресла и удивись, когда компилятор векторизует твой сишный код. Причем сделает это так, как тебе и не снилось.
Нет таких. Все, что можно сделать на компьютере, можно сделать в том числе и на ассемблере. Ассемблер — это текстовое представление сырого машинного кода, в который переводятся все программы, запущенные на компьютере.
Ты при желании можешь написать на ассемблере даже веб‑сайт. В девяностые С был вполне разумным выбором для этой цели. Используя такую вещь, как CGI BIN, веб‑сервер мог вызывать программу, написанную на С. Через stdin
сайт получал запрос, а через stdout
отправлял результат в браузер. Ты можешь легко реализовать тот же принцип на ассемблере.
Но зачем? Ты должен быть мазохистом, чтобы проделывать такое. Потому что когда ты пишешь на ассемблере, то сталкиваешься вот с такими проблемами.
Да, все можно написать на ассемблере. Но сегодня это нецелесообразно. Лучше пиши на С. Скорее всего, будет безопаснее, быстрее и более лаконично.
Автор статьи — большой поклонник С и настоятельно рекомендует этот язык. Мы не будем лишать его такой возможности. С — отличная штука и помогает как освоить основные концепции программирования, так и прочувствовать принципы работы компьютера. Однако при выборе языка для изучения ты можешь руководствоваться самыми разными соображениями. Например:
И так далее. Ответ на вопрос о том, с какого языка начать, зависит от многих факторов, и выбор — дело индивидуальное.
Конечно, когда ты знаешь ассемблер, у тебя будут значительные преимущества перед теми программистами, которые его не знают. Но прежде чем ознакомиться с этими преимуществами, запомни одну простую вещь: хорошие программисты знают ассемблер, но почти никогда не пишут на нем.
Чтобы писать эффективные программы (в плане быстродействия и экономии ресурсов), тебе обязательно надо знать ассемблер того железа, для которого ты пишешь. Когда ты знаешь ассемблер, ты не обманываешься внешней простотой и краткостью высокоуровневых функций, а понимаешь, во что в итоге превращается каждая из них: в пару‑тройку ассемблерных инструкций или в длиннющую их последовательность, переплетенную циклами.
Если работаешь с языками высокого уровня, такими как С, научись хотя бы читать и понимать ассемблерный код. Даже если ты в обозримом будущем не видишь себя пишущим на ассемблере (на самом деле мало кто себя так видит), знание ассемблера тебе пригодится.
Если будешь с ассемблером на ты, он сослужит тебе хорошую службу в отладке. Освоив ассемблер, ты будешь понимать, что происходит под капотом языков высокого уровня, как компьютер делает то, что он делает, и почему высокоуровневый компилятор иногда работает не так, как ты ждешь от него. Ты сможешь видеть причину этого и понимать, как ее устранить.
Плюс иногда ты ну никак не можешь понять, что у тебя за баг, пока не пройдешься в отладчике в пошаговом режиме по ассемблерному коду.
И вот еще тонкий намек: некоторые работодатели хотели бы видеть в твоем резюме слово «ассемблер». Это говорит им, что ты не просто по верхам нахватался, а действительно интересуешься программированием, копаешь вглубь.
Когда ты осваиваешь программирование, начиная с самых низов, в этом есть свои плюсы. Но ассемблер — это не самый низ. Если хочешь начать снизу, начни с логических вентилей и цифровой электроники. Затем поковыряйся с машинным кодом. И только потом приступай к ассемблеру.
Время от времени тебя будут посещать мысли, что ты занимаешься какой‑то ерундой. Но ты узнаешь много полезного для своей будущей работы, даже если она будет связана только с языками высокого уровня. Ты узнаешь, как именно компьютер делает те вещи, которые он делает.
Однако я бы не советовал начинать с ассемблера и более низких слоев. Во всем том, что перечислено в двух предыдущих абзацах, легче разобраться, когда ты начинаешь с какого‑нибудь языка высокого уровня. Так ты достигнешь желаемого результата быстрее, чем оно тебе наскучит.
Но в какой‑то момент тебе и правда обязательно надо познакомиться с ассемблером, особенно если программируешь на С. Я сомневаюсь, что ты сможешь стать полноценным программистом на С, не зная ассемблера. Но начинать с ассемблера не стоит.
Ассемблер совершенно не похож на языки высокого уровня. Поэтому народная мудрость «Тот опыт, который ты получил на одном языке, может быть легко сконвертирован на другой язык» с ассемблером не работает.
Если ты начнешь с ассемблера, то после того, как выучишь его и решишь освоить новый язык, тебе придется начинать как с чистого листа. Помню, мой однокашник еще в школе выучил ассемблер, написал на нем игрушку, с которой победил на конференции. Но при этом так и не смог хорошо освоиться в С, когда мы учились в универе.
Чем же ассемблер отличается от языков высокого уровня? Переменные в нем — это просто области памяти. Здесь нет ни int
, ни char
. Здесь нет массивов!
Есть только память. Причем ты работаешь с ней не так, как на языке высокого уровня. Ты можешь забыть, что в какую‑то область памяти поместил строку, и обратиться к ней как к числу. Программа все равно скомпилируется. Но только обрушится в рантайме. Причем обрушится жестко, без вежливого сообщения об ошибке.
В ассемблере нет do..until
, нет for..next
, нет if..then
. Вместо них там есть только операции сравнения и условного перехода. Строго говоря, там даже функций нет.
Но! Изучив ассемблер, ты будешь понимать, как реализуются и функции, и циклы, и все остальное. А разница между передачей параметра «по значению» и «по ссылке» станет для тебя самоочевидной. Плюс если ты пишешь на С, но не можешь до конца разобраться, как работают указатели, то, когда ты узнаешь, что такое регистры и относительная адресация, увидишь, что понять указатели совсем нетрудно.
Лучше начинай с С. На нем удобно осваивать основы: переменные, условия, циклы, логические построения и остальное. Опыт, который ты получишь при изучении С, легко сконвертировать на любой другой язык высокого уровня, будь то Java, Python или какой‑то еще. Да и с ассемблером легче разобраться, когда ты уже освоил С.
Если заглянешь на HH.ru, то, скорее всего, не найдешь ни одной вакансии, у которой в заголовке написано слово «ассемблер». Но время от времени какая‑нибудь контора лихорадочно ищет мага‑волшебника, который знает нутро компьютера настолько глубоко, что может полностью подчинить операционную систему своей воле. Мага‑волшебника, который умеет (1) латать систему, не имея на руках исходного кода, (2) перехватывать потоки данных на лету и вмешиваться в них.
Некоторая часть этой глубокой магии — а сейчас потребность в такой магии становится все более редкой — может быть воплощена только на языке очень низкого уровня.
Я слышал о конторе, которая ищет человека на разработку новой платформы для высокочастотного трейдинга. Там идея в том, что если ты получаешь информацию о котировках быстрее своих конкурентов и принимаешь решение быстрее них, то будешь грести баснословные суммы.
«Когда ты получаешь котировки, проходя через весь стек TCP/IP, это слишком медленно», — говорят парни из этой фирмы. Поэтому у них есть примочка, которая перехватывает трафик на уровне Ethernet, прямо внутри сетевой карты, куда залита кастомизированная прошивка.
Но эти ребята пошли еще дальше. Они собираются разработать девайс для фильтрации трафика Ethernet — на ПЛИС. Зачем? Чтобы ловить котировки на аппаратном уровне и тем самым экономить драгоценные микросекунды трейдингового времени и в итоге получать небольшое, очень небольшое, преимущество перед конкурентами. Язык С им не подошел. Им даже ассемблер не подошел. Так что эти парни выцарапывают программу прямо на кремнии!
Читайте также
Последние новости