Pull to refresh

Comments 13

Я Дельфи не трогал уже неизвестно сколько лет, поэтому вопрос, может быть, довольно наивный. Почему компоненты векторов и матриц хранятся в массиве? В Джаве, если бы мы перемножали такие матрицы и вектора, мы бы могли немало потерять на range checks во время доступа к компонентам. Многие библиотеки линейной алгебры обходятся просто именованными полями.

(в delphi) range checks обычно отключаются после отладки (если нужна производительность).
Пока вижу проблему только в том, что SetLength() в конструкторе выделяет несколько блоков памяти (это может быть медленно).

Потери на контроле диапазонов вообще незаметны на фоне динамического выделения памяти процедурой SetLength(), которая действительно приводит к некоторому замедлению. Это плата за универсальность, за возможность работать с матрицами произвольных размеров. Альтернативой может быть размещение элементов в статическом массиве с наперед заданными границами, взятыми с большим запасом. Основной минус такого подхода — очень неудобно смотреть в отладчике на массивы 20х20, состоящие почти из одних нулей.

Если нужна производительность, то, мне кажется, лучше хранить всё одним куском в памяти независимо от размеров и отдельно хранить размеры, сделать класс для каких-то базовых вещей вроде Create или извлечения элемента (это не так часто нужно, можно и с range check), а сами операции выполнять с помощью какой-нибудь OpenBLAS.

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

Это потому, что старые версии delphi (для которых, вероятно, писали код ваши коллеги) не поддерживали конструкторов для record'ов, перегрузки операторов, аннотаций, вот этого всего. Создавать объект класса, а потом освобождать его (.Free() ), было бы очень неудобно при использовании.


Вот это вот


TMatrix3x3 = array[1..3, 1..3] of Extended;

может размещаться на стеке, т.е. размещение выполняется очень быстро (просто изменение указателя стека), в отличие от вашего SetLength(a, m, n) (который ищет в куче (m+1) свободных блоков требуемого размера).
Если углубляться, доступ у "старой" версии тоже может быть чуть быстрее.


Но в целом, неплохо.

Не думали использовать вместо Extended тип с фиксированной запятой (свой) или BCD?
И почему для записей был выбран именно вариант constructor, а не function или procedure? Ведь для записей здесь не будет разницы (всегда есть не переопределяемый конструктор без параметров и выделение памяти осуществляется всегда). Да — всегда обновляй страницу — заметил, что вопрос задан, но ответом не доволен.
Альтернативы типу Extended для себя не вижу. Для моих задач важно при отладке видеть элементы «как есть», в численном представлении. С вещественными числами работать удобно.
Конструктор использовал для наглядности — мы же «класс» проектируем :)

Почему не double? Он вроде побыстрее должен работать (при меньшей точности, естественно).

Есть методы, сходимость которых обеспечивается высокой точностью вычислений. Поэтому Extended. При необходимости ускорить вычисления ценой потери точности, можно вызвать функцию Math.SetPrecision().
векторы, матрицы и пр — это всё частные случаи контейнеров. Нельзя в 21м веке писать контейнеры не используя шаблоны.
В самом деле — вектор может сожержать цедые числа, числа с плавающией запятой, двойной точности, extended, или наконец комплексные числа. И что на каждый тип элемента хранения писать свой новый класс и все сопутствующие алгоритмы??
Во-первых, шаблоны — штука полезная, но в Delphi их нет. Есть дженерики, а это совсем другая история. И в 21-ом веке пора бы их отличать. Дело в том, что над дженериками не определены арифметические операции, значит, вычислять на них нельзя.
Второй момент, матрицы — это не контейнеры, а математические объекты. Совсем разные понятия, лежащие в разных предметных областях. Контейнеры предназначены для хранения, для них определен стандартный интерфейс, куда должны входить методы «Добавить элемент» и «Удалить элемент». А матрицы применяются для решения систем уравнений и при рассмотрении линейных преобразований. Матрица — это частный случай тензора, а не контейнера.
К сожалению никак не могу сесть на XE7, приходится пока работать на Delphi2007, поэтому с дженериками знаком поверхносно.
Разве нельзя соорудить базовый класс с а весь набор арифметических операций вынести в отдельные private классы. А этот базовый при создании в зависимости от типа этого самого подключит нужный класс-реализацию? Для Extended свою, для целых свою и т.д.
При попытке использовать арифметические операции на параметризованном типе компилятор будет ругаться. Да и ни к чему это здесь. Серьёзно. Ценой производительности и удобства использования можно сделать всё, что угодно. Но всегда нужно исходить из имеющихся требований к разработке. Требования приведены в начале статьи.
Sign up to leave a comment.

Articles