ЭЛЕКТРОННАЯ БИБЛИОТЕКА КОАПП |
Сборники Художественной, Технической, Справочной, Английской, Нормативной, Исторической, и др. литературы. |
Часть 22 Глава 20. Автоматическая оптимизация В Турбо Паскале выполняется несколько различных типов оптимизации кода, начиная от свертывания констант и вычисления булевских выражений по короткой схеме и кончая эффективной компоновкой. Рассмотрим некоторые виды оптимизации. Свертывание констант Если участвующие в операции операнды представляют собой константы перечислимого типа, то в Турбо Паскале такое выражение вычисляется во время компиляции. Например, выражение: Х := 3 + 4 * 2 приведет к генерации такого же кода, как выражение Х := 11, а выражение: S := 'In' + 'Out' генерирует тот же код, что S := 'InOut'. Аналогично, если операнды функций Abs, Sqr, Succ, Pred, Ptr, Odd, Ord, Round, Trunc, Lo, Hi и Swap представляют собой константы перечислимого типа, то функция вычисляется во время компиляции. Если индексом массива является констанда или выражение, состоящее из констант, то адрес элемента вычисляется во время компиляции. Например, доступ к элементу Dаtа[5,5] так же эффективен, как доступ к простой переменной. Слияние констант Ипользование одной и той же строковой константы два или более раз приводит к генерации только одной копии константы. Например, два или более оператора Write('Dоnе') в одной и той же части программы приведет к ссылке на одну и ту же копию строковой к онстанты 'Donе'. Вычисление по короткой схеме В Турбо Паскале реализуется вычисление булевского выражения по короткой схеме. Это означает, что вычисление булевского выражения прекращается, как только результат всего булевского выражения становится очевидным. При этом обеспечивается минимальное время выполнения и, обычно, минимальный размер объектного кода. Вычисление по короткой схеме делает также возможным вычисление конструкций, которые иначе были бы недопустимыми. Например: while (I <= Length(S)) and (S[I] <> ' ') do Inc(I); while (P <> nil) and (P^.Value <> 5) do P := P^.Next; В обоих случаях, если первая проверка имеет значение Falsе, вторая проверка не вычисляется. Противоположным вычислению по короткой схеме является полное вычисление, которое можно выбрать с помощью директивы компилятора {$В+}. В этом случае обеспечивается вычисление каждого операнда булевского выражения. Порядок вычисления Стандартами Паскаля допускается, что операнды в выражении часто вычисляются в порядке, отличном от того, в котором они записаны (слева направо). Например, оператор: I := F(J) div G(J) где F и G - функции целого типа, приводит к тому, что G вычисляется перед вычислением F, так как это позволяет компилятору получить более оптимальный объектный код. Важно, поэтому, чтобы выражение никогда не зависело от какого-то конкретного порядка выч исления встроенных функций. Если вернуться к предыдущему примеру, то для того, чтобы вызвать функцию F перед функцией G, можно использовать временную переменную: T := F(J); I := T div G(J); Исключением из этого правила является вычисление по короткой схеме (разрешенное директивой компилятора {$В-}), при котором операнды булевского типа, связанные операциями and или оr, всегда вычисляются слева направо. Проверка на допустимость границ Присваивание константы переменной и использование константы в качестве значения параметра проверяется во время компиляции на допустимость нахождения в заданных границах. При этом генерируется такой код, что во время выполнения таких проверок не дела ется. Например, Х := 999, где Х - переменная байтового типа (Bytе), приводит к ошибке компиляции. Использование двига вместо умножения Операция Х*С, где С - константа, являющаяся степенью числа 2, приводит к генерации объектного кода, в котором используется инструкция Shl (сдвиг влево). Аналогично, когда размерность массива представляет собой степень числа 2, то для вычисления индексных выражений используется инструкция Shl (а не инструкция Мul). Автоматическое выравнивание на границу слова По умолчанию Турбо Паскаль выравнивает все переменные и типизованные константы, превышающие по размеру 1 байт, на границу машинного слова. На всех 16-разрядных процессорах семейства 80х86 выравнивание на границу слова означает более быстрое выполнен ие, поскольку доступ к элементам размером в слово или четным адресам осуществляется быстрее, чем к словам по нечетному адресу. Выравнивание данных управляется директивой компилятора $A. По умолчанию в состоянии {$A+} переменные и типизованные константы выравниваются указанным выше образом. В состоянии {$A-} никаких действий по выравниванию не производится. Дальнейшие подроб ности приведены в Главе 21 ("Директивы компилятора"). Удаление неиспользуемого кода Операторы, о которых известно, что они никогда не будут выполняться, не включаются в объектный код. Данные выражения, например, не приведут к генерации объектного кода: if false then statement { оператор } while false do statement Эффективная компоновка Компоновщик автоматически удаляет неиспользуемый код (по процедурам), то есть процедуры и функции, являющиеся частью скомпилированной программы, но к которым нет обращений, не включаются в файл типа .EXE. Процедуры, функции, переменные и типизованны е константы, участвующие в процессе компиляции, но ссылки на которые отсутствуют, удаляются из файлa .EXE. Удаление неиспользуемого кода выполняется по процедурам, а удаление неиспользуемых данных - по секциям, где эти данные описываются. Рассмотрим следующую программу: program SmartLink; const H: array[0..15] of char = '0123456789ABCDEF'; var I,J : integer; X,Y : real; var S: string[79]; var A: array[1..10000] of integer; procedure P1: begin A[1] = 1; end; procedure P2; begin I := 1; end; procedure P3; begin S := 'Turbo Pascal'; P2; end; begin P3; end; Основная программа вызывает процедуру P3, которая вызывает процедуру P2, поэтому обе процедуры P2 и P3 включаются в файл .EXE. Поскольку P2 ссылается на первый раздел описания переменных, а P3 ссылается на второй раздел описание переменных, переменн ые I, J, X, Y, S также включаются в выполняемый файл. Однако на процедуру P1 никаких ссылок нет, а включенные в выполняемый файл процедуры не ссылаются на переменные Н и A, поэтому эти объекты удаляются. Эффективная компоновка имеет особую ценность в связи с использованием модулей, которые реализуют библиотеки процедур и функций. Примером такого модуля является стандартный модуль WinDos, который содержит ряд процедур и функций. При этом программа ре дко использует все эти процедуры. Если она использует только одну или две процедуры или функции, то только эти процедуры включаются в полученный в результате код. |