Чтение онлайн

на главную - закладки

Жанры

Фундаментальные алгоритмы и структуры данных в Delphi

Бакнелл Джулиан М.

Шрифт:

– ------

Что дает нам постоянный размер узла? При необходимости записи данных в связный список нужно предварительно распределить память под узел. Для этого пришлось бы использовать очень сложный диспетчер кучи Delphi, всего лишь, чтобы распределить 8 байт памяти. Диспетчер кучи содержит код, который может манипулировать кусками памяти, а также распределять и освобождать память любого размера. Все операции выполняются быстро и эффективно. Но мы знаем, что будут использоваться только 8-байтные блоки, и нам нужны только такие блоки. Можно ли, учитывая постоянство размеров блоков, увеличить скорость распределения памяти для новых узлов и освобождения памяти, занимаемой удаляемыми узлами? Конечно же, ответ "да".

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

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

Что в предыдущем абзаце понимается под понятием "свободный список"? Это общепринятая конструкция в программировании. у нас имеется набор некоторых элементов, которые "распределяются" и "освобождаются". При освобождении элемента он, скорее всего, в какой-то момент времени будет использоваться повторно поэтому вместо возвращения занимаемой элементом памяти поддерживается свободный список, т.е. список свободных элементов. Диспетчер кучи Delphi использует свободный список освобожденных блоков памяти различного размера. Многие базы данных поддерживают скрытый свободный список удаленных записей, которые можно использовать повторно. Массив записей, описанный в главе 2, использует цепочку удаленных записей, которая является ни чем иным, как другим названием свободного списка. При необходимости распределения памяти под элемент приложение обращается к свободному списку и выбирает один из свободных элементов.

Давайте разработаем диспетчер распределения узлов. Он будет содержать свободный список узлов, который вначале устанавливается равным nil, что означает, что список пуст. При распределении узла диспетчер будет просматривать свой свободный список. Если он пуст (равен nil), диспетчер распределяет большой блок памяти, обычно называемый страницей, при помощи диспетчера кучи Delphi. Затем станица разделяется на отдельные куски с размером, равным размеру узла, и куски записываются в свободный список. После этого узел извлекается из списка и передается пользователю. При освобождении узла он записывается в свободный список. Под "записью" здесь понимается вставка узла в начало списка, а под "извлечением" - считывание узла с начала списка.

Листинг 3.1. Класс TtdNodeManager

TtdNodeManager = class private

FNodeSize : cardinal;

FFreeList : pointer;

FNodesPerPage : cardinal;

FPageHead : pointer;

FPageSize : cardinal;

protected

procedure nmAllocNewPage;

public

constructor Create(aNodeSize : cardinal);

destructor Destroy; override;

function AllocNode : pointer;

procedure FreeNode(aNode : pointer);

end;

Как видно из приведенного кода, реализация интерфейса класса достаточно проста. Класс позволяет создавать и уничтожать экземпляры и распределять и освобождать узлы. Конструктор Create в качестве единственного входного параметра принимает размер узла, а затем на его основании вычисляет несколько значений: количество узлов на странице и размер страницы. Класс пытается распределять страницы размером 1024 байта. Но если размер узла настолько велик, что на одной

такой странице может поместиться только один узел, размер страницы выбирается равным размеру узла. Для увеличения эффективности размер узла округляется до ближайшей границы 4 байт (фактически округление выполняется до ближайшего значения sizeof(pointer)).

Листинг 3.2. Конструктор TtdNodeManager.Create

constructor TtdNodeManager.Create(aNodeSize : cardinal);

begin

inherited Create;

{сохранить размер узла, округленный до ближайших 4 байт}

if (aNodeSize <= sizeof(pointer)) then

aNodeSize := sizeof(pointer) else

aNodeSize := ((aNodeSize + 3) shr 2) shl 2;

FNodeSize := aNodeSize;

{вычислить размер страницы (по умолчанию используется 1024 байта) и количество узлов на странице; если размер страницы по умолчанию недостаточен для размещения двух или большего количества узлов, выбрать размер страницы равным размеру узла}

FNodesPerPage := (PageSize - sizeof(pointer)) div aNodeSize;

if (FNodesPerPage > 1) then

FPageSize := PageSize

else begin

FNodesPerPage := 1;

FPagesize := aNodeSize + sizeof(pointer);

end;

end;

Код метода AllocNode очень прост. Если свободный список пуст, вызывается метод nmAllocNewPage, который распределяет новую страницу и вносит все ее узлы в свободный список. Если свободный список не пуст, из него выбирается первый узел (фактически с помощью процедуры удаления первого узла).

Листинг 3.3. Распределение памяти для узла в классе TtdNodeManager

