Оптимизация программ на ассемблере


Оптимизация приложений при помощи ассемблера

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

Часть программистов считает, что улучшить производительность работы приложения без использования средств отладки самого компилятора нельзя, тем более что все современные компиляторы имеют встроенные(built-in) средства оптимизации программного кода. Можно полностью положиться на компилятор, который сгенерирует для вас оптимальный код, и вообще не заниматься улучшением качества программы. При этом в целом ряде случаев может и не понадобиться никаких доработок и улучшений. Например, при создании небольших офисных приложений или утилит тестирования сети оптимизация обычно не нужна.

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

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

Улучшение производительности программ — процесс обычно трудоемкий, занимающий значительную часть времени. Хочется отметить, что не существует единого критерия оптимизации. Более того, сам процесс оптимизации довольно противоречив. Например, если добиться уменьшения объема памяти, используемого программой, то за это придется расплатиться потерей быстродействия работы программы.

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

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

Для многих офисных приложений очень важным показателем является удобство интерфейса пользователя и как можно более высокая функциональность. Например, для пользователя программы электронного телефонного справочника тот факт, что программа работает на 10% быстрее или медленнее, особого значения не имеет. Размер такой программы, в принципе, не критичен и также не имеет особого значения, т. к. объем современных жестких дисков достаточно большой, чтобы поместить десятки и даже сотни таких электронных справочников. Программе может быть необходимо десятки мегабайт оперативной памяти для работы — это тоже сейчас не проблема. Но вот возможность удобной манипуляции данными для пользователя будет очень важной.

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

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

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

Простейший способ заставить приложения работать быстрее — это повысить вычислительную мощь компьютера за счет установки более производительного процессора или увеличения объема памяти, т. е. сделать апгрейд (upgrade) аппаратной части. В этом случае проблема производительности будет решена сама собой.

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

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

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

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

Оптимизация может проводиться по следующим направлениям:

тщательная проработка алгоритма разрабатываемой программы;

учет существующих аппаратных средств компьютера и использование их оптимальным образом;

использование средств языка высокого уровня той среды, в которой разрабатывается приложение;

использование языка низкого уровня ассемблера;

учет специфических особенностей процессора.

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

Оптимизация программ на ассемблере

Название: Использование ассемблера для оптимизации программ на C++ (+CD)
Автор: Магда Ю.С.
Издательство: БХВ-Петербург
Год: 2004
Страниц: 496
ISBN: 5-94157-414-2
Формат: DJVU
Размер: 155 Мб
Язык: русский
Серия: Профессиональное программирование

Рассматривается использование языка Ассемблер для оптимизация программ, написанных на языке C++. Подробно изложены вопросы применения современных технологий обработки данных MMX и SSE, а также использования особенностей архитектур современных процессоров для оптимизации программ. Рассмотрена оптимизация логических структур высокого уровня, использование эффективных алгоритмов вычислений, работа со строками и массивами данных.
В книгу включены примеры программного кода приложений, иллюстрирующие различные аспекты применения ассемблера. В качестве средств разработки примеров используются макроассемблер MASM 6.14 и Microsoft Visual C++ .NET 2003.
Исходные тексты программ содержатся на прилагаемом к книге компакт-диске.

Предисловие
Введение
Глава 1. Оптимизация ассемблерного кода для процессоров Pentium
Глава 2. Оптимизация вычислительных алгоритмов с помощью ассемблера
Глава 3. Разработка и использование подпрограмм на ассемблере
Глава 4. Оптимизация логических структур C++ с помощью ассемблера
Глава 5. Интерфейс модулей на ассемблере с программами на C++
Глава 6. Особенности разработки и применения подпрограмм на ассемблере
Глава 7. Компоновка ассемблерных модулей с программами на C++ .NET
Глава 8. Разработка библиотек динамической компоновки (DLL) на ассемблере
Глава 9. Базовые структуры встроенного ассемблера Visual C++ .NET 2003
Глава 10. Встроенный ассемблер и оптимизация приложений. Технологии MMX и SSE
Глава 11. Оптимизация мультимедийных приложений с помощью ассемблера
Глава 12. Оптимизация многопоточных приложений с помощью ассемблера
Глава 13. Встроенный ассемблер C++ .NET и функции времени Windows
Глава 14. Ассемблер в задачах системного программирования Windows
Глава 15. Оптимизация процедурно-ориентированных приложений и системных служб
Заключение
Приложение 1. Инструкции процессоров 80×86
Приложение 2. Описание CD
Список литературы
Предметный указатель

Способы увеличения быстродействия программ

Современные ЭВМ обладают очень большой мощностью. Скорость работы процессора (ЦП) современных ЭВМ измеряется гигагерцами, объём оперативной памяти гигабайтами, а современные интерфейсы устройств обеспечивают скорость обмена данными порядка, как минимум, нескольких сотен мегабайт в секунду [1]. Производительность, которая ещё несколько лет назад казалась «сказочной» в настоящее время стала нормой жизни.

Однако параллельно росту мощности ЭВМ увеличивается и ресурсоёмкость приложений. У приложений совершенствуется функционал, интерфейс, возрастает объём обрабатываемых данных и как следствие системные требования. Поэтому вопрос об увеличении быстродействия приложений не теряет своей актуальности.

Общие вопросы быстродействия программ.

Быстродействие программ (ПО) зависит от многих факторов, но основными из них являются два:

  • Соотношение между реальными системными требованиями ПО и существующей аппаратной конфигурацией ЭВМ;
  • Алгоритмы работы ПО.

Если низкое быстродействие обусловлено первым фактором, то решением является модернизация аппаратной части (hardware). В некоторых случаях проблему можно решить также с помощью тонкой настройки hardware и операционной системы. Однако этот путь имеет ряд недостатков:

  1. Увеличивается производительность hardware, а вовсе не быстродействие ПО;
  2. Производительность hardware ограничена возможностями существующих в данный момент элементной базы и инженерных решений в данной области;
  3. Большие финансовые затраты на модернизацию и настройку по причине высокой стоимости комплектующих ЭВМ и услуг специалистов требуемой квалификации.

По этим причинам при разработке ПО прибегают к увеличению его быстродействия с помощью различных средств программной инженерии. Это позволяет:

  1. Обеспечить работу нового ПО на уже существующем оборудовании;
  2. Разработать масштабируемое ПО;
  3. Значительно уменьшить финансовые и трудовые затраты при внедрении.

