Skip to content

Latest commit

 

History

History
104 lines (89 loc) · 10.3 KB

readme-ru.md

File metadata and controls

104 lines (89 loc) · 10.3 KB

Standard Generic Library (SGL) for Pascal

Почему появился это проект

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

Мне очень не нравиться предлагаемый режим обновления данных. Чтобы обновить данные мне нужно извлечь из коллекции помещённое в неё значение, обновить значение и потом обратно поместить изменённое значение. Это требует как минимум двух дополнительных операций копирования. Мы не можем передать элемент данных как var параметр в процедуру.

Для коллекции нет возможности поменять способ выделения памяти. Память для объектов и записей выделяется из одной общей кучи. После использования память надо аккуратно вернуть в кучу. Правильное освобождение памяти не всегда является тривиальной задачей и на неё тратится и время процессора, и время программиста на написание этого кода. В STL для всех видов коллекций можно указать свой аллокатор памяти.

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

Управление памятью на основе типизированных регионов памяти

Данная реализация коллекций опирается на механизм управления памятью на основе типизированных регионов памяти. Использование типизированных регионов памяти позволяет упростить решение ряда задач:

  • Код освобождения памяти. Задача освобождения памяти становиться проще и может быть выполнена гораздо быстрее.
  • Параллельное программирование. Общеизвестный факт - стандартный менеджер памяти должен быть потоко-защищённым. В любой момент времени только один поток может обращаться к менеджеру памяти. Выделение и освобождение памяти используют механизмы взаимного исключения и это не быстрая операция, особенно при значительной дефрагментации памяти. При использования отдельного типизированного региона памяти мы обращаемся к стандартном менеджеру памяти только в момент увеличения требуемой памяти и удаления структуры после её использования.

Стандартные структуры данных

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

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

В конечном счёте работать через указатели очень удобно и эффективно. Код становиться гораздо проще и лаконичнее. Однако, если нет опыта работы с указателями легко "выстрелить себе в ногу". Для любителей инкапсуляции, можно нужную структуру агрегировать в качестве приватного поля. Далее мы открываем только необходимую часть интерфейса путём переопределения в public секции нужных методов и свойств. Если поставить опцию inline мы избежим дополнительных затрат. Компилятор Дельфи не будет генерировать код для переопределённых методов. В месте вызова метода будет непосредственный вызов метода агрегированной структуры.

Generic списки и словари

  • TsgTuple<T1, ...> Generic Tuples
  • TsgArray<T> Generic Array with memory allocation from a shared memory region
  • TsgList<T> Generic List of Values
  • TsgRecordList<T> Generic List of Values accessed by pointer
  • TsgLinkedList<T> Generic Bidirectional Linked List
  • TsgForwardList<T> Generic Unidirectional Linked List
  • TsgHashMap<Key, T> Generic Unordered dictionary
  • TsgMap<Key, T> Generic Ordered Dictionary based on 2-3 tree
  • TsgSet<Key> Generic Set based on 2-3 trees

Нетипизированные структуры данных

  • TsgPointerArray Untyped List of Pointers
  • TsgPointerList Untyped List of Values accessed by pointer
  • TCustomLinkedList Untyped Bidirectional Linked List
  • TsgCustomTree Untyped Dictionary based on 2-3 trees

Итераторы

Мы начали добавление Delphi итераторов. Теперь мы можем использовать конструкцию for p in List do ; Самое интересное, для реализации итератора мы используем record и это работает! По сравнению с использованием объектов генерируемый код гораздо эффективнее и что приятно, нет обращений к куче, переменная для итератора располагается в стеке. Для меня это оказалось достаточно приятной неожиданностью!

Аллокатор памяти

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

Пул объектов

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

Если мы что-то перестаём использовать не всегда стоит удалять структуру или объект. Частенько объект приходится создавать повторно.