Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
kernigan_paik.doc
Скачиваний:
0
Добавлен:
01.07.2025
Размер:
2.91 Mб
Скачать

7.5. Эффективное использование памяти

Память — один из самых дорогостоящих компьютерных ресурсов, ко­торого вечно не хватает; огромное количество плохих программ возник­ло из попыток выжать все, что можно, из того немногого, что имелось в наличии. Небезызвестная "проблема 2000 года" зачастую причисляет­ся к разряду именно таких проблем: когда память была действительно буквально на вес золота, потратить два байта на число 19 считалось не­позволительной роскошью. Действительно ли память является главной причиной проблемы или нет, — возможно, все беды возникли из-за пере­несения наших бытовых привычек (мы нечасто указываем в документах, к какому веку они относятся, — это и так очевидно) на программы, — но приведенный пример отлично демонстрирует всю опасность недально­видной оптимизации.

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

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

Используйте минимально возможный размер типа данных. Первый шаг к более эффективному использованию места — попытаться с минималь­ными изменениями лучше распределить существующую память, исполь­зуя, например, минимальные из возможных типов данных. Эта экономия может означать замену int на short, если данные это позволяют, в частно­сти такое представление используется для координат в системах двумер­ной графики, поскольку 16 битов, по-видимому, достаточно для любых диапазонов экранных координат. Можно экономить память и заменой double на float, но при этом надо опасаться потери точности, ведь float содержит, как правило, только 6 или 7 десятичных знаков.

В описанных и аналогичных им случаях в программу почти наверня­ка придется внести и другие соответствующие изменения, самым оче­видным из которых будет замена спецификаторов формата в printf и, что особенно важно, в scanf.

Логическим расширением этого подхода является кодирование ин­формации в байте или даже в нескольких битах. Не используйте бито­вые поля C/C++; они ухудшают переносимость и нередко ведут к не­однозначному и неэффективному коду. Вместо этого поместите нужные операции в функции, которые будут считывать и устанавливать отдель­ные биты внутри слова или массива слов с помощью сдвигов или бито­вых масок. Такие функции возвращают группу последовательных битов из середины слова:

/* getbits: получает п битов начиная с позиции р */

/* биты нумеруются с 0 (наименее значимого) вверх */

unsigned int getbits(unsigned int x, int p, int n)

{

return (x >> (p+1-n)) & (0 << n);

}

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

Не храните то, что можете без труда вычислить. Надо сказать, что по­добные изменения — не самые эффективные; они похожи на тонкую настройку кода. Глобальные улучшения, как правило, достигаются только подбором более удачной структуры данных и соответствую­щим изменением алгоритма. Приведем пример. Много лет назад к од­ному из нас обратился за помощью наш коллега: он пытался произво­дить некоторые вычисления над матрицей, которая была так велика, что для того, чтобы она уместилась в памяти, приходилось перезагру­жать компьютер, сильно урезая операционную систему. Очень хоте­лось найти какую-нибудь альтернативу, потому что работать в подоб­ном режиме было ужасно неудобно. Мы сразу попросили рассказать, что же представляет собой эта матрица, и выяснили, что она содержала целые числа, большая часть из которых была нулями13. И лишь ме­нее 5 % элементов матрицы были отличны от нуля. Подобная структу­ра тут же навела нас на мысль о представлении, в котором хранились бы только ненулевые элементы матрицы, а доступ к каждому элементу n[i][j] заменялся бы вызовом функции m(i, j). Существует несколько способов хранить данные. Наверное, самый простой из них — это мас­сив указателей, по одному на столбец, каждый из которых указывает на компактный массив номеров строк и соответствующих значений. При такой организации на каждый ненулевой элемент тратится больше ме­ста, однако суммарное место все же экономится; доступ к каждому эле­менту получается более медленным, но это все равно быстрее, чем пе­резагружать машину. Наш коллега принял наше предложение и был вполне доволен результатом.

Аналогичный подход мы использовали и при решении современного варианта этой проблемы. Некая система проектирования должна была представлять данные о виде поверхности и силе радиосигнала, относя­щиеся к достаточно большим площадям (от 100 до 200 километров в по­перечнике), с разрешением в 100 метров. Сохранение этих данных в одном большом прямоугольном массиве неизбежно привело бы к переполне­нию памяти па машинах, для которых эта система разрабатывалась, и, со­ответственно, к активному дисковому обмену. Однако было ясно, что для достаточно больших территорий данные о поверхности Земли и о си­ле сигнала будут одинаковы, так что для решения проблемы можно было применить иерархическое представление данных, при котором ре­гионы с одинаковыми параметрами хранились в одном элементе.

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

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

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

Более эффективное распределение пространства зачастую покупает­ся ценой увеличения времени исполнения программы. Некое прило­жение должно было передавать большой рисунок из одной программы в другую. Рисунки в достаточно простом формате РРМ занимали, как правило, около мегабайта, так что мы подумали, что было бы гораздо бы­стрее кодировать их для передачи в сжатом формате GIF, — тогда файлы получались бы размером примерно 50 килобайтов. Однако кодирование и декодирование GIF занимало столько времени, что это сводило на нет всю экономию от передачи файла меньшего размера, то есть мы ничего не выгадывали. Код для обработки формата GIF состоял примерно из 500 строк, а для РРМ из 10. Таким образом, для облегчения поддер­жки кода мы отказались от преобразования в GIF, и программа до сих пор работает с форматом РРМ. Естественно, если бы речь шла о переда­че файлов по сети, то предпочтение было бы отдано формату GIF.

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]