function TtdNodeManager.AllocNode : pointer;

begin

{если свободный список пуст, распределить память для новой страницы; это приведет к заполнению свободного списка}

if (FFreeList = nil) then

nmAllocNewPage;

{вернуть первый элемент свободного списка}

Result := FFreeList;

FFreeList := PGenericNode(FFreeList)^.gnNext;

end;

Тип PGenericNode представляет собой запись с одним полем - полем для ссылки gnNext. Этот тип и преобразование типов в коде позволяет работать с узлами в списке свободных узлов на основании общего алгоритма - нечто похожее на тип TSimpleNode, который мы рассматривали ранее. Обратите внимание, конструктор гарантирует, что размер узлов, отслеживаемых диспетчером узлов, составляет, по крайней мере, 4 байта, т.е. размер указателя.

Следующий метод - FreeNode - ничуть не сложнее предыдущего. Он просто вставляет новый узел в начало свободного списка (используется код вставки перед первым узлом).

Листинг 3.4. Освобождение узла в классе TtdNodeManager

procedure TtdNodeManager.FreeNode(aNode : pointer);

begin

{вставить узел (если он не nil) в начало свободного списка}

if (aNode <> nil) then begin

PGenericNode(aNode)^.gnNext := FFreeList;

FFreeList := aNode;

end;

end;

Следующий метод, заслуживающий внимания, - nmAllocNewPage. Этот метод предназначен для распределения памяти объемом FpageSize, вычисленным в конструкторе Create, для новой страницы. Каждая страница содержит указатель и узлы FNodesPerPage. Указатель используется для создания связного списка страниц (именно поэтому конструктор учитывает в своих вычислениях значение sizeof(pointer)). Находящиеся на странице узлы вставляются в свободный список с помощью простого вызова FreeNode. Поскольку переменная NewPage объявлена как PAnsiChar, при выполнении простых операций с указателем для идентификации отдельных узлов на странице преобразование указателя в тип integer не требуется.

Поделиться:
Популярные книги

Моя на одну ночь

Тоцка Тала
Любовные романы:
современные любовные романы
короткие любовные романы
5.50
рейтинг книги
Моя на одну ночь

Черный Маг Императора 8

Герда Александр
8. Черный маг императора
Фантастика:
юмористическое фэнтези
попаданцы
аниме
5.00
рейтинг книги
Черный Маг Императора 8

Измена. Отбор для предателя

Лаврова Алиса
1. Отбор для предателя
Фантастика:
фэнтези
5.00
рейтинг книги
Измена. Отбор для предателя

Кодекс Крови. Книга II

Борзых М.
2. РОС: Кодекс Крови
Фантастика:
фэнтези
попаданцы
аниме
5.00
рейтинг книги
Кодекс Крови. Книга II

Шаг в бездну

Муравьёв Константин Николаевич
3. Перешагнуть пропасть
Фантастика:
фэнтези
космическая фантастика
7.89
рейтинг книги
Шаг в бездну

Часовая битва

Щерба Наталья Васильевна
6. Часодеи
Детские:
детская фантастика
9.38
рейтинг книги
Часовая битва

Вечная Война. Книга II

Винокуров Юрий
2. Вечная война.
Фантастика:
юмористическая фантастика
космическая фантастика
8.37
рейтинг книги
Вечная Война. Книга II

Хроники странного королевства. Вторжение. (Дилогия)

Панкеева Оксана Петровна
110. В одном томе
Фантастика:
фэнтези
9.38
рейтинг книги
Хроники странного королевства. Вторжение. (Дилогия)

Часовой ключ

Щерба Наталья Васильевна
1. Часодеи
Фантастика:
фэнтези
9.36
рейтинг книги
Часовой ключ

Инвестиго, из медика в маги

Рэд Илья
1. Инвестиго
Фантастика:
фэнтези
городское фэнтези
попаданцы
5.00
рейтинг книги
Инвестиго, из медика в маги

Кротовский, может, хватит?

Парсиев Дмитрий
3. РОС: Изнанка Империи
Фантастика:
попаданцы
альтернативная история
аниме
7.50
рейтинг книги
Кротовский, может, хватит?

Драконий подарок

Суббота Светлана
1. Королевская академия Драко
Любовные романы:
любовно-фантастические романы
7.30
рейтинг книги
Драконий подарок

Очешуеть! Я - жена дракона?!

Амеличева Елена
Фантастика:
юмористическая фантастика
5.43
рейтинг книги
Очешуеть! Я - жена дракона?!

Идеальный мир для Лекаря 9

Сапфир Олег
9. Лекарь
Фантастика:
боевая фантастика
юмористическое фэнтези
6.00
рейтинг книги
Идеальный мир для Лекаря 9