Это команда PDL :: Internalsp, которую можно запустить в провайдере бесплатного хостинга OnWorks, используя одну из наших многочисленных бесплатных онлайн-рабочих станций, таких как Ubuntu Online, Fedora Online, онлайн-эмулятор Windows или онлайн-эмулятор MAC OS.
ПРОГРАММА:
ИМЯ
PDL :: Internals - описание некоторых аспектов текущих внутренних компонентов
ОПИСАНИЕ
Главная
В этом документе объясняются различные аспекты текущей реализации PDL. Если ты просто
хотите использовать PDL для чего-то, вам определенно не нужно это читать. Даже если ты хочешь
для взаимодействия ваших подпрограмм C с PDL или создания новых функций PDL :: PP вам не нужно
прочтите эту страницу руководства (хотя она может быть информативной). Этот документ в первую очередь предназначен для
люди, заинтересованные в отладке или изменении внутреннего устройства PDL. Чтобы прочитать это, хороший
понимание языка C и программирования и структур данных в целом
требуется, а также некоторое понимание Perl. Если вы прочитаете этот документ и
понимают все это и могут указать, к чему относится любая часть этого документа в
Основные источники PDL и дополнительные попытки понять PDL :: PP, вы получите
название «PDL Guru» (конечно, текущая версия этого документа настолько неполна, что
это практически невозможно только из этих заметок).
Внимание! Если вам кажется, что этот документ устарел, сообщите об этом PDL.
список рассылки носильщиков ([электронная почта защищена]). Это вполне может случиться.
Piddles
Объект данных pdl обычно является непрозрачной скалярной ссылкой на структуру pdl в
объем памяти. В качестве альтернативы, это может быть ссылка на хэш с полем «PDL», содержащим
скалярная ссылка (это упрощает перегрузку piddles, см. PDL :: Objects). Вы легко можете
узнайте на уровне Perl, с каким типом piddle вы имеете дело. Пример кода
ниже показано, как это сделать:
# проверяем, не пидл ли это
умри "не пидл", если только UNIVERSAL :: isa ($ pdl, 'PDL');
# это скалярная ссылка или хеш-ссылка?
if (UNIVERSAL :: isa ($ pdl, "HASH")) {
die "недопустимый PDL", если не существует $ pdl -> {PDL} &&
УНИВЕРСАЛЬНЫЙ :: isa ($ pdl -> {PDL}, 'PDL');
print "Это ссылка на хэш",
"поле PDL содержит скалярную ссылку \ n";
} Еще {
print "Это скалярная ссылка, указывающая на адрес $$ pdl в памяти \ n";
}
Скалярная ссылка указывает на числовой адрес структуры C типа "pdl", которая является
определяется в пдл.ч. Отображение между объектом на уровне Perl и структурой C
содержащий фактические данные и структуру, составляющую piddle, выполняется PDL
typemap. Функции, используемые в карте типов PDL, определены в значительной степени в верхней части
файл pdlcore.h. Итак, как выглядит структура:
структура pdl {
unsigned long magicno; / * Всегда сохраняет PDL_MAGICNO как проверку работоспособности * /
/ * Это первое, поэтому большинство обращений к указателям неправильного типа перехватываются * /
int состояние; / * Что в этом pdl * /
pdl_trans * trans; / * Непрозрачный указатель на внутренности преобразования из
родитель * /
pdl_vaffine * vafftrans;
void * sv; / * (необязательно) указатель на исходный sv.
ВСЕГДА проверяйте наличие ненулевого значения перед использованием.
Мы не можем ссылаться на это, иначе мы бы
никогда не быть уничтоженным * /
void * datasv; / * Указатель на SV, содержащий данные. Refcnt inced * /
void * data; / * Null: для этого нет данных * /
PDL_Indx nval; / * Сколько значений выделено * /
int тип данных;
PDL_Indx * dims; / * Массив измерений данных * /
PDL_Indx * dimincs; / * Массив приращений данных по умолчанию * /
короткие ndims; / * Количество измерений данных * /
беззнаковый символ * threadids; / * Начальный индекс набора индексов потока n * /
беззнаковые char nthreadids;
pdl * прародитель; / * Я живу в мутировавшей семье. make_physical_now должен
скопируйте меня в новое поколение. * /
pdl * future_me; / * Я pdl "тогда", а это мое "сейчас" (или более современный
версия, в любом случае * /
pdl_children дети;
short living_for; / * Сторона Perl не указана; удали меня, когда * /
PDL_Indx def_dims [PDL_NDIMS]; / * Предварительно выделенное пространство для эффективности * /
PDL_Indx def_dimincs [PDL_NDIMS]; / * Предварительно выделенное пространство для эффективности * /
беззнаковый символ def_threadids [PDL_NTHREADIDS];
struct pdl_magic * magic;
void * hdrsv; / * "заголовок", настраиваемый извне * /
};
Это настоящая структура для хранения каких-то данных - что происходит?
Хранилище данных
Мы собираемся начать с некоторых более простых членов: прежде всего, это
член
void * datasv;
который на самом деле является указателем на структуру Perl SV («SV *»). Ожидается, что SV будет
представляющая строку, в которой данные piddle хранятся в плотно упакованном
форма. Этот указатель считается ссылкой на SV, поэтому счетчик ссылок был
увеличился, когда здесь был помещен "SV *" (этот подсчет ссылок должен делать
с механизмом сборки мусора Perl - не волнуйтесь, если это не имеет большого значения для
ты). Этот указатель может иметь значение "NULL", что означает, что нет
фактический Perl SV для этих данных - например, данные могут быть выделены "mmap"
операция. Обратите внимание, что использование SV * было чисто для удобства, это позволяет легко
преобразование упакованных данных из файлов в файлы. Другие реализации не
не входит.
Фактический указатель на данные хранится в члене
void * data;
который содержит указатель на область памяти с пространством для
PDL_Indx nval;
элементы данных типа данных этого piddle. PDL_Indx может быть либо long, либо long long
в зависимости от того, является ли ваш Perl 64-битным или нет.
Тип данных хранится в переменной
int тип данных;
значения для этого члена приведены в перечислении «pdl_datatypes» (см. пдл.ч).
В настоящее время у нас есть типы byte, short, unsigned short, long, float и double, см. Также
PDL :: Типы.
Габаритные размеры:
Количество измерений в piddle задается членом
целые числа;
который показывает, сколько записей в массивах
PDL_Indx * dims;
PDL_Indx * dimincs;
Эти массивы тесно связаны: «dims» дает размеры размеров, а
"dimincs" всегда рассчитывается по коду
PDL_Indx вкл = 1;
для (i = 0; i ndims; i ++) {
it-> dimincs [i] = inc; inc * = it-> dims [i];
}
в подпрограмме «pdl_resize_defaultincs» в «pdlapi.c». Это означает, что
dimincs можно использовать для вычисления смещения по коду, например
PDL_Indx выкл. = 0;
для (i = 0; i ndims; i ++) {
offs + = it-> dimincs [i] * index [i];
}
но это не всегда правильно, по крайней мере, без точной проверки
сначала.
Хранилище по умолчанию
Поскольку у подавляющего большинства точек не более 6 измерений, это более
эффективно иметь хранилище по умолчанию для размеров и уменьшений внутри PDL
структура.
PDL_Indx def_dims [PDL_NDIMS];
PDL_Indx def_dimincs [PDL_NDIMS];
"Dims" и "dimincs" могут быть установлены так, чтобы указывать на начало этих массивов, если
«ndims» меньше или равно константе времени компиляции «PDL_NDIMS». Это
важно отметить при освобождении структуры piddle. То же самое и с threadid:
беззнаковый символ def_threadids [PDL_NTHREADIDS];
магия
Можно прикрепить магию к лужам, как и собственный волшебный механизм Perl. Если
указатель на член
struct pdl_magic * magic;
не равно нулю, PDL имеет некоторую магию. Реализация магии может быть
почерпнутый из файла pdlmagic.c в раздаче.
Область
Одним из первых членов структуры является
int состояние;
Возможные флаги и их значения приведены в "pdl.h". В основном они используются для
реализовать ленивый механизм оценки и отслеживать узлы в этих
операций.
Преобразования и виртуальные аффинные преобразования
Как вы уже знаете, пидлы часто несут информацию о том, откуда они берутся.
из. Например, код
$ b = $ a-> slice ("2: 5");
$ b. = 1;
изменит $ a. Итак, $ b и $ a знают что они связаны через
«ломтик» - преобразование. Эта информация хранится в членах
pdl_trans * trans;
pdl_vaffine * vafftrans;
Оба $ a ( родителя) и $ b (дочерний элемент) хранят эту информацию о
преобразование в соответствующие слоты структуры «pdl».
"pdl_trans" и "pdl_vaffine" - это структуры, которые мы рассмотрим более подробно.
внизу.
Perl SV
Когда piddle упоминается через Perl SV, мы сохраняем дополнительную ссылку на него.
в члене
void * sv;
чтобы иметь возможность вернуть ссылку пользователю, когда он захочет проверить
структура преобразования на стороне Perl.
Также мы храним непрозрачные
void * hdrsv;
который предназначен только для использования пользователем для подключения произвольных данных к этому sv. Вот этот
обычно управляется вызовами sethdr и gethdr.
Smart Рекомендации и преобразования: нарезка и перетасовки
Реализованы интеллектуальные ссылки и большинство других фундаментальных функций, работающих с piddles.
с помощью преобразований (Как упоминалось выше), которые представлены типом "pdl_trans" в
ПДЛ.
Преобразование связывает элементы ввода и вывода и содержит всю инфраструктуру, которая
определяет, как
· Выходные точки получаются из входных точек
· Изменения в интеллектуально связанных piddles вывода (например, ребенок нарезанного родителя лужа)
возвращаются к входному piddle в преобразованиях, где это поддерживается (
здесь чаще всего используется пример «срез»).
· Получены тип данных и размер выходных piddles, которые необходимо создать
В общем, выполнение функции PDL для группы piddles приводит к созданию
преобразование запрошенного типа, связывающее все входные и выходные аргументы (как минимум
те, что являются пиддлами). В функциях PDL, поддерживающих поток данных между вводом и выводом
args (например, «срез», «индекс») это преобразование связывает родителя (ввод) и ребенок (выход)
piddles постоянно до тех пор, пока ссылка не будет явно разорвана запросом пользователя ("sever" at
уровень Perl) или все родители и дети были уничтожены. В тех случаях
преобразование выполняется с отложенной оценкой, например, выполняется только тогда, когда значения piddle на самом деле
доступ.
In не текущий функции, например сложение ("+") и внутренние продукты ("внутренний"),
преобразование устанавливается так же, как и в текущих функциях, но тогда преобразование
немедленно выполняется и уничтожается (разрыв связи между входными и выходными аргументами)
до того, как функция вернется.
Следует отметить, что тесная связь между входными и выходными аргументами текущей функции
(например, срез) требует, чтобы объекты piddle, которые связаны таким образом, оставались живыми
за пределами той точки, где они вышли за рамки с точки зрения Perl:
$ a = обнуляет(20);
$ b = $ a-> slice ('2: 4');
undef $ a; # последняя ссылка на $ a теперь уничтожена
Хотя $ a теперь должен быть уничтожен в соответствии с правилами Perl, лежащий в основе "pdl"
структура должна быть освобождена только тогда, когда $ b также выходит за пределы области видимости (поскольку она все еще
ссылается внутри на некоторые данные $ a). Этот пример демонстрирует, что такой поток данных
парадигма между объектами PDL требует специального алгоритма разрушения, который принимает
связи между piddles и связаны с продолжительностью жизни этих объектов. Затем на-
тривиальный алгоритм реализован в функции "pdl_destroy" в pdlapi.c. Фактически, большинство
кода в pdlapi.c и pdffamily.c заботится о том, чтобы piddles ("pdl
* "s) создаются, обновляются и освобождаются в нужное время в зависимости от взаимодействия с
другие piddles через преобразования PDL (помните, «pdl_trans»).
Доступ к дети и родители of a лужа
Когда piddles динамически связаны с помощью преобразований, как предложено выше, ввод и
выходные точки называются родителями и дочерними элементами соответственно.
Пример обработки дочерних элементов piddle предоставляется методом baddata из
PDL :: Bad (доступно только в том случае, если вы скомпилировали PDL с параметром "WITH_BADVAL", установленным на 1,
но все же полезно в качестве примера!).
Рассмотрим следующую ситуацию:
pdl> $ a = rval (7,7, Center => [3,4]);
pdl> $ b = $ a-> slice ('2: 4,3: 5');
pdl>? варс
Переменные PDL в пакете main ::
Имя Тип Размерность Состояние потока Mem
-------------------------------------------------- --------------
$ a Double D [7,7] P 0.38 КБ
$ b Double D [3,3] VC 0.00Kb
Теперь, если я вдруг решу, что $ a должен быть помечен как возможно содержащий недопустимые значения,
через
pdl> $ a->плохие данные(1)
тогда я хочу состояние $ b - это ребенок - также необходимо изменить (так как он будет либо
разделять или наследовать некоторые данные $ a, а также быть плохой), так что я получил четверку в Область
поле:
pdl>? варс
Переменные PDL в пакете main ::
Имя Тип Размерность Состояние потока Mem
-------------------------------------------------- --------------
$ a Double D [7,7] PB 0.38 КБ
$ b Double D [3,3] VCB 0.00 КБ
Это чудо выполняется функцией "propogate_badflag", которая указана ниже:
/ * newval = 1 означает установленный флаг, 0 означает его очистку * /
/ * за это спасибо Кристиану Соллеру * /
void propogate_badflag (pdl * it, int newval) {
PDL_DECL_CHILDLOOP (это)
PDL_START_CHILDLOOP (это)
{
pdl_trans * trans = PDL_CHILDLOOP_THISCHILD (это);
инт я;
for (i = trans-> vtable-> nparents;
я <транс-> vtable-> npdls;
i ++) {
pdl * child = trans-> pdls [i];
если (новое значение) дочерний-> состояние | = PDL_BADVAL;
иначе дочерний-> состояние & = ~ PDL_BADVAL;
/ * убедитесь, что мы распространяемся среди внуков и т. д. * /
propogate_badflag (дочерний, новый);
} / * для: i * /
}
PDL_END_CHILDLOOP (это)
} / * propogate_badflag * /
Учитывая piddle ("pdl * it"), процедура проходит через каждую структуру "pdl_trans", где
доступ к этой структуре обеспечивается макросом «PDL_CHILDLOOP_THISCHILD». В дети
piddle хранятся в массиве "pdls" после родители, следовательно, цикл от "i =
... nparents "на" i = ... nparents - 1 ". Получив указатель на дочерний piddle, мы
можем делать с ним то, что хотим; здесь мы меняем значение переменной "состояние", но
подробности не важны). Какие is важно то, что мы называем это "propogate_badflag"
piddle, чтобы убедиться, что мы перебираем его дочерние элементы. Эта рекурсия гарантирует, что мы доберемся до всех
Отпрыск определенного пидла.
Доступ к родители аналогична, с заменой цикла for на:
для (i = 0;
i <trans-> vtable-> nparents;
i ++) {
/ * что-то делать с родительским #i: trans-> pdls [i] * /
}
какой in a преобразование ("pdl_trans")
Все преобразования реализованы в виде структур.
структура XXX_trans {
int magicno; / * для обнаружения перезаписи памяти * /
короткие флажки; / * состояние транс * /
pdl_transvtable * vtable; / * все важные vtable * /
void (* freeproc) (struct pdl_trans *); / * Звоните, чтобы освободить транс
(на случай, если нам нужно было перебрать что-то для этой трансляции) * /
pdl * pdls [NP]; / * PDL, участвующие в преобразовании * /
int __datatype; / * тип преобразования * /
/ * в общем больше участников
/ * в зависимости от фактического преобразования (срез, добавление и т. д.)
*/
};
Преобразование идентифицирует все "PDL", участвующие в трансформации.
pdl * pdls [NP];
с "NP" в зависимости от количества аргументов-аргументов конкретной трансляции. Он записывает
состояние
короткие флажки;
и тип данных
интервал __тип данных;
трансформера (в который должны быть преобразованы все piddles, если они явно не типизированы, PDL
функции, созданные с помощью PDL :: PP, гарантируют, что эти преобразования выполняются по мере необходимости).
Наиболее важным является указатель на vtable (виртуальную таблицу), которая содержит фактические
функциональность
pdl_transvtable * vtable;
Структура vtable, в свою очередь, выглядит примерно так (слегка упрощенная из пдл.ч для
ясность)
typedef структура pdl_transvtable {
транстип pdl_transtype;
внутренние флаги;
int nparents; / * количество родительских PDL (ввод) * /
int npdls; / * количество дочерних PDL (вывод) * /
char * per_pdl_flags; / * флаги оптимизации * /
void (* redodims) (pdl_trans * tr); / * вычисляем тусклости детей * /
void (* прочитанные данные) (pdl_trans * tr); / * передать родителей детям * /
void (* writebackdata) (pdl_trans * tr); / * обратный поток * /
пустота (* freetrans) (pdl_trans * tr); / * Освобождаем как содержимое, так и его
транс-член * /
pdl_trans * (* копия) (pdl_trans * tr); / * Полная копия * /
int Structsize;
название символа; / * В основном для отладчиков * /
} pdl_transvtable;
Мы ориентируемся на функции обратного вызова:
void (* redodims) (pdl_trans * tr);
"redodims" будет определять размеры piddles, которые необходимо создать, и называется
из функции API, которая должна быть вызвана, чтобы гарантировать, что размеры
piddle доступны (pdlapi.c):
пусто pdl_make_physdims (pdl * it)
"readdata" и "writebackdata" отвечают за фактические вычисления дочернего элемента.
данные от родителей или данные о родителях от детей, соответственно (
аспект потока данных). Ядро PDL гарантирует, что они вызываются по мере необходимости, когда piddle
данные доступны (ленивая оценка). Общая функция API, гарантирующая, что piddle
актуально
пусто pdl_make_physvaffine (pdl * it)
который должен вызываться перед доступом к данным piddle из XS / C (см. Core.xs для некоторых
Примеры).
"freetrans" по мере необходимости освобождает динамически выделяемую память, связанную с трансмиссией, и
«копия» может копировать преобразование. Опять же, функции, созданные с помощью PDL :: PP, гарантируют, что
копирование и освобождение с помощью этих обратных вызовов происходит в нужное время. (Если они не сделают
что у нас произошла утечка памяти - это было в прошлом;).
Код преобразования и vtable вряд ли когда-либо написан вручную, а скорее сгенерирован
PDL :: PP из кратких описаний.
Определенные типы преобразований можно оптимизировать очень эффективно, устраняя необходимость в
явные методы "readdata" и "writebackdata". Эти преобразования называются
pdl_vaffine. Большинство функций управления измерениями (например, «slice», «xchg») принадлежат этому
класса.
Основная хитрость заключается в том, что родитель и потомок такого преобразования работают над одним и тем же.
(общий) блок данных, который они просто хотят интерпретировать по-разному (используя разные
«dims», «dimincs» и «offs» для одних и тех же данных, сравните структуру «pdl» выше). Каждый
таким образом, операция с одним piddle, разделяющим данные с другим, автоматически
перелетели от ребенка к родителю и обратно - ведь они читают и пишут одно и то же
блок памяти. В настоящее время это не является потокобезопасным Perl - нет большой потери, поскольку весь PDL
ядро не реентерабельно (Perl threading "! =" PDL threading!).
Подписи: нарезания резьбы за элементарный операции
Большая часть этой функциональности PDL threading (автоматическая итерация элементарных операций
over multi-dim piddles) реализован в файле pdlthread.c.
Функции, сгенерированные PDL :: PP (в частности, "readdata" и "writebackdata"
обратные вызовы) используют эту инфраструктуру, чтобы убедиться, что основная операция реализована
с помощью trans выполняется в соответствии с семантикой потоковой передачи PDL.
Определяющий new PDL Функции -- Клей код поколение
См. PDL :: PP и примеры в поставке PDL. Реализация и синтаксис
в настоящее время далеки от совершенства, но делает хорошую работу!
" Основные структура
Как обсуждалось в PDL :: API, PDL использует указатель на структуру, чтобы разрешить модулям PDL доступ к
его основные процедуры. Определение этой структуры («Ядро» структуры) находится в pdlcore.h
(сделано pdlcore.h.PL in Базовый / Ядро) и выглядит примерно так
/ * Структура для хранения указателей основных подпрограмм PDL для использования
* много модулей
*/
структура ядра {
Версия I32;
pdl * (* SvPDLV) (SV *);
void (* SetSV_PDL) (SV * sv, pdl * it);
#if defined (PDL_clean_namespace) || определено (PDL_OLD_API)
pdl * (* новый) (); / * заставить его работать с gimp-perl * /
#еще
pdl * (* pdlnew) (); / * переименован из-за конфликта C ++ * /
#endif
pdl * (* tmp) ();
pdl * (* create) (тип int);
void (* уничтожить) (pdl * it);
...
}
typedef структура Core Core;
Первое поле структуры («Версия») используется для обеспечения согласованности между модулями.
во время выполнения; следующий код помещается в раздел BOOT сгенерированного кода xs:
если (PDL-> Версия! = PDL_CORE_VERSION)
Perl_croak (aTHX_ "Foo необходимо перекомпилировать против недавно установленного PDL");
Если вы добавите новое поле в Основные struct вам следует:
· Обсудить это в списке рассылки pdl porters ([электронная почта защищена]) [с
возможность внесения изменений в отдельную ветвь дерева CVS, если это
изменение, на выполнение которого потребуется время]
· Увеличить на 1 значение переменной $ pdl_core_version в pdlcore.h.PL. Это устанавливает
значение макроса C "PDL_CORE_VERSION", используемого для заполнения поля версии
· Добавить документацию (например, к PDL :: API), если это «полезная» функция для внешнего модуля
писатели (а также обеспечение того, чтобы код был так же хорошо документирован, как и остальная часть PDL;)
Использование PDL :: Internalsp в Интернете с помощью сервисов onworks.net