Основные понятия для изучения в сборке - PullRequest
7 голосов
/ 29 сентября 2008

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

То, что я хочу знать, какие концепции сборки, по вашему мнению, наиболее важны для любого программиста? Это не обязательно должно быть напрямую связано со сборкой - это также может быть то, что типичный программист, который все свое время проводит на языках более высокого уровня, не понимает или считает само собой разумеющимся, например, кэш процессора.

Ответы [ 9 ]

7 голосов
/ 01 октября 2008

Регистр распределения и управления

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

Это действительно помогло мне с моим C-кодированием. Я стараюсь сделать все петли туго и просто, используя как можно меньше спагетти.

х86 тупой

Изучение нескольких языков ассемблера заставило меня понять, насколько отсталый набор инструкций x86. Инструкции переменной длины? Трудно предсказать время? Неортогональные режимы адресации? Тьфу.

Мир был бы лучше, если бы мы все использовали MIPS, я думаю, или даже ARM или PowerPC :-) А точнее, если бы Intel / AMD воспользовались своим опытом в области полупроводников и использовали его для создания многоядерных, сверхбыстрых, ультра -дешевые процессоры MIPS вместо процессоров x86 со всеми этими качествами выкупа.

5 голосов
/ 05 ноября 2008

Я думаю, что язык ассемблера может научить вас многим мелочам, а также нескольким большим понятиям.

Я перечислю здесь несколько вещей, о которых могу подумать, но ничто не заменит обучения и использования как x86, так и набора команд RISC.

Вы, вероятно, думаете, что целочисленные операции выполняются быстрее всего. Если вы хотите найти целочисленный квадратный корень из целого числа (т. Е. Floor (sqrt (i))), лучше всего использовать процедуру аппроксимации только для целых чисел, верно?

Нах. Математический сопроцессор (то есть на x86) имеет инструкцию fsqrt . Преобразование во float, получение квадратного корня и повторное преобразование в int быстрее, чем алгоритм из целых чисел.

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

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

Как насчет соглашений о вызовах? (Некоторые ассемблеры позаботятся об этом за вас - настоящие программисты не используют их.) Очищает ли вызывающий или вызываемый объект стек? Вы вообще используете стек? Вы можете передавать значения в регистрах, но из-за забавного набора команд x86 лучше передавать определенные вещи в определенные регистры. И какие регистры будут сохранены? Компиляторы C не могут оптимизировать сами по себе - это вызовы.

Есть небольшие хитрости, такие как PUSHing адрес возврата и затем JMPing в процедуру; когда процедура вернется, она перейдет по адресу PUSHed. Этот отход от обычного мышления о вызовах функций является еще одним из тех «состояний просветления». Если вам когда-либо приходилось разрабатывать язык программирования с инновационными функциями, вы должны знать о забавных вещах, на которые способно аппаратное обеспечение.

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

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

Но все эти вещи нуждаются в правильном уме. Если вы тот человек, который может поставить

while(x--)
{
  ...
}

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

4 голосов
/ 29 сентября 2008

Хорошо знать язык ассемблера, чтобы лучше понять, как компьютер работает «под капотом», и это помогает, когда вы что-то отлаживаете, и все, что может дать отладчик, - это список кода сборки, который при по крайней мере, дает вам шанс выяснить, в чем может быть проблема. Однако попытка применить низкоуровневые знания к высокоуровневым языкам программирования, например, попытаться воспользоваться преимуществами того, как процессор кэширует инструкции, а затем написать непонятный высокоуровневый код, чтобы заставить компилятор создавать суперэффективный машинный код, вероятно, признак того, что вы пытаетесь микрооптимизировать. В большинстве случаев, как правило, лучше не пытаться перехитрить компилятор, если только вам не нужно выигрыш в производительности, и в этом случае вы все равно можете написать эти биты в сборке.

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

3 голосов
/ 29 сентября 2008

Самая важная концепция - SIMD, и творческое использование этого. Правильное использование SIMD может дать огромный выигрыш в производительности в широком спектре приложений - от обработки строк до манипуляций с видео и математической математики. Именно здесь вы можете получить более чем 10-кратное повышение производительности по сравнению с чистым кодом C - вот почему сборка по-прежнему полезна помимо простой отладки.

Некоторые примеры из проекта, над которым я работаю (все числа являются счетчиками тактов на Core 2):

Инверсия 8x8 H.264 DCT (преобразование частоты):

c: 1332
mmx: 187
sse2: 127

8x8 Компенсация движения цветности (билинейный интерполяционный фильтр):

c: 639
mmx: 144
sse2: 110
ssse3: 79

4 16x16 Сумма операций абсолютной разности (поиск движения):

c: 3948
mmx: 278
sse2: 231
ssse3: 215

(да, все верно - более чем в 18 раз быстрее, чем C!)

Средняя квадратичная ошибка блока 16x16:

c: 1013
mmx: 193
sse2: 131

Дисперсия блока 16x16:

c: 783
mmx: 171
sse2: 106
2 голосов
/ 29 сентября 2008

Память, регистры, переходы, циклы, сдвиги и различные операции, которые можно выполнять в ассемблере. Я не пропускаю дни отладки моих программ на языке ассемблера - они были болезненными! - но это, безусловно, дало мне хорошую основу.

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

Теперь мы, безусловно, можем сделать продуктивную и прибыльную карьеру, не зная ассемблера, но я думаю, что эти концепции полезно знать.

1 голос
/ 29 сентября 2008

Я бы сказал, что изучение рекурсии и циклов в сборке научило меня многому. Это заставило меня понять основную концепцию того, как компилятор / интерпретатор языка, который я использую, помещает вещи в стек и выводит их по мере необходимости. Я также узнал, как использовать печально известное переполнение стека. (что все еще удивительно легко в C с некоторыми командами get и put).

Кроме использования asm в повседневных ситуациях, я не думаю, что я бы использовал какие-либо концепции, которым меня научила сборка.

0 голосов
/ 29 сентября 2008

1001 * время * быстрое исполнение: параллельная обработка простые инструкции справочные таблицы прогноз ветвления, конвейерная обработка быстрый или медленный доступ к хранилищу: регистры кеш и различные уровни кеша куча памяти и стек виртуальная память внешний ввод / вывод

0 голосов
/ 29 сентября 2008

Я бы сказал, что режимы адресации чрезвычайно важны.

Моя alma mater довела это до крайности, и поскольку x86 не хватало их, мы изучили все на симуляторе PDP11, в котором должно было быть как минимум 7 из них, которые я помню. Оглядываясь назад, это был хороший выбор.

0 голосов
/ 29 сентября 2008

В настоящее время x86 asm - это не прямая связь с внутренними процессорами, а скорее API. Написанные вами коды операций на ассемблере сами по себе собраны в совершенно другой набор команд, переставлены, переписаны, исправлены и, как правило, искажены до неузнаваемости.

Так что не похоже на то, что изучение ассемблера дает вам фундаментальное понимание того, что происходит внутри процессора. ИМХО, важнее, чем изучение ассемблера, понять, как работает целевой процессор и иерархия памяти.

Эта серия статей довольно подробно охватывает последнюю тему.

...