Энциклопедия Turbo Pascal. Главы 9-11 - Использование процедур и функций

ОГЛАВЛЕНИЕ

Использование процедур и функций

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

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

Чтобы понять, как вызов функции может замедлить вашу программу, рассмотрим два примера:

   Версия 1                     Версия 2
    for x:=1 to 100 do                for x:=1 to 100
      t:=compute(x);                  t:=Abs(Sin(q)/100/3.1416);

    function compute(q: integer): real;
    var
      t:real;
    begin
      compute:=Abs(Sin(q)/100/3.1416);
    end;

Хотя каждый цикл выполняет одну и ту же функцию, версия 2 гораздо быстрее, так как использование непосредственного кода устраняет задержки, связанные с последовательностями вызова и возврата. Для понимания того, сколько времени тратится, рассмотрим следующий код на псевдоассемблере, который демонстрирует в теории последовательности вызова и возврата для функции compute:

    ; последовательность вызова
    move A, x ; поместить значение х в аккумулятор
    push A
    call compute ; инструкция вызова помещает адрес
               ; возврата в стек

    ; последовательность возврата
    ; значение      возврата функции должно быть помещено в
    ; регистр - мы используем В
    move B, stack-1 ; доставить значение во временное t
    return ; возврат к вызвавшей процедуре
    ; вызвавшая процедура затем выполняет следующие действия

Использование функции compute внутри цикла ведет к тому, что последовательности вызова и возврата будут выполнены 100 раз. Если вы хотите написать быстрый код, то использование данной функции внутри цикла - это не самый лучший подход.

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