3.6. Память

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

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

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

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

3.6.1. ОВЕРЛЕЙНОСТЬ ПРОГРАММЫ "

Под оверлейностью программы понимают возможность перенесения подпрограмм во время работы программы в быстродействующую память из некоторого другого типа памяти таким образом, что несколько подпрограмм в различное время занимают одну и ту же область памяти. Оверлейность используют в том случае, когда общие требования к объему программы превышают размер имеющейся в нашем распоряжении оперативной памяти. Одной из немногих систем, не предусматривающих оверлейность,. является компилятор \VATFIV ФОРТРАНа.

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

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

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

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

Можно сделать некоторые замечания относительно оверлейности для экономии времени и памяти. Желательно, чтобы сегменты /были примерно одного размера. Размер максимального оверлейного сегмента определяет требования ко всей памяти.

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

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

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

3.6.2. ВИРТУАЛЬНАЯ ПАМЯТЬ

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

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

Программист может принять некоторые меры для повышения эффективности выполнения программы при использовании виртуальной памяти. Программу следует писать с подпрограммами. Это улучшает свойство локализованное™ программы, т. е. степени, до которой во время выполнения удается выделить в программе некоторый ее фрагмент. Это легко выполняемый прием программирования. Локализованность улучшается при использовании методов структурного программирования. Избегайте использования COMMON-, или глобальных, переменных, так как они приводят к ухудшению локальности. Кроме того, старайтесь не употреблять операторы GO ТО и метки. То и другое обеспечит высокую степень локализованности логики вашей программы и ссылок на данные.

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

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

Имеется несколько классических примеров очень простых программ, которые неэффективно выполняются при использовании виртуальной памяти. Например, программа на ФОРТРАНе, присваивающая нулевые значения массиву чисел двойной точности:

DO 15 К=1, 512 DO 15 L=l, 20 15 X(K,L)=0.0

Программа составлена неудачно, так как массивы в ФОРТРА-Не хранятся столбцами и каждый столбец занимает страницу размером 4К. Таким образом, при каждом выполнении оператора с меткой 15 нужно обращаться к другой странице. Внутренний цикл будет присваивать нулевые значения элементам, находящимся на 20 различных страницах, и это действие будет выполняться 512 раз. Решение проблемы состоит в изменении порядка циклов: необходимо сделать К= 1,512 внутренним циклом. В этом случае программа присвоит нулевые значения 512 элементам, расположенным на одной странице, прежде чем потребуется новая страница. Сделав такое простое изменение порядка циклов, мы можем уменьшить количество обращений к дискам в 512 раз.

Подобная ситуация возникает при обработке некоторых массивов внутри цикла, например:

DO 25 К=1, N А(К)=0.0 В(К) = 0.0 С(К)=0.0 25 CONTINUE

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

3.6.3. СОВЕТЫ ПОЛЬЗОВАТЕЛЯМ ВИРТУАЛЬНОЙ ПАМЯТИ

1. Создавайте программы при помощи оптимизирующего компилятора, так как в этом случае программа будет короче.

2. Структурированные программы и использование модулей увеличат локализованность.

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

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

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

В работе [7] вы найдете многочисленные предложения, относящиеся к системам с виртуальной памятью.

3.6.4. ЗАГОЛОВКИ СООБЩЕНИИ

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

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

3.6.5. ЭКВИВАЛЕНТНОСТЬ

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

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

3.6.6. ИСПОЛЬЗОВАНИЕ ЦИКЛОВ

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

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

3.5. Эффективность выполнения программ || Оглавление || 3.7. Вычисление констант


Услуги