Вместе с тем и у этого пути имеется ряд недостатков:

  1. Значительно усложняется процесс разработки ПО, так как более «быстрые» алгоритмы сложнее более «медленных» (на пример алгоритм бинарного поиска сложнее, чем алгоритм линейного поиска) [2];
  2. Реализация более сложных алгоритмов, как правило, требует привлечения специалистов более высокой квалификации;
  3. В случае работы с большими объёмами данных или выполнении задач требующих больших и сложных вычислений, ресурсоёмкость ПО всё равно остаются достаточно высокой. Несмотря, на какие либо способы увеличения быстродействия.

Таким образом, в общем случае обеспечение быстродействия ПО является комплексной задачей.

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

Программная инженерия предоставляет несколько способов увеличения быстродействия программ. Рассмотрим их на примере языков программирования Delphi и Assembler.

Увеличение быстродействия программ.

Как было показано в предыдущем параграфе, можно увеличить быстродействие ПО соответствующим образом реализовав его алгоритмы. Количественным показателем быстродействия алгоритма (а, следовательно, и ПО) является время его выполнения, измеренное по специальной методике, так называемого профилирования [2]. Таким образом, в общем случае выбор наиболее «быстрых» алгоритмов сводится к измерению времени их выполнения и сравнении полученных результатов между собой. Такой способ анализа быстродействия является наиболее объективным [2]. На протяжении многих лет программистами был накоплен большой опыт профилирования, который позволяет сделать определённые выводы относительно возможности оптимизации быстродействия ПО ещё на стадии написания.

Эти выводы были обобщены и представлены в виде определённых рекомендаций [2, 4]. Если программист будет следовать данным рекомендациям, то написанная программа вероятнее всего будет обладать большим быстродействием, чем в случае их игнорирования. Однако следует ещё раз подчеркнуть, что достоверные сведения о быстродействии может дать только профилирование. Это обусловлено тем, что быстродействие алгоритма определяет в первую очередь его конкретная реализация. Кроме того необходимо ещё раз отметить, что в отношении увеличения быстродействия ПО программная инженерия не всесильна (см. предыдущий параграф).

В чём же состоят выше упомянутые рекомендации? Их краткое содержание применительно к языку программирования Delphi приведено ниже.

  1. При написании кода программ рекомендуется избегать процедур, состоящих из сотен строк. Практически всегда в них можно выделить блоки, которые лучше оформить в виде отдельной процедуры. Возможно, позже вы ей даже воспользуетесь где-то в другом месте. Не говоря уже о том, что это повышает понимание программы и вами, и другими программистами. К тому же так проще искать «узкие» места в программе.
  2. Использование оператора case (switch) вместо многократных if… then… else (if… else). Во втором варианте компилятор будет выполнять проверку условия столько раз, сколько у вас вариантов. В первом проверка выполняется лишь однажды.
  3. Некоторые действия могут быть довольно продолжительными, поэтому рекомендуется выносить за рамки цикла всё, что можно выполнить вне его, чтобы избежать большого числа повторений внутри цикла.
  4. В циклах типа for нужно стараться, чтобы значение счетчика уменьшалось до нуля, а не наоборот — начиналось с нуля. Это связано с особенностями процессора. Сравнение с нулём выполняется гораздо быстрее, чем с другим числом.
  5. Пользоваться типом Variant только при необходимости. Операции над этим типом сложнее, чем, например, над Integer или String.
  6. Не злоупотреблять «программированием на компонентах». В частности не использовать компонент TTreeView для хранения древовидных структур данных — он работает очень медленно и предназначен только для визуального отображения. В случае работы со структурами данных лучше использовать алгоритмы, созданные самостоятельно на основе фундаментальных.
  7. Сохранение и загрузка свойств компонентов с помощью методов ReadComponent и WriteComponent работает довольно медленно, поэтому по возможности рекомендуется сохранять и восстанавливать состояние программы между сеансами при помощи других способов.
  8. Заменить простой в реализации алгоритм на более сложный, но с большим быстродействием. Например, если заранее известно, что в списке для поиска будет много элементов, лучше его отсортировать и применять бинарный поиск вместо линейного.
  9. В критических с точки зрения быстродействия местах программы делать вставки на ассемблере. Команды ассемблера напрямую транслируются в машинный код. Таким образом, в отличие от высокоуровневых языков при компиляции отсутствует проблема синхронизации и ряд других негативных обстоятельств.

