Элементарная оптимизация
Когда дело доходит до оптимизации, многие люди относятся пренебрежительно: «Скорость компьютеров сейчас такая высокая, какой смысл быть на несколько процентов быстрее?» В этом есть некоторый смысл. Результаты, собранные текущими компиляторами, полностью оптимизированы. За исключением разработки специального программного обеспечения, такого как графика, изображения и мультимедиа, в большинстве случаев преднамеренная оптимизация не требуется, но если код пишут разработчики. время вы уже знали об оптимизации. Выполняя оптимизацию, вы можете обеспечить или даже повысить эффективность разработки. Почему бы и нет?
Конечно, конструкция алгоритма является основой оптимизации. В большинстве случаев эффективность выполнения программы в основном определяется общим пониманием программы разработчиком, дизайном алгоритма и т. д.! Но иногда оптимизация деталей тоже имеет смысл!
Более того, во многих случаях такого рода оптимизация не требует написания кода непосредственно через ассемблер, но в данном случае это также может отражать превосходство владения знаниями ассемблера!
Например, следующие две функции:
функция GetBit(i: Кардинал; n: Кардинал): Boolean;
начинать
Результат:= Boolean((i shr n) и 1);
конец;
функция GetBit(i: Кардинал; n: Кардинал): Boolean;
начинать
Результат:= Boolean((1 shl n) и i);
конец;
Соответствующий ассемблерный код:
МОВ ЭКХ, EDX
SHR EAX, CL
И EAX, $01
МОВ ЭКХ, EDX
МОВ EDX, 01 долл. США
СХЛ ЭДКС, КЛ
И EAX, EDX
У них одна и та же функция: все они принимают значение определенного бита i и возвращают True, если оно равно 1, и False, если оно равно 0!
На первый взгляд может показаться, что эффективность выполнения этих двух функций одинакова, но на самом деле есть разница. Операция сдвига в первой программе выполняется в соответствии с соглашением о вызовах в Delphi, зарегистрируйте, в это время значение сохраняется в регистре EAX, и операция сдвига может быть завершена напрямую, но вторая программа отличается. Чтобы завершить операцию сдвига для непосредственного значения 1, его необходимо сначала передать в регистр. так что должна быть еще одна инструкция! Конечно, не во всех случаях меньшее количество инструкций обязательно будет быстрее, чем большее количество инструкций. Во время конкретного выполнения мы также должны учитывать такие вопросы, как тактовый цикл выполнения инструкций и спаривание инструкций (подробнее об этом позже). проблема независима только тогда, когда Сравнения можно проводить только в определенных средах кода.
В обычных обстоятельствах эта разница в эффективности слишком незначительна, но всегда полезно помнить об оптимизации во время программирования! Если такой код расположен на самом внутреннем слое цикла, и через большое количество циклов накапливается N тактов, разница в эффективности выполнения может стать очень большой!
Выше приведен лишь небольшой пример. Видно, что если вы сможете подумать о некоторых вопросах с точки зрения сборки во время разработки, вы сможете написать более эффективный подробный код на языках высокого уровня, обеспечив при этом эффективность разработки! Но все же во многих случаях детальную оптимизацию приходится выполнять с использованием встроенного ассемблерного кода, а иногда благодаря применению встроенного ассемблерного кода написание кода также может стать более эффективным.
Если вам нужно изменить порядок байтов 32-значного числа, как вы можете сделать это полностью на языке высокого уровня в Delphi? Вы можете использовать сдвиг, вы также можете вызывать встроенную функцию Swap несколько раз, но если подумать об инструкции BSWAP, все становится очень просто.
функция SwapLong(Значение: Кардинал): Кардинал;
Асм
БСВАП EAX
конец;
Примечание. Как и выше, значение Value хранится в регистре EAX, а 32-значное значение также возвращается через EAX, поэтому требуется только одно предложение.
Конечно, большинство оптимизаций встроенных ассемблеров не так просты, но добиться более глубокой оптимизации с небольшими знаниями ассемблера, полученными в колледже, сложно. Опыт можно получить только путем постоянного накопления и сравнения скомпилированных ассемблерных кодов! К счастью, в большинстве случаев детальная оптимизация не является основной частью разработки программы.
Однако если разрабатываемая программа предполагает использование графики, изображений, мультимедиа и т. д., провести более глубокую оптимизацию все равно необходимо! К счастью, Delphi6 может обеспечить хорошую поддержку, будь то оптимизация инструкций с плавающей запятой или применение MMX, SSE, 3DNow и т. д. Даже если вы хотите, чтобы более ранние версии Delphi поддерживали эти расширенные наборы инструкций ЦП или хотите поддерживать новые наборы инструкций ЦП в будущем, вы можете использовать четыре ассемблерные инструкции DB, DW, DD и DQ, поддерживаемые Delphi во встроенной ассемблере ( в официальном руководстве Delphi6 от Borland. В руководстве по языку говорится только, что он поддерживает DB, DW и DD), а числовое представление соответствующих инструкций также может быть гибко реализовано.
нравиться:
DW $A20F //ИД процессора
DW $770F //EMMS
БД $0F, $6F, $C1 //MOVQ MM0, MM1
Понимание инструкций — это только основа. После разработки алгоритма на основе FPU, MMX и SSE, если вы хотите его оптимизировать, вы также должны понимать некоторые технические характеристики самого ЦП.
Давайте посмотрим на следующие два фрагмента кода:
Асм
ДОБАВИТЬ[а], ЭКХ
ДОБАВИТЬ [б], EDX
конец
Асм
ДВИЖЕНИЕ EAX, [а]
МОВ EBX, [б]
ДОБАВИТЬ EAX, ECX
ДОБАВИТЬ EBX, EDX
МОВ [а], EAX
МОВ [б], EBX
конец
Второй более эффективен? Неправильно, как уже говорилось выше, меньшее количество инструкций не означает высокую эффективность выполнения. Согласно соответствующей информации, такт выполнения двух инструкций в первом участке кода равен 3 (каждая инструкция должна выполнить три шага чтения). шаг модификации и записи), все тактовые циклы, выполняемые шестью инструкциями во втором разделе кода, равны 1. Значит, эти два фрагмента кода одинаково эффективны? И снова неверно: второй фрагмент кода на самом деле выполняется более эффективно, чем первый! Почему? Поскольку процессоры после класса Pentium имеют два конвейера для выполнения инструкций, когда две соседние инструкции могут быть объединены в пару, они могут выполняться одновременно! Каковы конкретные причины для двух приведенных выше фрагментов кода?
Хотя две инструкции в первом коде могут быть спарены, общий требуемый такт выполнения составляет 5 вместо 3, тогда как шесть инструкций во втором коде могут выполняться параллельно, что и приводит к такому результату.
Говоря об этом, это все очень простые примеры, которые сами по себе не могут вам помочь. Если вы действительно хотите оптимизировать конкретную программу, вам следует поискать специальные статьи по оптимизации FPU и MMX или найти технические руководства для изучения и изучения таких технологий, как «выполнение вне очереди» и «предсказание ветвей». Я просто надеюсь, что все мои друзья, которые учатся в колледже, не будут просто сосредоточены на этих «приносящих деньги» инструментах разработки и модных новых технологиях, но смогут уделять больше времени закладке фундамента. Имея прочный фундамент, вы сможете быстро освоить новые знания. , Только так мы сможем быстрее освоить новые инструменты и навыки разработки... (пропустить тысячу слов).
Но опять же, знания все равно нужно использовать для решения практических задач. Если вы каждый день сосредотачиваетесь только на технических деталях, вы можете стать отличным хакером, но никогда не будете разрабатывать первоклассное программное обеспечение. Следовательно, фундаментальной целью по-прежнему должно быть создание стоимости. Итак... Я больше не буду об этом говорить, это не будет похоже на техническую статью, если я буду говорить об этом дальше. ^_^
Приложение: Помимо рассмотрения эффективности выполнения, оптимизация программы также должна учитывать проблемы с размером (маленький размер может быстрее загружать память и быстрее выполнять декодирование инструкций и другие задачи). Например, чтобы очистить регистр EAX, используйте SUB EAX, EAX или XOR EAX. , EAX вместо MOV EAX, $0 Хотя тактовый цикл их выполнения равен 1, длина инструкции первого (2 байта) явно короче, чем у второго (5 байт). Но поскольку вышеизложенное — это все детали, вопрос объема не был упомянут. Дополнительные проблемы уменьшения размера следует оставить на усмотрение компилятора. Просто уделите немного внимания при написании встроенного кода ASM.