Il s'agit de la commande PDL::Internalsp qui peut être exécutée dans le fournisseur d'hébergement gratuit OnWorks en utilisant l'un de nos multiples postes de travail en ligne gratuits tels que Ubuntu Online, Fedora Online, l'émulateur en ligne Windows ou l'émulateur en ligne MAC OS
PROGRAMME:
Nom
PDL::Internals - description de certains aspects des internes actuels
DESCRIPTION
Introduction
Ce document explique divers aspects de la mise en œuvre actuelle de PDL. Si vous venez de
voulez utiliser PDL pour quelque chose, vous n'avez certainement pas besoin de lire ceci. Même si tu veux
pour interfacer vos routines C avec PDL ou créer de nouvelles fonctions PDL::PP, vous n'avez pas besoin de
lisez cette page de manuel (bien qu'elle puisse être informative). Ce document est principalement destiné aux
les personnes intéressées par le débogage ou la modification des composants internes de PDL. Pour lire ceci, un bon
la compréhension du langage C et de la programmation et des structures de données en général est
requis, ainsi qu'une certaine compréhension de Perl. Si vous lisez ce document et
tout comprendre et sont capables d'indiquer à quoi se réfère n'importe quelle partie de ce document dans le
Les sources principales de PDL et en plus du mal à comprendre PDL::PP, vous recevrez le
titre "PDL Guru" (bien sûr, la version actuelle de ce document est tellement incomplète que
c'est presque impossible à partir de ces seules notes).
Mise en garde: S'il semble que ce document est obsolète, veuillez en informer le PDL
liste de diffusion des porteurs ([email protected]). Cela peut bien arriver.
Piddles
L'objet de données pdl est généralement une référence scalaire opaque dans une structure pdl dans
Mémoire. Alternativement, il peut s'agir d'une référence de hachage avec le champ "PDL" contenant le
référence scalaire (cela facilite la surcharge des piddles, voir PDL::Objects). Vous pouvez facilement
découvrez au niveau Perl à quel type de piddle vous avez affaire. L'exemple de code
ci-dessous montre comment le faire:
# vérifie s'il s'agit d'un piddle
die "pas un piddle" à moins que UNIVERSAL::isa($pdl, 'PDL');
# est-ce une référence scalaire ou une référence de hachage ?
if (UNIVERSAL::isa($pdl, "HASH")) {
die "pas un PDL valide" sauf si $pdl->{PDL} && existe
UNIVERSEL::isa($pdl->{PDL},'PDL');
print "Ceci est une référence de hachage,",
" le champ PDL contient la référence scalaire\n" ;
} Else {
print "Ceci est une référence scalaire qui pointe vers l'adresse $$pdl en mémoire\n" ;
}
La référence scalaire pointe vers l'adresse numérique d'une structure C de type "pdl" qui est
défini dans pdl.h. La correspondance entre l'objet au niveau Perl et la structure C
contenant les données réelles et structurelles qui composent un piddle est fait par le PDL
carte de type. Les fonctions utilisées dans le typemap PDL sont définies à peu près en haut de la
filet pdlcore.h. Alors à quoi ressemble la structure :
structure pdl {
magicno long non signé; /* Stocke toujours PDL_MAGICNO en tant que contrôle d'intégrité */
/* C'est d'abord ainsi la plupart des accès de pointeur au mauvais type sont interceptés */
état entier ; /* Qu'y a-t-il dans ce pdf */
pdl_trans *trans; /* Pointeur opaque vers les internes de la transformation de
parent */
pdl_vaffine *vafftrans;
vide* sv; /* (facultatif) pointeur vers le sv d'origine.
Vérifiez TOUJOURS s'il n'y a pas de valeur nulle avant utilisation.
Nous ne pouvons pas refcnt sur celui-ci ou nous aurions
ne jamais être détruit */
void *datasv; /* Pointeur vers SV contenant des données. Refcnt inced */
vide *données ; /* Null : aucune donnée allouée pour celui-ci */
PDL_Indx nvals ; /* Combien de valeurs allouées */
type de données entier ;
PDL_Indx * dims; /* Tableau des dimensions des données */
PDL_Indx *dimincs ; /* Tableau des incréments par défaut des données */
ndims courts ; /* Nombre de dimensions de données */
caractère non signé *threadids; /* Index de début de l'index de thread défini n */
nthreadids de caractères non signés ;
pdl *ancêtre; /* Je suis dans une famille mutée. make_physical_now doit
copiez-moi à la nouvelle génération. */
pdl *future_me; /* Je suis le "alors" pdl et c'est mon "maintenant" (ou plus moderne
version, de toute façon */
pdl_children enfants;
courte vie_pour; /* Côté Perl non référencé ; supprime moi quand */
PDL_Indx def_dims[PDL_NDIMS] ; /* Espace préalloué pour plus d'efficacité */
PDL_Indx def_dimincs[PDL_NDIMS] ; /* Espace préalloué pour plus d'efficacité */
caractère non signé def_threadids[PDL_NTHREADIDS] ;
struct pdl_magic *magie;
vide *hdrsv; /* "header", paramétrable de l'extérieur */
};
C'est toute une structure pour simplement stocker des données - que se passe-t-il ?
Stockage de données
Nous allons commencer par quelques-uns des membres les plus simples : tout d'abord, il y a le
membre
void *datasv ;
qui est en réalité un pointeur vers une structure Perl SV ("SV *"). Le SV devrait être
représentant une chaîne, dans laquelle les données du piddle sont stockées dans un
former. Ce pointeur compte comme une référence à la SV, donc le décompte de références a été
incrémenté lorsque le "SV *" a été placé ici (cette affaire de comptage de références doit faire
avec le mécanisme de récupération de place de Perl -- ne vous inquiétez pas si cela ne signifie pas grand-chose pour
tu). Ce pointeur peut avoir la valeur "NULL", ce qui signifie qu'il n'y a pas de
Perl SV réel pour ces données - par exemple, les données peuvent être allouées par un "mmap"
opération. Notez que l'utilisation d'un SV* était purement pratique, elle permet
transformation de données compressées à partir de fichiers en piddles. Les autres implémentations ne sont pas
exclu.
Le pointeur réel vers les données est stocké dans le membre
vide *données ;
qui contient un pointeur vers une zone mémoire avec un espace pour
PDL_Indx nvals ;
éléments de données du type de données de ce piddle. PDL_Indx est soit 'long' soit 'long long'
selon que votre perl est en 64 bits ou non.
Le type de données des données est stocké dans la variable
type de données entier ;
les valeurs de ce membre sont données dans l'énumération "pdl_datatypes" (voir pdl.h).
Actuellement, nous avons les types byte, short, unsigned short, long, float et double, voir aussi
PDL :: Types.
Dimensions
Le nombre de dimensions dans le piddle est donné par le membre
int ndims ;
qui montre combien d'entrées il y a dans les tableaux
PDL_Indx * dims;
PDL_Indx *dimincs ;
Ces tableaux sont intimement liés : "dims" donne les tailles des dimensions et
"dimincs" est toujours calculé par le code
PDL_Indx inc = 1 ;
pour(i=0; je ndims; i++) {
it->dimincs[i] = inc; inc *= it->dims[i];
}
dans la routine "pdl_resize_defaultincs" dans "pdlapi.c". Ce que cela signifie, c'est que le
dimincs peut être utilisé pour calculer le décalage par code comme
PDL_Indx désactivé = 0 ;
pour(i=0; je ndims; i++) {
offs += it->dimincs[i] * index[i];
}
mais ce n'est pas toujours la bonne chose à faire, du moins sans vérifier certains
les choses d'abord.
Stockage par défaut
Puisque la grande majorité des piddles n'ont pas plus de 6 dimensions, il est plus
efficace d'avoir un stockage par défaut pour les dimensions et les dimincs à l'intérieur du PDL
structure.
PDL_Indx def_dims[PDL_NDIMS] ;
PDL_Indx def_dimincs[PDL_NDIMS] ;
Les "dims" et "dimincs" peuvent être définis pour pointer vers le début de ces tableaux si
"ndims" est inférieur ou égal à la constante de compilation "PDL_NDIMS". C'est
important de noter lors de la libération d'une structure piddle. Il en va de même pour les threadids :
caractère non signé def_threadids[PDL_NTHREADIDS] ;
la magie
Il est possible d'attacher de la magie aux piddles, un peu comme le propre mécanisme magique de Perl. Si
le pointeur de membre
struct pdl_magic *magie;
est différent de zéro, le PDL a une certaine magie qui s'y rattache. La mise en œuvre de la magie peut être
tiré du fichier pdlmagic.c dans la répartition.
Région
L'un des premiers membres de la structure est
état entier ;
Les drapeaux possibles et leurs significations sont donnés dans "pdl.h". Ceux-ci sont principalement utilisés pour
mettre en œuvre le mécanisme d'évaluation paresseux et garder une trace des piddles dans ces
opérations.
Transformations et transformations affines virtuelles
Comme vous devez déjà le savoir, les piddles contiennent souvent des informations sur leur provenance
de. Par exemple, le code
$b = $a->slice("2:5");
$b .= 1 ;
modifiera $a. Donc $b et $a savoir qu'ils sont connectés via un
"tranche"-transformation. Ces informations sont stockées dans les membres
pdl_trans *trans;
pdl_vaffine *vafftrans;
Les deux $a (le mère) et $b (l'enfant) stockent ces informations sur le
transformation en slots appropriés de la structure "pdl".
"pdl_trans" et "pdl_vaffine" sont des structures que nous allons voir plus en détail
ci-dessous.
Les SV Perl
Lorsque des piddles sont référencés via des SV Perl, nous stockons une référence supplémentaire à celui-ci
dans le membre
vide* sv;
afin de pouvoir renvoyer une référence à l'utilisateur lorsqu'il souhaite inspecter le
structure de transformation côté Perl.
Aussi, nous stockons un opaque
vide *hdrsv;
qui est juste à l'usage de l'utilisateur pour connecter des données arbitraires avec ce sv. Celui-ci
est généralement manipulé via les appels sethdr et gethdr.
Smart et métamorphoses : tranchage et découper
Des références intelligentes et la plupart des autres fonctions fondamentales fonctionnant sur des piddles sont implémentées
via transformations (Acomme mentionné ci-dessus) qui sont représentés par le type "pdl_trans" dans
PDL.
Une transformation relie les piddles d'entrée et de sortie et contient toute l'infrastructure qui
définit comment
· les piddles de sortie sont obtenus à partir des piddles d'entrée
· des changements dans les piddles de sortie intelligemment liés (par exemple, le enfant d'un tranché mère faire pipi)
sont renvoyés au piddle d'entrée dans les transformations où cela est pris en charge (le
l'exemple le plus souvent utilisé étant "slice" ici).
· le type de données et la taille des piddles de sortie qui doivent être créés sont obtenus
En général, l'exécution d'une fonction PDL sur un groupe de piddles entraîne la création d'un
transformation du type demandé qui lie tous les arguments d'entrée et de sortie (au moins
ceux qui sont des piddles). Dans les fonctions PDL qui prennent en charge le flux de données entre l'entrée et la sortie
args (par exemple "slice", "index") cette transformation lie mère (entrée) et enfant (production)
piddles en permanence jusqu'à ce que le lien soit explicitement rompu par la demande de l'utilisateur ("sever" à
le niveau Perl) ou tous les parents et enfants ont été détruits. Dans ces cas le
la transformation est évaluée paresseux, par exemple exécutée uniquement lorsque les valeurs de piddle sont réellement
accédé.
In non coulant fonctions, par exemple l'addition ("+") et les produits internes ("inner"), le
la transformation est installée comme dans les fonctions fluides mais alors la transformation est
immédiatement exécuté et détruit (rupture du lien entre les arguments d'entrée et de sortie)
avant le retour de la fonction.
Il convient de noter que le lien étroit entre les arguments d'entrée et de sortie d'une fonction fluide
(comme slice) nécessite que les objets piddle qui sont liés de telle manière soient maintenus en vie
au-delà du point où ils sont devenus hors de portée du point de vue de Perl :
$une = zéros(20);
$b = $a->slice('2:4');
undef $a; # la dernière référence à $a est maintenant détruite
Bien que $a doive maintenant être détruit selon les règles de Perl, le "pdl" sous-jacent
la structure ne doit en fait être libérée que lorsque $b sort également de la portée (puisqu'il reste
référence en interne certaines des données de $a). Cet exemple montre qu'un tel flux de données
paradigme entre les objets PDL nécessite un algorithme de destruction spécial qui prend le
les liens entre les piddles tiennent compte et couplent la durée de vie de ces objets. Le non-
algorithme trivial est implémenté dans la fonction "pdl_destroy" dans pdlapi.c. En fait, la plupart
du code dans pdlapi.c et pdlfamily.c est soucieux de s'assurer que les piddles ("pdl
*"s) sont créés, mis à jour et libérés au bon moment en fonction des interactions avec
d'autres piddles via des transformations PDL (rappelez-vous, "pdl_trans").
Accès les enfants et parents of a faire pipi
Lorsque les piddles sont liés dynamiquement via des transformations comme suggéré ci-dessus, entrez et
les piddles de sortie sont respectivement appelés parents et enfants.
Un exemple de traitement des enfants d'un piddle est fourni par la méthode "baddata" de
PDL::Bad (uniquement disponible si vous avez compilé PDL avec l'option "WITH_BADVAL" définie sur 1,
mais toujours utile comme exemple!).
Considérez la situation suivante :
pdl> $a = rvals(7,7,Centre=>[3,4]);
pdl> $b = $a->slice('2:4,3:5');
pdl> ? vars
Variables PDL dans le package principal : :
Nom Type Dimension Flux État Mem
-------------------------------------------------- --------------
$a Double D [7,7] P 0.38 Ko
$b Double D [3,3] CV 0.00Kb
Maintenant, si je décide soudainement que $a doit être signalé comme contenant peut-être de mauvaises valeurs,
en utilisant
pdl> $a->données erronées(1)
alors je veux l'état de $b - c'est enfant - à changer également (puisqu'il sera soit
partager ou hériter de certaines données de $a et ainsi être aussi mauvais), pour que j'obtienne un « B » dans le Région
champ:
pdl> ? vars
Variables PDL dans le package principal : :
Nom Type Dimension Flux État Mem
-------------------------------------------------- --------------
$a Double D [7,7] PB 0.38 Ko
$b Double D [3,3] VCB 0.00 Ko
Ce peu de magie est effectué par la fonction "propogate_badflag", qui est répertoriée ci-dessous :
/* newval = 1 signifie définir l'indicateur, 0 signifie l'effacer */
/* merci à Christian Soeller pour cela */
void propogate_badflag( pdl *it, int newval ) {
PDL_DECL_CHILDLOOP(le)
PDL_START_CHILDLOOP(le)
{
pdl_trans *trans = PDL_CHILDLOOP_THISCILD(le);
int i;
for( i = trans->vtable->nparents;
i < trans->vtable->npdls ;
je++ ) {
pdl *enfant = trans->pdls[i];
if ( newval ) child->state |= PDL_BADVAL;
sinon enfant->état &= ~PDL_BADVAL;
/* assurez-vous que nous propageons aux petits-enfants, etc */
propogate_badflag( enfant, newval );
} /* pour : je */
}
PDL_END_CHILDLOOP(le)
} /* propager_badflag */
Étant donné un piddle ("pdl *it"), la routine parcourt chaque structure "pdl_trans", où
l'accès à cette structure est fourni par la macro "PDL_CHILDLOOP_THISCHILD". Les les enfants
du piddle sont stockés dans le tableau "pdls", après le parents, d'où la boucle de "i =
...nparents" à "i = ...nparents - 1". Une fois que nous avons le pointeur vers le piddle enfant, nous
on peut en faire ce qu'on veut; ici on change la valeur de la variable "state", mais le
les détails sont sans importance). Quoi is important est que nous appelions "propogate_badflag" sur ce
piddle, pour s'assurer que nous parcourons ses enfants. Cette récursivité garantit que nous arrivons à tous les
Progéniture d'un piddle particulier.
L'accès à la parents est similaire, avec la boucle "for" remplacée par :
pour( je = 0;
i < trans->vtable->nparents ;
je++ ) {
/* faire des trucs avec le parent #i : trans->pdls[i] */
}
Ce qui est in a transformation ("pdl_trans")
Toutes les transformations sont implémentées en tant que structures
structure XXX_trans {
int magicno; /* pour détecter les écrasements de mémoire */
drapeaux courts; /* état du trans */
pdl_transvtable *vtable; /* la toute importante vtable */
void (*freeproc)(struct pdl_trans *); /* Appel pour libérer ce trans
(au cas où nous devions mallocer des trucs pour cette trans) */
pdl *pdls[NP]; /* Les pdls impliqués dans la transformation */
int __type de données ; /* le type de la transformation */
/* en général plus de membres
/* en fonction de la transformation réelle (slice, add, etc)
*/
};
La transformation identifie tous les "pdl" impliqués dans la trans
pdl *pdls[NP] ;
avec "NP" en fonction du nombre d'arguments piddle du trans particulier. Il enregistre un
Etat
drapeaux courts;
et le type de données
int __type de données ;
du trans (en lequel tous les piddles doivent être convertis à moins qu'ils ne soient explicitement tapés, PDL
fonctions créées avec PDL::PP s'assurent que ces conversions sont effectuées si nécessaire).
Le plus important est le pointeur vers la vtable (table virtuelle) qui contient le
pdl_transvtable *vtable;
La structure vtable ressemble à son tour à quelque chose comme (légèrement simplifié de pdl.h pour
clarté)
typedef struct pdl_transvtable {
pdl_transtype transtype ;
indicateurs entiers ;
int parents; /* nombre de pdls parents (entrée) */
int npdls; /* nombre de pdls enfants (sortie) */
char *per_pdl_flags; /* indicateurs d'optimisation */
void (*redodims)(pdl_trans *tr); /* comprendre les dims des enfants */
void (*readdata)(pdl_trans *tr); /* flux parents vers enfants */
void (*writebackdata)(pdl_trans *tr); /* couler en arrière */
void (*freetrans)(pdl_trans *tr); /* Libère à la fois le contenu et celui-ci de
le membre trans */
pdl_trans *(*copie)(pdl_trans *tr); /* Copie complète */
taille de structure int ;
char *nom; /* Pour les débogueurs, principalement */
} pdl_transvtable ;
Nous nous concentrons sur les fonctions de rappel :
void (*redodims)(pdl_trans *tr);
"redodims" déterminera les dimensions des piddles qui doivent être créés et s'appelle
à partir de la fonction API qui doit être appelée pour s'assurer que les dimensions d'un
piddle sont accessibles (pdlapi.c):
annuler pdl_make_physdims(pdl *it)
"readdata" et "writebackdata" sont responsables des calculs réels de l'enfant
les données des parents ou les données des parents de celles des enfants, respectivement (le
aspect flux de données). Le noyau PDL s'assure que ceux-ci sont appelés au besoin lorsque piddle
les données sont accédées (évaluation paresseuse). La fonction API générale pour s'assurer qu'un piddle est
à jour est
void pdl_make_physvaffine(pdl *it)
qui doit être appelé avant d'accéder aux données de piddle depuis XS/C (voir Core.xs pour certains
exemples).
"freetrans" libère la mémoire allouée dynamiquement associée au trans selon les besoins et
"copy" peut copier la transformation. Encore une fois, les fonctions construites avec PDL::PP s'assurent que
la copie et la libération via ces rappels se produisent au bon moment. (S'ils ne parviennent pas à faire
que nous avons une fuite de mémoire -- cela s'est produit dans le passé ;).
Le code de transformation et de vtable n'est presque jamais écrit à la main mais plutôt généré par
PDL::PP à partir de descriptions concises.
Certains types de transformations peuvent être optimisés de manière très efficace en évitant le besoin de
méthodes explicites "readdata" et "writebackdata". Ces transformations sont appelées
pdl_vaffine. La plupart des fonctions de manipulation de dimension (par exemple, "slice", "xchg") appartiennent à cette
classe.
L'astuce de base est que le parent et l'enfant d'une telle transformation travaillent sur le même
bloc de données (partagé) qu'ils choisissent simplement d'interpréter différemment (en utilisant différents
"dims", "dimincs" et "offs" sur les mêmes données, comparez la structure "pdl" ci-dessus). Chaque
l'opération sur un piddle partageant des données avec un autre de cette manière est donc automatiquement
volé d'enfant à parent et retour - après tout, ils lisent et écrivent la même chose
bloc de mémoire. Ce n'est actuellement pas sûr pour les threads Perl -- pas de grosse perte puisque l'ensemble de la PDL
core n'est pas réentrant (threading Perl "!=" threading PDL!).
signatures: filetage plus de élémentaire
La plupart des fonctionnalités du threading PDL (itération automatique des opérations élémentaires
sur des piddles multi-dim) est implémenté dans le fichier pdlthread.c.
Les fonctions générées par PDL::PP (en particulier les "readdata" et "writebackdata"
rappels) utilisent cette infrastructure pour s'assurer que l'opération fondamentale implémentée
par le trans est effectuée en accord avec la sémantique de threading de PDL.
Définir nouvelle PDL fonctions -- Colle code génération
Veuillez consulter PDL::PP et des exemples dans la distribution PDL. L'implémentation et la syntaxe sont
actuellement loin d'être parfait mais il fait du bon travail !
Votre Core struct
Comme discuté dans PDL::API, PDL utilise un pointeur vers une structure pour permettre aux modules PDL d'accéder à
ses routines de base. La définition de cette structure (la struct "Core") est en pdlcore.h
(créé par pdlcore.h.PL in De base/de base) et ressemble à quelque chose comme
/* Structure pour contenir les pointeurs des routines PDL de base afin d'être utilisé par
* de nombreux modules
*/
struct Noyau {
Version I32 ;
pdl* (*SvPDLV) (SV*);
void (*SetSV_PDL) ( SV *sv, pdl *it );
#if défini(PDL_clean_namespace) || défini(PDL_OLD_API)
pdl* (*nouveau) ( ); /* le faire fonctionner avec gimp-perl */
#else
pdl* (*pdlnouveau) ( ); /* renommé à cause d'un conflit C++ */
#endif
pdl* (*tmp) ( );
pdl* (*create) (type int);
void (*détruire) (pdl *it);
}
typedef struct Noyau Noyau;
Le premier champ de la structure ("Version") est utilisé pour assurer la cohérence entre les modules
à l'exécution; le code suivant est placé dans la section BOOT du code xs généré :
si (PDL->Version != PDL_CORE_VERSION)
Perl_croak(aTHX_ "Foo doit être recompilé avec le PDL nouvellement installé");
Si vous ajoutez un nouveau champ au Core structure, vous devez :
· en discuter sur la liste de diffusion des porteurs pdl ([email protected]) [avec le
possibilité d'apporter vos modifications à une branche distincte de l'arborescence CVS s'il s'agit d'un
changement qui prendra du temps à se terminer]
· augmenter de 1 la valeur de la variable $pdl_core_version dans pdlcore.h.PL. Cela définit
la valeur de la macro C "PDL_CORE_VERSION" utilisée pour renseigner le champ Version
· ajouter de la documentation (par exemple à PDL::API) si c'est une fonction "utile" pour le module externe
rédacteurs (en plus de s'assurer que le code est aussi bien documenté que le reste de PDL ;)
Utilisez PDL::Internalsp en ligne à l'aide des services onworks.net