Для других языков программирования вышеприведённый список может несколько отличаться, в частности отсутствием поддержки ассемблера и как следствие возможности оптимизации с его помощью (Java, Visual C# и др.).

Особо следует отметить, что рекомендации 3 и 4 применяются не только для языков высокого уровня, но и для ассемблера. Помимо вышеуказанных для увеличения быстродействия программ написанных на ассемблере, в том числе и вставок, существуют следующие рекомендации [3]:

  1. Замещение универсальных инструкций на учитывающие конкретную ситуацию, например, замена умножения на степень двойки на команды сдвига (отказ от универсальности).
  2. Уменьшение количества передач управления в программе: за счет преобразования подпрограмм в макрокоманды для непосредственного включения в машинный код; за счет преобразования условных переходов так, чтобы условие перехода оказывалось истинным значительно реже, чем условие для его отсутствия; перемещение условий общего характера к началу разветвленной последовательности переходов; преобразование вызовов, сразу за которыми следует возврат в программу, в переходы («сращивание хвостов» и «устранение рекурсивных хвостов») и т.д.
  3. Максимальное использование всех доступных регистров за счет хранения в них рабочих значений всякий раз, когда это возможно, чтобы минимизировать число обращений к памяти, упаковка множественных значений или флагов в регистры и устранение излишних продвижений стека (особенно на входах и выходах подпрограмм).
  4. Использование специфических для данного процессора инструкций, например, инструкции засылки в стек непосредственного значения, которая имеется в процессоре 80286 и более поздних. Другие примеры – двухсловные строковые инструкции, команды перемножения 32-разрядных чисел, деление 64-разрядного на 32-разрядное число и умножение на непосредственное значение, которые реализованы в процессорах 80386 и 80486. Программа должна, разумеется, вначале определить, с каким типом процессора она работает!
Илон Маск рекомендует:  Что такое код ldap_next_entry

Заключение.

Методы оптимизации быстродействия, рассмотренные в этой статье, были выработаны и проверены не одним поколением программистов и уже стали классическими. В то же время информационные технологии, в частности технологии программирования, постоянно развиваются. Появляются новые технологии, старые – модернизируются или уходят в прошлое. Растёт производительность аппаратной части ЭВМ и параллельно с этим растёт сложность и ресурсоёмкость выполняемых ими задач.

Поэтому вопрос оптимизации быстродействия сейчас актуален также как и много лет назад. Более того он будет также актуален и в будущем.

Список литературы и источников.

  1. Быстро и легко. Сборка, диагностика, оптимизация и апгрейд современного компьютера/ под ред. Печникова В.Н. Учебное пособие – М.: Лучшие книги, 2005;
  2. Бакнелл Д. Фундаментальные алгоритмы и структуры данных в Delphi. М.: ООО «ДиаСофтЮП»; СПб.: Питер, 2006;
  3. Дункан Р. Оптимизация программ на ассемблере./ Дункан Р.//PC Magazine/Russian Edition №1/1992;
  4. Список пропускных способностей интерфейсов передачи данных;
  5. Гапанович А. Как сделать свою программу быстрой./Компьютерра Online.

Использование языка Ассемблера для оптимизации программ

В случае использования мощного ПК вы наверняка обнаружили, что после уменьшения числа спрайтов до 200 темп вывода кадров (FPS) достигает величины кадровой развертки (70 Гц).

Если этот результат получен для программы, которую не предполагается запускать на слабых машинах, то ее оптимизацию следует считать законченной и можно сформулировать такой постулат: «При отображении картинки на экран не следует стремиться к частоте вывода кадров, большей частоты кадровой развертки». Но с одной стороны, игра — это не только отображение спрайтов на экран. При ее создании необходимо предусмотреть использование части вычислительной мощи процессора также на физическую модель и логику взаимодействия объектов. С другой стороны, разрешение 320×200 точек было характерно для ПК моделей 286 и 386 с тактовой частотой 8—40 МГц, но сейчас более адекватным следует считать разрешение 800×600 или 1024×768 точек. Однако с таким разрешением мы работать не будем, так как использовать в этом случае Борланд Паскаль довольно неудобно из-за сегментированной модели памяти реального режима. При высоком экранном разрешении целесообразно выбрать защищенный режим, к чему мы еще вернемся в последних статьях цикла, а пока продолжим работу с разрешением 320×200 точек.

Прежде всего, следует сказать, что язык Ассемблера не очень подходит для написания крупных программ. При использовании языка высокого уровня ПО получается компактнее, понятнее и содержит меньше ошибок, поэтому целесообразно переписать на языке Ассемблера лишь некоторые критичные по времени исполнения фрагменты. Какова их доля? Предполагается, что она колеблется от 1—5 до 10—15% от общей длины текста. При выборе этих фрагментов следует придерживаться второго правила, гласящего: «В оптимизации нуждается только тот фрагмент программного текста, который занимает значительную долю времени выполнения». Как найти такие фрагменты? Это можно сделать несколькими способами: с помощью профайлера, путем проведения анализа вложенности циклов, последовательно отключая модули и наблюдая, как это скажется на времени выполнения программы. Работу с профайлером мы рассматривать не будем, а воспользуемся двумя другими методами, тем более что они прекрасно дополняют друг друга. С одной стороны, оценка на основе анализа текста может дать лишь рекомендации, которые нужно проверить, а с другой, далеко не все модули можно отключить без потери работоспособности программы.

Итак, начнем с анализа. Оценим, какие части программы выполняются чаще всего, — возможно, ими окажутся циклы с наибольшей вложенностью. Например, в нашей программе есть основной игровой цикл, выполняемый при отрисовке каждого кадра. Значит, все, что лежит за его пределами, можно не рассматривать. А внутри самого цикла вложений не так уж и много: вывод в буфер фона спрайтов и текста, а также переброска данных из буфера на экран и их синхронизация. В первую очередь наиболее глубокий цикл существует при выводе спрайта: внутри основного игрового выполняется цикл по спрайтам, а внутри него — вложенный цикл вывода точек по горизонтали и вертикали. Если иметь 200 спрайтов размером 20×20 точек, то можно получить 80 тыс. повторений на каждый кадр основного игрового цикла. Во вторую очередь идет вывод текста: отображение строки происходит посимвольно, а для показа символа опять же используется вложенный цикл. Таким образом, вложенность здесь такая же, как и при выводе спрайтов, только сами циклы короче, поэтому при 10—30 символах размером 8×8 точек цикл повторяется 700—2000 раз. Казалось бы, не так уж много, но современные суперскалярные процессоры, способные выполнять несколько команд за один такт, очень плохо переносят вызовы процедур, поэтому следует запомнить еще одно правило: «Не нужно размещать вызов процедуры или функции во вложенном цикле, а лучше перенести внутрь цикла фрагмент программного текста из процедуры». Иногда это может дать выигрыш в несколько раз. Мы не будем сейчас следовать этому принципу, а лучше минимизируем количество выводимого в кадре текста — тогда о его оптимизации можно не заботиться.

Теперь все рассмотрено, и список циклов глубокой вложенности исчерпан. Однако при переброске фона в буфер или изображения на экран приходится последовательно копировать 64 000 точек. Это также цикл, хотя он и скрыт внутри процедуры move. Значит, анализ привел нас к необходимости минимизировать количество выводимого текста, для чего мы уберем с экрана слова «Демонстрационная программа» и оптимизируем процедуры BackBufferToScreen, PutSprite, ScreenBufferToScreen.

Переходим к следующему этапу: отключим «ненужные» процедуры. В основном игровом цикле оставим только вызов процедур PutText и ScreenBufferToScreen (иначе мы не увидим на экране величины FPS), а все остальное временно «закомментируем». Запустим урезанную программу и запомним для дальнейшего сравнения величину FPS.

Сначала перепишем Screen BufferToScreen на языке Ассемблера (листинг 1). Здесь воспользуемся командой rep movsX (где X может быть b, w или d — при этом соответственно команда за один раз копирует по одному, два или четыре байта). Она служит для копирования одной области памяти в другую. Процедура move использует команду rep movsb. Копировать по 4 байта за один раз могут только процессоры, начиная с 386-го, о которых Борланд Паскаль ничего не знает. Команда rep movsd на встроенном Ассемблере Борланд Паскаля будет выглядеть так: db $66 rep movsw. Еще одна тонкость, связанная с использованием языка Ассемблера в Борланд Паскале: все статические переменные хранятся в сегменте данных, адресуемом через регистр ds. Поэтому, если необходимо переопределить последний, то его содержимое следует запомнить, а затем восстановить. Кроме того, изменение ds следует провести как можно позже, ведь после нее обращение к статическим переменным программы будет уже невозможным.

Затем восстановим переброску фона, вновь запустим программу и запомним FPS. Аналогично перепишем BackBufferToScreen и снова измерим FPS. Если в вашем ПК установлен современный процессор (Pentium II, Celeron и выше), то вас ждет разочарование, — практически ничего не изменится. Это связано с влиянием кэш-памяти, в которую целиком помещаются оба буфера. Как только мы подключим вывод спрайтов, кэш-память переполнится, и наше усовершенствование не будет незамеченным.

Далее приступим к оптимизации PutSprite. Измерим FPS до и после изменений, показанных в листинге 1. Приведенный вариант, конечно, не идеален. Например, при наличии процессора Celeron, Pentium II или Pentium III замена loop @l2 на последовательность команд

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

Если в ПК установлен быстрый процессор (обеспечивающий FPS > 140), спрайты через некоторое время могут остановиться. Это связано с тем, что вычисленная величина приращения координат стала меньше 1/2 и была округлена до 0. Восстановите кадровую синхронизацию — оптимизация закончена!

Есть способ добиться правильного движения спрайтов и при высоких FPS: перейти от целых координат к дробным. Этот же подход позволит обеспечить и различную скорость движения разных спрайтов. Кстати, «дробные» не обязательно означают «с плавающей точкой». Можно воспользоваться и представлением чисел с фиксированной точкой. Таких чисел стандартного типа нет, но их легко получить из целых. Например, мы знаем, что диапазон изменения координат не превышает 0—320 и для его показа достаточно 9-разрядных чисел (или 10-разрядных, если число со знаком). Давайте считать, что единица соответствует 1/64 размера пиксела, тогда старшие 10 разрядов будут представлять целую часть координаты на экране, а младшие 6 — дробную. Чтобы перейти от целых чисел к числам с фиксированной точкой, достаточно при определении координат и приращений умножить числа на 64, а при использовании — разделить их на 64. Изменения в модуле Sprites и в основной программе даны в листинге 2. Вместо деления или умножения на число, являющееся степенью два, можно применить сдвиг, что будет более быстрой операцией.

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

ЛАБОРАТОРНАЯ РАБОТА №10 Оптимизация исполняемого кода с помощью ассемблера

Цель: изучение возможностей оптимизации исполняемого кода с помощью ассемблера

1. Написать на Turbo Pascal 7.0 программу с использованием циклов и массивов вещественного типа. Пример:
var x: single;
a,b: array[1..5] of single;
begin
x := 0;
for i := 1 to 5 do
x := x + a[i] * b[i];
end.

2. Откомпилировать эту программу с ключом N+ — для использования сопроцессора.

3. Нажать Shift+F4 для загрузки автономного отладчика.

4. Просмотреть откомпилированный код в окне CPU отладчика, рекомендуется в режиме “Mixed Yes”, чтобы видеть исходный код на Паскале и соответствующий ему исполняемый код в виде мнемоник аасемблера.

5. Изучив откомпилированный код и записав его, предложить свой вариант более эффективного решения задачи с использованием встроенного ассемблера. В программу включить дополнительно одну команду, по согласованию с преподавателем.

6. Написать, откомпилировать и просмотреть с помощью автономного отладчика откомпилированный код новой программы.

7. Используя выпадающее меню (появляется при нажатии правой кнопки мыши в соответствующем окне) ввести в окно Watch несколько переменных.

8. Открыть окна CPU и Numeric processor.

9. Провести пошаговое выполнение этой программы и оценить ее эффективность по сравнению с исходной. В ходе выполнения обращать внимание на содержимое регистров процессора и детально рассмотреть изменения в регистрах сопроцессора.

10.Детальное описание работы одной из команд ассемблера для сопроцессора включить в отчет.

1. Отладчики: определение, назначение, классификация, примеры.

2. Отладчик TD: определение, назначение, использование.

3. Сходство и различие режимов Step Over и Trace Into и особенности их реализации в окнах Module и CPU.

4. Окна Watch, CPU, Numeric processor, Dump, Register. Назначение, использование, особенности меню.

5. Особенности изменения значений данных (переменных, регистров и флагов) в окнах CPU, Numeric processor, Watch и других

7. Возможности оптимизации кода программы с помощью встроенного ассемблера.

ЛАБОРАТОРНАЯ РАБОТА №11 Тестирование и экспертная оценка ПС

Цель: изучить методику тестирования, основные особенности этой методики, изучить тестируемое программное средство и написать краткую инструкцию по работе с ним

1. Получить задание у преподавателя на тестирование ПС. Записать название ПС. В данной работе запись обязательна для всех пунктов работы без дополнительных напоминаний, а объем отчета может значительно влиять на оценку. Пункт без информации в отчете считается невыполненным.

2. Изучить назначение, особенности установки, работы ПС, входные и выходные данные. Записать все доступные сведения о разработчиках (фамилии, адреса, организации), времени создания (дата, версия), используемых языках и системах программирования (язык; платформа, название, версия, год выпуска, фирма-разработчик системы программирования), прочая информация.

3. Описать данные системы:

3.1. количество файлов, название и назначение каждого файла,

3.2. структура важнейших файлов,

3.3. описание формата входных данных,

3.4. описание формата выходных данных.

4. Оценить следующие параметры:

4.1. характер интерфейса пользователя,

4.2. быстродействие (указать основные параметры машины),

4.3. совместимость (по данным),

4.4. возможность коллективного использования данных,

4.5. документация (наличие, качество),

4.6. простота установки, освоения, использования,

4.7. надежность, обработка ошибок, защита и восстановление данных,

4.8. политика поддержки,

4.9. качество поддержки,

4.10. адаптивность (указать операционную систему и среду),

4.11. степень соответствия заявленному,

4.13. максимальные размеры данных.

5. Перечислить основные и дополнительные функции системы, указать степень их реализации, удобство использования каждой функции .

6. Написать краткую методику использования данного ПС.

7. Отчет — обязательно в текстовом файле, не менее 3-х страниц содержательного текста (по 60 строк страница). На двоих — не менее 6 страниц.

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

ЛАБОРАТОРНАЯ РАБОТА №12 Программирование суперскалярных операций

Цель: изучить основы разработки программ для суперскалярных процессоров

1. Выбрать задание по варианту.

2. Разработать алгоритм программы.

3. Используя систему программирования Delphi 7 версии написать программу для реализации вычислений, с использованием регистров и команд XMM.

4. Тестирование программы выполнять на процессорах Pentium 3 и старше.

Вариант 0 A * B + C + D
Вариант 1 A * (B + C) + D
Вариант 2 A * (B + C + D)
Вариант 3 A * B * C + D
Вариант 4 A * (B * C + D)
Вариант 5 A * B * (C + D)
Вариант 6 A * B + C * D
Вариант 7 A * (B + C) * D
Вариант 8 (A + B) * (C + D)
Вариант 9 A * B * C * D

1. Параллельные системы: определение, классификация, примеры.

2. Конвейерные системы: определение, назначение, использование.

3. Кластерные системы: определение, назначение, использование.

Заключение

Методические указания к выполнению лабораторных работ ориентированы на небольшой по объему практический курс, поэтому ориентирован на приобретение навыков работы с наиболее популярными пользовательскими средами. Мы ставили целью рассмотрение таких умений и приемов работы, как научиться определять конфигурацию и компоненты вычислительной системы и их основные параметры; изучить конструктивные особенности системного блока, особенностей материнской платы и особенности сборки-разборки компьютера; изучить параметры и особенности работы периферийного устройства; изучить основные особенности программирования на ассемблере и сравнить с программированием на Паскале; изучить основные особенности программирования на ассемблере процедур. Многие вопросы данного курса предназначены для самостоятельного изучения, а некоторые — для реферативного исследования.

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

42. Оптимизация по быстродействию в Ассемблер

42. Оптимизация по быстродействию в Ассемблер

Приведем некоторые из самых общих процедур этой категории.

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

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

2. Оптимизация циклов, в том числе сдвиг вычислений неизменяющихся величин за границы циклов, разворачивание циклов и «соединение» отдельных циклов, выполняемых одно и то же количество раз, в единый цикл («сжатие цикла»).

Илон Маск рекомендует:  Asp объект response

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

4. Применение специфических для этого процессора инструкций, например, инструкции засылки в стек прямого значения или умножения числа на непосредственный операнд, имеющийся в процессорах 80186, 80188, 80286, 80386 и 80486. Также примером могут быть двухсловные строковые инструкции, команды перемножения 32-разрядных чисел и деления 64-разрядного на 32-разрядное число, которые проводятся в процессорах 80386 и 80486. Программа должна, конечно, первоначально определять, с каким типом процессора она работает.

В процессорах 80 x 86, но не 80 x 88, возможно, удастся повысить скорость действия программы на несколько процентов в результате выравнивания расположения данных и меток, на которые осуществляется передача управления, относительно определенных границ.

Процессоры 8088 и 80188 имеют 8-разрядную шину, и для них не имеет значения, на какую границу выровнены данные, поэтому выравнивание можно не применять или установить на границу байта (1 байт, 8 бит); процессоры 8086, 80186 и 80286 обладают 16-разрядной шиной, и им проще действовать с данными, выровненными на границу слова (2 байта, 16 бит); процессор 80386, для которого свойственна 32-разрядная шина, использует выравнивание на границу двойного слова (4 байта, 32 бита); из-за особенностей своей внутренней кэш-памяти процессору 80486, тоже с 32-разрядной шиной, проще работать, если осуществляется выравнивание на границу параграфа (16 байт, 96 бит).

ForCoder

Навазние: Использование ассемблера для оптимизации программ на C++
Автор: Юрий Магда
Издательство:
Год: 2004
Страниц: 452
Язык: Русский
Размер: 11
Формат: djvu

Описание книги Использование ассемблера для оптимизации программ на C++:
Рассматривается использование языка ассемблера для оптимизации программ, написанных на языке C++. Подробно изложены вопросы применения современных технологий обработки данных ММХ и SSE, а также использования особенностей архитектур современных процессоров для оптимизации программ. Приведены практические рекомендации по оптимизации логических структур высокого уровня, использованию эффективных алгоритмов вычислений, работе со строками и массивами данных.

В книгу включены примеры программного кода приложений, иллюстрирующие различные аспекты применения ассемблера. В качестве средств разработки примеров используются макроассемблер MASM 6.14 и Microsoft Visual C++ .NET 2003. Исходные тексты программ содержатся на прилагаемом к книге компакт-диске.

8,469 просмотров всего, 1 просмотров сегодня

ЛАБОРАТОРНАЯ РАБОТА №10 Оптимизация исполняемого кода с помощью ассемблера

Цель: изучение возможностей оптимизации исполняемого кода с помощью ассемблера

1. Написать на Turbo Pascal 7.0 программу с использованием циклов и массивов вещественного типа. Пример:
var x: single;
a,b: array[1..5] of single;
begin
x := 0;
for i := 1 to 5 do
x := x + a[i] * b[i];
end.

2. Откомпилировать эту программу с ключом N+ — для использования сопроцессора.

3. Нажать Shift+F4 для загрузки автономного отладчика.

4. Просмотреть откомпилированный код в окне CPU отладчика, рекомендуется в режиме “Mixed Yes”, чтобы видеть исходный код на Паскале и соответствующий ему исполняемый код в виде мнемоник аасемблера.

5. Изучив откомпилированный код и записав его, предложить свой вариант более эффективного решения задачи с использованием встроенного ассемблера. В программу включить дополнительно одну команду, по согласованию с преподавателем.

6. Написать, откомпилировать и просмотреть с помощью автономного отладчика откомпилированный код новой программы.

7. Используя выпадающее меню (появляется при нажатии правой кнопки мыши в соответствующем окне) ввести в окно Watch несколько переменных.

8. Открыть окна CPU и Numeric processor.

9. Провести пошаговое выполнение этой программы и оценить ее эффективность по сравнению с исходной. В ходе выполнения обращать внимание на содержимое регистров процессора и детально рассмотреть изменения в регистрах сопроцессора.

10.Детальное описание работы одной из команд ассемблера для сопроцессора включить в отчет.

1. Отладчики: определение, назначение, классификация, примеры.

2. Отладчик TD: определение, назначение, использование.

3. Сходство и различие режимов Step Over и Trace Into и особенности их реализации в окнах Module и CPU.

4. Окна Watch, CPU, Numeric processor, Dump, Register. Назначение, использование, особенности меню.

5. Особенности изменения значений данных (переменных, регистров и флагов) в окнах CPU, Numeric processor, Watch и других

7. Возможности оптимизации кода программы с помощью встроенного ассемблера.

ЛАБОРАТОРНАЯ РАБОТА №11 Тестирование и экспертная оценка ПС

Цель: изучить методику тестирования, основные особенности этой методики, изучить тестируемое программное средство и написать краткую инструкцию по работе с ним

1. Получить задание у преподавателя на тестирование ПС. Записать название ПС. В данной работе запись обязательна для всех пунктов работы без дополнительных напоминаний, а объем отчета может значительно влиять на оценку. Пункт без информации в отчете считается невыполненным.

2. Изучить назначение, особенности установки, работы ПС, входные и выходные данные. Записать все доступные сведения о разработчиках (фамилии, адреса, организации), времени создания (дата, версия), используемых языках и системах программирования (язык; платформа, название, версия, год выпуска, фирма-разработчик системы программирования), прочая информация.

3. Описать данные системы:

3.1. количество файлов, название и назначение каждого файла,

3.2. структура важнейших файлов,

3.3. описание формата входных данных,

3.4. описание формата выходных данных.

4. Оценить следующие параметры:

4.1. характер интерфейса пользователя,

4.2. быстродействие (указать основные параметры машины),

4.3. совместимость (по данным),

4.4. возможность коллективного использования данных,

4.5. документация (наличие, качество),

4.6. простота установки, освоения, использования,

4.7. надежность, обработка ошибок, защита и восстановление данных,

4.8. политика поддержки,

4.9. качество поддержки,

4.10. адаптивность (указать операционную систему и среду),

4.11. степень соответствия заявленному,

4.13. максимальные размеры данных.

5. Перечислить основные и дополнительные функции системы, указать степень их реализации, удобство использования каждой функции .

6. Написать краткую методику использования данного ПС.

7. Отчет — обязательно в текстовом файле, не менее 3-х страниц содержательного текста (по 60 строк страница). На двоих — не менее 6 страниц.

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

ЛАБОРАТОРНАЯ РАБОТА №12 Программирование суперскалярных операций

Цель: изучить основы разработки программ для суперскалярных процессоров

1. Выбрать задание по варианту.

2. Разработать алгоритм программы.

3. Используя систему программирования Delphi 7 версии написать программу для реализации вычислений, с использованием регистров и команд XMM.

4. Тестирование программы выполнять на процессорах Pentium 3 и старше.

Вариант 0 A * B + C + D
Вариант 1 A * (B + C) + D
Вариант 2 A * (B + C + D)
Вариант 3 A * B * C + D
Вариант 4 A * (B * C + D)
Вариант 5 A * B * (C + D)
Вариант 6 A * B + C * D
Вариант 7 A * (B + C) * D
Вариант 8 (A + B) * (C + D)
Вариант 9 A * B * C * D

1. Параллельные системы: определение, классификация, примеры.

2. Конвейерные системы: определение, назначение, использование.

3. Кластерные системы: определение, назначение, использование.

Заключение

Методические указания к выполнению лабораторных работ ориентированы на небольшой по объему практический курс, поэтому ориентирован на приобретение навыков работы с наиболее популярными пользовательскими средами. Мы ставили целью рассмотрение таких умений и приемов работы, как научиться определять конфигурацию и компоненты вычислительной системы и их основные параметры; изучить конструктивные особенности системного блока, особенностей материнской платы и особенности сборки-разборки компьютера; изучить параметры и особенности работы периферийного устройства; изучить основные особенности программирования на ассемблере и сравнить с программированием на Паскале; изучить основные особенности программирования на ассемблере процедур. Многие вопросы данного курса предназначены для самостоятельного изучения, а некоторые — для реферативного исследования.

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

Оптимизация программ на ассемблере

Это в наше-то время говорить об оптимизации программ? Бросьте! Не лучше ли сосредоточиться на изучении классов MFC или технологии .NET? Современные компьютеры так мощны, что даже Windows XP оказывается бессильна затормозить их!

Нынешние программисты к оптимизации относятся более чем скептически. Позволю себе привести несколько типичных высказываний:

» …я применяю относительно медленный и жадный до памяти язык Perl, поскольку на нем я фантастически продуктивен. В наше время быстрых процессоров и огромной памяти эффективность — другой зверь. Большую часть времени я ограничен вводом/выводом и не могу читать данные с диска или из сети так быстро, чтобы нагрузить процессор. Раньше, когда контекст был другим, я писал очень быстрые и маленькие программы на C. Это было важно. Теперь же важнее быстро писать, поскольку оптимизация может привести к столь малому росту быстродействия, что он просто не заметен «, — говорит Robert White;

» …а стоит ли тратить усилия на оптимизацию и чего этим можно достичь? Дело в том, что чем сильнее вы будете адаптировать вашу программу к заданной архитектуре, тем, с одной стороны, вы достигнете лучших результатов, а, с другой стороны, ваша программа не будет хорошо работать на других платформах. Более того, «глубокая» оптимизация может потребовать значительных усилий. Все это требует от пользователя точного понимания чего он хочет добиться и какой ценой «, — пишет в своей книге «Оптимизация программ под архитектуру CONVEX C» М. П. Крутиков;

  • » Честно говоря, я сам большой любитель “вылизывания” кода с целью минимизации используемой памяти и повышения быстродействия программ. Наверное, это рудименты времен работы на ЭВМ с оперативной памятью в 32 Кбайт. С тем большей уверенностью я отношу “эффективность” лишь на четвертое место в критериях качества программ «, — признается Алексей Малинин — автор цикла статей по программированию на Visual Basic в журнале «Компьютер Пресс».
  • С приведенными выше тезисами, действительно, невозможно не согласиться. Тем не менее, не стоит бросаться и в другую крайность. Начертавший на своем знамени лозунг «на эффективность — плевать» добьется только того, что плевать (причем дружно) станут не в эффективность, а в него самого. Не стоит переоценивать аппаратные мощности! И сегодня существуют задачи, которым не хватает производительности даже самых современных процессоров. Взять хотя бы моделирование различных физических процессов реального мира, обработку видео-, аудио- и графических изображений, распознавание текста… Да что угодно, вплоть до элементарного сжатия данных архиватором a la Super Win Zip!

    Да, мощности процессоров растут, но ведь параллельно с этим растут и требования к ним. Если раньше считалось нормальным, поставив программу на выполнение, уйти пить пиво, то сегодняшний пользователь хочет, чтобы все операции выполнялись мгновенно, ну если и не мгновенно, то с задержкой, не превышающей нескольких минут. Не стоят на месте и объемы обрабатываемых данных. Признайтесь, доводилось ли вам находить на своем диске файл размером в сотню-другую мегабайт? А ведь буквально вчера емкость целого жесткого диска была на порядок меньше!

    Цель — определяет средства . Вот из этого, на протяжении всей книги, мы и будем исходить. Ко всем оптимизирующим алгоритмам будут предъявляется следующие жесткие требования:

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

    оптимизация не должна увеличивать трудоемкость разработки (в т. ч. и тестирования) приложения более чем на 10%—15%, а в идеале, все критические алгоритмы желательно реализовать в виде отдельной библиотеки, использование которой не увеличивает трудоемкости разработки вообще;

    оптимизирующий алгоритм должен давать выигрыш не менее чем на 20%—25% в скорости выполнения. Приемы оптимизации, дающие выигрыш менее 20% в настоящей книге не рассматриваются вообще, т. к. в данном случае «овчинка выделки не стоит». Напротив, основной интерес представляют алгоритмы, увеличивающие производительность от двух до десяти (а то и более!) раз и при этом не требующие от программиста сколь ни будь значительных усилий. И такие алгоритмы, пускай это покажется удивительным, в природе все-таки есть!

  • оптимизация должна допускать безболезненное внесение изменений.
    Достаточно многие техники оптимизации «умерщвляют» программу, поскольку даже незначительная модификация оптимизированного кода «срубает» всю оптимизацию на корню. И пускай все переменные аккуратно распределены по регистрам, пускай тщательно распараллелен микрокод и задействованы все функциональные устройства процессора, пускай скорость работы программы не увеличить и на такт, а ее размер не сократить и на байт! Все это не в силах компенсировать утрату гибкости и жизнеспособности программы. Поэтому, мы будем говорить о тех, и только тех приемах оптимизации, которые безболезненно переносят даже кардинальную перестройку структуры программы. Во всяком случае, грамотную перестройку. (Понятное дело, что «кривые» руки угробят что угодно — против лома нет приема).
  • Согласитесь, что такая постановка вопроса очень многое меняет. Теперь никто не сможет заявить, что, дескать, лучше прикупить более мощный процессор, чем тратить усилия на оптимизацию. Ключевой момент предлагаемой концепции состоит в том, что никаких усилий на оптимизацию тратить как раз не надо . Ну… почти не надо, — как минимум вам придется прочесть эту книжку, а это какие ни какие, а все-таки усилия . Другой вопрос, что данная книга предлагает более или менее универсальные и вполне законченные решения, не требующие индивидуальной подгонки под каждую решаемую задачу.

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

    И в заключении позвольте привести еще одну цитату:

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

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

    О чем и для кого предназначена эта книга

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

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

    Илон Маск рекомендует:  Функции hyperwave

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

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

    Материал книги в основном ориентирован на микропроцессоры AMD Athlon и Intel Pentium-II, Pentium-III и Pentium-4, но местами описываются и более ранние процессоры.

    Семь китов оптимизации или
    Жизненный цикл оптимизации

    Часто программист (даже высококвалифицированный!), обнаружив профилировщиком «узкие» места в программе, автоматически принимает решение о переносе соответствующих функций на ассемблер. А напрасно! Как мы еще убедимся (см. Часть III. » Смертельная схватка: ассемблер VS-компилятор «), разница в производительности между ручной и машинной оптимизацией в подавляющем большинстве случаев крайне мала. Очень может статься так, что улучшать уже будет нечего, — за исключением мелких, «косметических» огрехов, результат работы компилятора идеален и никакие старания не увеличат производительность, более чем на 3%—5%. Печально, если это обстоятельство выясняется лишь после переноса одной или нескольких таких функций на ассемблер. Потрачено время, затрачены силы… и все это впустую. Обидно, да?

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

    Назовем ряд правил оптимизации.

    Правило I

    Прежде, чем оптимизировать код, обязательно следует иметь надежно работающий не оптимизированный вариант или «. put all your eggs in one basket, after making sure that you’ve built a really *good* basket» («…прежде, чем класть все яйца в одну корзину — убедись, что ты построил действительно хорошую корзину «). Таким образом прежде, чем приступать к оптимизации программы, убедись, что программа вообще-то работает.

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

    Правило II

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

    Правило III

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

    Правило IV

    Прежде, чем порываться переписывать программу на ассемблер, изучите ассемблерный листинг компилятора на предмет оценки его совершенства. Возможно, в неудовлетворительной производительности кода виноват не компилятор, а непосредственно сам процессор или подсистема памяти, например. Особенно это касается наукоемких приложений, жадных до математических расчетов и графических пакетов, нуждающихся в больших объемах памяти. Наивно думать, что перенос программы на ассемблер увеличит пропускную способность памяти или, скажем, заставит процессор вычислять синус угла быстрее. Получив ассемблерный листинг откомпилированной программы (для Microsoft Visual C++, например, это осуществляется посредством ключа /FA), бегло просмотрите его глазами на предмет поиска явных ляпов и откровенно глупых конструкций наподобие: MOV EAX, [EBX]\MOV [EBX], EAX. Обычно гораздо проще не писать ассемблерную реализацию с чистого листа, а вычищать уже сгенерированный компилятором код. Это требует гораздо меньше времени, а результат дает ничуть не худший.

    Правило V

    Если ассемблерный листинг, выданный компилятором, идеален, но программа без видимых причин все равно исполняется медленно, не отчаивайтесь, а загрузите ее в дизассемблер. Как уже отмечалось выше, оптимизаторы крайне неаккуратно подходят к выравниванию переходов и кладут их куда "глюк" на душу положит. Наибольшая производительность достигается при выравнивании переходов по адресам, кратным шестнадцати, и будет уж совсем хорошо, если все тело цикла целиком поместится в одну кэш-линейку (т. е. 32 байта). Впрочем, мы отвлеклись. Техника оптимизации машинного кода — тема совершенно другого разговора. Обратитесь к документации, распространяемой производителями процессоров — Intel и AMD.

    Правило VI

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

    Правило VII

    Если уж взялись писать на ассемблере, пишите максимально "красиво" и без излишнего трюкачества. Да, недокументированные возможности, нетрадиционные стили программирования, "черная магия", — все это безумно интересно и увлекательно, но… плохо переносимо, непонятно окружающим (в том числе и себе самому после возращения к исходному коду десятилетней давности) и вообще несет в себе массу проблем. Автор этих строк неоднократно обжигался на своих же собственных трюках, причем самое обидное, что трюки эти были вызваны отнюдь не "производственной необходимостью", а… ну, скажем так, "любовью к искусству". За любовь же, как известно, всегда приходится платить. Не повторяйте чужих ошибок! Не брезгуйте комментариями и непременно помещайте все ассемблерные функции в отдельный модуль. Никаких ассемблерных вставок — они практически непереносимы и создают очень много проблем при портировании приложений на другие платформы или даже при переходе на другой компилятор.

    Единственная предметная область, не только оправдывающая, но, прямо скажем, провоцирующая ассемблерные извращения, это — защита программ, но это уже тема совсем другого разговора…

    Распространенные заблуждения

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

    Заблуждение I

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

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

    Заблуждение II

    Перенос программы на ассемблер только в исключительных случаях увеличивает ее эффективность. При трансляции качественного исходного кода, оптимизирующие компиляторы отстают от идеальной ручной оптимизации не более чем на 10%—20%. Конечно, это весьма ощутимая величина, но все же не настолько, чтобы оправдать трудоемкость программирования на чистом ассемблере!

    Подробнее о сравнении качества машинной и ручной оптимизации см. Часть III. " Смертельная схватка: ассемблер VS-компилятор ".

    Заблуждение III

    Вообще говоря, кроме компиляторов, разрабатываемых Intel, никакие другие компиляторы не умеют генерировать оптимально спланированный с точки зрения микроархитектуры процессора код. Несколькими страницами далее (см. " Практический сеанс профилировки с VTune ") вы собственноручно сможете убедиться в этом, а пока же просто поверьте автору на слово.

    Под оптимальностью обычно понимается глобальный экстремум некоторой оценочной функции. При оптимизации по скорости ищут абсолютный минимум числа тактов. Очевидно, этот минимум зависит от входных данных. В лучшем случае компилятору можно передать данные тестового прогона (так называемый profiler feedback). На основании этих данных компилятор может более аккуратно присвоить частоты выполнения разным последовательностям инструкций, не более того. Компиляторы Intel ни коим образом не генерируют оптимального кода. Исходя из определения, оптимальная по скорости программа не может быть переписана с сохранением смысла так, что начнет исполняться быстрее. Мне приходилось переписывать вручную порожденный этим компилятором код так, что он становился быстрее.

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

    Тем не менее, современные процессоры с одной стороны достаточно умны и самостоятельно оптимизируют переданный им на выполнение код. С другой стороны оптимального кода для всех процессоров, все равно не существует и архитектурные особенности процессоров P-II, P-4, AMD K6 и Athlon отличаются друг от друга столь разительно, что все позывы к ручной оптимизации гибнут прямо на корю.

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

    Заблуждение IV

    Как гласит народная мудрость "Хорошо там, — где нас нет". Сам я, правда, ничего не оптимизирую под PowerPC, но хорошо знаком с людьми, разрабатывающими под него оптимизирующие компиляторы. И могу сказать, что они далеко не в восторге от его "закидонов", коих у него, поверьте уж, предостаточно.

    Да, семейству процессоров x86 присущи многие проблемы и ограничения. Но это ничуть не оправдывает программистов, пишущих "уродливый" код, и палец о палец не ударяющих, чтобы хоть как-то его улучшить.

    А "язык" x86 процессоров, между прочим, очень интересен. На сегодняшний день они имеют едва ли не самую сложную систему команд, дающую системным программистам безграничные возможности для самовыражения. Прикладные программисты даже не догадываются сколько красок мира у них украли компиляторы!

    Asmworld Программирование на ассемблере для начинающих и не только

    Учебный курс. Часть 0. Зачем учить ассемблер

    Автор: xrnd | Рубрика: Учебный курс | 27-01-2010 | Распечатать запись

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

    Ассемблер — это практически самый древний язык программирования. До него было лишь программирование в машинных кодах ��

    Итак, какие же преимущества дает знание ассемблера:

    1. Глубокое понимание работы компьютера и операционной системы.
    2. Максимальная гибкость при работе с аппаратными ресурсами.
    3. Оптимизация программ по скорости выполнения.
    4. Оптимизация программ по размеру кода.
    5. Дизассемблирование и отладка.

    Глубокое понимание работы компьютера и операционной системы.

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

    Максимальная гибкость при работе с аппаратными ресурсами.

    Используя ассемблер, можно делать с компьютером все что угодно! А языки высокого уровня ограничены компилятором и используемыми библиотеками. Такие современные языки, как Java и C# вобще не позволяют работать с аппаратными ресурсами и операционной системой напрямую.

    Оптимизация программ по скорости выполнения.

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

    Оптимизация программ по размеру кода.

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

    Дизассемблирование и отладка.

    Знание ассемблера позволяет расковырять любую программу дизассемблером и изучить механизм её работы! Только представьте — можно залезть внутрь любой программы и посмотреть как она работает. Иногда бывает, что попадается интересная программа и не понятно, что там внутри, как она написана. Зная ассемблер, можно заглянуть внутрь любой программы и удовлетворить свое любопытство, даже не имея исходников.

    Ассемблер очень может помочь при отладке. Иногда случаются ошибки и в компиляторах — очень злая тема. Вроде бы корректно написанный код выполняется вовсе не так, как предполагалось. Чтобы обнаружить такую ошибку надо посмотреть, во что скомпилился код, а разобраться в этом без ассемблера невозможно.

    Понравилась статья? Поделиться с друзьями:
    Кодинг, CSS и SQL