PDL::Internalsp - 云端在线

这是 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
搬运工电子邮件列表(pdl-porters@jach.hawaii.edu)。 这很可能发生。

皮德尔斯
pdl 数据对象通常是对 pdl 结构的不透明标量引用
记忆。 或者,它可能是一个带有包含“PDL”字段的散列引用
标量引用(这使得重载 piddles 变得容易,请参阅 PDL::Objects)。 你可以轻松
在 Perl 级别找出您正在处理的 piddle 类型。 示例代码
下面演示了如何做到这一点:

#检查这是否是一个piddle
除非 UNIVERSAL::isa($pdl, 'PDL');
# 是标量引用还是散列引用?
如果(通用::isa($ pdl,“哈希”)){
死“不是有效的 PDL”,除非存在 $pdl->{PDL} &&
通用::isa($pdl->{PDL},'PDL');
打印“这是一个散列引用”,
" PDL 字段包含标量引用\n";
} {
print "这是一个标量引用,指向内存中的 $$pdl 地址\n";
}

标量引用指向“pdl”类型的 C 结构的数字地址,它是
定义于 下载文件. Perl层面的对象与C结构体的映射
包含构成 piddle 的实际数据和结构由 PDL 完成
类型图。 PDL 类型映射中使用的函数几乎定义在
文件 内核文件. 那么结构是什么样的:

结构 pdl {
无符号长magicno; /* 始终将 PDL_MAGICNO 存储为完整性检查 */
/* 这是第一个,因此大多数对错误类型的指针访问都会被捕获 */
内部状态; /* 这个 pdl 里有什么 */

pdl_trans *trans; /* 指向内部转换的不透明指针
父母 */

pdl_vaffine *vafftrans;

无效* SV; /*(可选)指向原始 sv 的指针。
使用前始终检查非空值。
我们不能提及这个,否则我们会
永远不会被破坏 */

无效 *datasv; /* 指向包含数据的 SV 的指针。 引用 */
无效*数据; /* 空:没有为此分配数据 */
PDL_Indx nvals; /* 分配了多少个值 */
int 数据类型;
PDL_Indx *暗淡; /* 数据维度数组 */
PDL_Indx *dimincs; /* 数据默认增量数组 */
短 ndim; /* 数据维数 */

无符号字符 *threadids; /* 线程索引集 n 的起始索引 */
无符号字符 nthreadids;

pdl *祖; /* 我在一个变异的家庭中。 make_physical_now 必须
把我复制到新一代。 */
pdl *future_me; /* 我是“当时”的 pdl,这是我的“现在”(或更现代的
版本,无论如何*/

pdl_children 儿童;

短暂的生活_for; /* 未引用 Perl 端; 删除我 */

PDL_Indx def_dims[PDL_NDIMS]; /* 预分配空间以提高效率 */
PDL_Indx def_dimincs[PDL_NDIMS]; /* 预分配空间以提高效率 */
无符号字符 def_threadids[PDL_NTHREADIDS];

结构 pdl_magic *magic;

无效 * hdrsv; /* "header", 可从外部设置 */
};

这是一个仅用于存储一些数据的结构 - 发生了什么?

数据存储
我们将从一些更简单的成员开始:首先,有
会员

无效 *datasv;

这实际上是一个指向 Perl SV 结构(“SV *”)的指针。 预计 SV
表示一个字符串,其中 piddle 的数据存储在一个紧密包装的字符串中
形式。 此指针计为对 SV 的引用,因此引用计数已
当“SV *”放在这里时增加(这个引用计数业务要做
使用 Perl 的垃圾收集机制——如果这对你来说意义不大,请不要担心
你)。 允许该指针具有值“NULL”,这意味着没有
此数据的实际 Perl SV - 例如,数据可能由“mmap”分配
手术。 请注意,使用 SV* 纯粹是为了方便,它允许轻松
将打包数据从文件转换为 piddles。 其他实现不
排除在外。

实际指向数据的指针存储在成员中

无效*数据;

它包含一个指向具有空间的内存区域的指针

PDL_Indx nvals;

此 piddle 的数据类型的数据项。 PDL_Indx 是“long”或“long long”
取决于您的 perl 是否为 64 位。

数据的数据类型存储在变量中

int 数据类型;

该成员的值在枚举“pdl_datatypes”中给出(参见 下载文件).
目前我们有 byte、short、unsigned short、long、float 和 double 类型,另见
PDL::类型。

尺寸
piddle中的维数由成员给出

内部结构;

它显示了数组中有多少条目

PDL_Indx *暗淡;
PDL_Indx *dimincs;

这些数组密切相关:“dims”给出了维度的大小和
“dimincs”始终由代码计算

PDL_Indx 公司 = 1;
for(i=0; i ndims; 我++) {
it->dimincs[i] = inc; inc *= it->dims[i];
}

在“pdlapi.c”的例程“pdl_resize_defaultincs”中。 这意味着
dimincs 可用于通过代码计算偏移量,如

PDL_Indx 关闭 = 0;
for(i=0; i ndims; 我++) {
offs += it->dimincs[i] * index[i];
}

但这并不总是正确的做法,至少在没有确定的情况下
事情第一。

默认存储
由于绝大多数 piddles 的维度不超过 6 个,因此更多
有效地为 PDL 中的维度和 diminc 提供默认存储
结构。

PDL_Indx def_dims[PDL_NDIMS];
PDL_Indx def_dimincs[PDL_NDIMS];

“dims”和“dimincs”可以设置为指向这些数组的开头,如果
“ndims”小于或等于编译时常量“PDL_NDIMS”。 这是
释放 piddle 结构时要注意的重要一点。 这同样适用于 threadid:

无符号字符 def_threadids[PDL_NTHREADIDS];

魔术
可以将魔法附加到 piddles 上,就像 Perl 自己的魔法机制一样。 如果
成员指针

结构 pdl_magic *magic;

非零,PDL 附加了一些魔法。 魔术的实现可以是
从文件中收集 pdlmagic.c 在分布中。

州/领地
该结构的第一批成员之一是

内部状态;

“pdl.h”中给出了可能的标志及其含义。 这些主要用于
实施惰性评估机制并跟踪这些
操作。

变换和虚拟仿射变换
正如您应该已经知道的那样,piddles 通常会携带有关它们来往何处的信息
从。 例如,代码

$b = $a->slice("2:5");
$b .= 1;

将改变 $a。 所以 $b 和 $a 知道 他们通过一个连接
“切片”-转换。 此信息存储在成员中

pdl_trans *反式;
pdl_vaffine *vafftrans;

两个 $a ( ) 和 $b (孩子) 存储有关
在“pdl”结构的适当插槽中进行转换。

“pdl_trans”和“pdl_vaffine”是我们将更详细地研究的结构
联络一位教师

Perl SV
当通过 Perl SV 引用 piddles 时,我们存储对它的附加引用
在成员

无效* SV;

为了能够在用户想要检查时将引用返回给用户
Perl 端的转换结构。

此外,我们存储一个不透明的

无效 * hdrsv;

仅供用户使用此 sv 连接任意数据。 这个
通常通过 sethdr 和 gethdr 调用进行操作。

智能 引用 转换: 切片 切块
实现了智能引用和大多数其他在 piddles 上运行的基本功能
通过 转换 (如上所述)由类型“pdl_trans”表示
PDL。

转换将输入和输出连接起来并包含所有基础设施
定义如何

· 输出piddles从输入piddles中获得

· 智能链接输出的变化(例如 孩子 切片的 提琴)
在支持的转换中飞回输入通道(
最常用的例子是这里的“切片”)。

· 获取需要创建的输出piddles的数据类型和大小

通常,在一组 piddles 上执行 PDL 函数会导致创建一个
链接所有输入和输出参数的请求类型的转换(至少
那些是小玩意儿)。 在支持输入和输出之间数据流的 PDL 函数中
args(例如“slice”、“index”)这个转换链接 (输入)和 孩子 (output)
永久闲置,直到链接被用户请求明确破坏(“sever” at
Perl 级别)或所有父子节点都已被销毁。 在这些情况下
转换是惰性求值的,例如仅在实际值是 piddle 值时才执行
访问。

In 不流动 函数,例如加法 ("+") 和内积 ("inner"),
转换就像在流动函数中一样安装,但是转换是
立即执行并销毁(断开输入和输出参数之间的链接)
在函数返回之前。

应该注意的是,流动函数的输入和输出参数之间的紧密联系
(如切片)要求以这种方式链接的 piddle 对象保持活动状态
从 Perl 的角度来看,它们超出了范围:

$a = (20);
$b = $a->slice('2:4');
undef $a; # 最后对 $a 的引用现在被销毁

尽管 $a 现在应该根据 Perl 的规则销毁,但底层的“pdl”
实际上只有当 $b 也超出范围时才必须释放结构(因为它仍然
在内部引用 $a 的一些数据)。 这个例子演示了这样一个数据流
PDL 对象之间的范式需要一种特殊的销毁算法,该算法采用
将 piddles 之间的联系考虑在内,并结合这些对象的寿命。 非
微不足道的算法在函数“pdl_destroy”中实现 pdlapic.c. 事实上,大多数
中的代码 pdlapic.cpdlfamily.c 关心的是确保 piddles ("pdl
*"s) 在正确的时间被创建、更新和释放,具体取决于与
通过 PDL 转换的其他 piddles(请记住,“pdl_trans”)。

访问 孩子 父母 of a iddle
当 piddles 通过上面建议的转换动态链接时,输入和
输出 piddles 分别称为父级和子级。

处理 piddle 的孩子的一个例子是由“baddata”方法提供的
PDL::Bad(仅当您编译 PDL 时“WITH_BADVAL”选项设置为 1 时才可用,
但作为例子仍然有用!)。

考虑以下情况:

pdl> $a = rvals(7,7,Centre=>[3,4]);
pdl> $b = $a->slice('2:4,3:5');
pdl> ? 变量
包 main 中的 PDL 变量::

名称 类型 维度 Flow State Mem
-------------------------------------------------- --------------
$a 双 D [7,7] P 0.38Kb
$b 双 D [3,3] VC 0.00Kb

现在,如果我突然决定将 $a 标记为可能包含错误值,
运用

pdl> $a->坏数据(1)

然后我想要 $b 的状态 - 它是 孩子 - 也要改变(因为它要么
共享或继承 $a 的一些数据,因此也是 ),所以我在 州/领地
领域:

pdl> ? 变量
包 main 中的 PDL 变量::

名称 类型 维度 Flow State Mem
-------------------------------------------------- --------------
$a 双 D [7,7] PB 0.38Kb
$b 双 D [3,3] VCB 0.00Kb

这一点魔法是由“propogate_badflag”函数执行的,如下所示:

/* newval = 1 表示设置标志,0 表示清除 */
/* 感谢 Christian Soeller 为此 */

无效propogate_badflag(pdl *it,int newval){
PDL_DECL_CHILDLOOP(它)
PDL_START_CHILDLOOP(它)
{
pdl_trans *trans = PDL_CHILDLOOP_THISCHILD(它);
int i;
for( i = trans->vtable->nparents;
i <trans->vtable->npdls;
我++ ) {
pdl *child = trans->pdls[i];

if ( newval ) child->state |= PDL_BADVAL;
else child->state &= ~PDL_BADVAL;

/* 确保我们传播给孙子等 */
propogate_badflag(孩子,新瓦尔);

} /* 为我 */
}
PDL_END_CHILDLOOP(它)
/* propogate_badflag */

给定一个 piddle ("pdl *it"),例程循环遍历每个 "pdl_trans" 结构,其中
对该结构的访问由“PDL_CHILDLOOP_THISCHILD”宏提供。 这 孩子
的 piddle 存储在“pdls”数组中,在 父母,因此循环从“i =
...nparents" 到 "i = ...nparents - 1"。一旦我们有了指向 child piddle 的指针,我们
可以为所欲为; 这里我们改变了“state”变量的值,但是
细节不重要)。 什么 is 重要的是,我们在此调用“propogate_badflag”
piddle,以确保我们遍历它的孩子。 这种递归确保我们得到所有
一个特定的琴弦。

访问 父母 类似,将“for”循环替换为:

对于(我= 0;
i <trans->vtable->nparents;
我++ ) {
/* 与父级#i 一起做事:trans->pdls[i] */
}

什么是 in a 改造 (“pdl_trans”)
所有转换都实现为结构

结构 XXX_trans {
int magicno; /* 检测内存覆盖 */
短旗; /* 传输状态 */
pdl_transvtable *vtable; /* 所有重要的 vtable */
void (*freeproc)(struct pdl_trans *); /* 调用释放这个传输
(以防我们不得不为这个 trans 分配一些东西)*/
pdl *pdls[NP]; /* 参与转换的 pdls */
int __数据类型; /* 转换类型 */
/* 一般来说更多的成员
/* 取决于实际的转换(切片、添加等)
*/
};

转换标识了转换中涉及的所有“pdl”

pdl *pdls[NP];

“NP”取决于特定反式的 piddle args 的数量。 它记录了一个


短旗;

和数据类型

int __数据类型;

trans 的(除非显式键入,否则所有 piddles 都必须转换为它,PDL
使用 PDL::PP 创建的函数确保根据需要完成这些转换)。
最重要的是指向包含实际数据的 vtable(虚拟表)的指针
功能

pdl_transvtable *vtable;

vtable 结构又看起来像(从 下载文件
明晰)

typedef 结构 pdl_transvtable {
pdl_transtype 转换类型;
整数标志;
国际父母; /* 父 pdls 的数量(输入)*/
int npdls; /* 子 pdls 的数量(输出)*/
字符 *per_pdl_flags; /* 优化标志 */
void (*redodims)(pdl_trans *tr); /* 找出孩子的点数 */
void (*readdata)(pdl_trans *tr); /* 将父进程传给子进程 */
void (*writebackdata)(pdl_trans *tr); /* 向后流动 */
void (*freetrans)(pdl_trans *tr); /* 释放内容和它
跨性别成员*/
pdl_trans *(*copy)(pdl_trans *tr); /* 完整复制 */
整数结构大小;
字符 *名称; /* 对于调试器,主要是 */
pdl_transvtable;

我们专注于回调函数:

无效 (*redodims)(pdl_trans *tr);

“redodims”将计算出需要创建并被调用的 piddles 的尺寸
从应该调用的 API 函数中以确保
piddle 是可访问的(pdlapic.c):

无效 pdl_make_physdims(pdl *it)

“readdata”和“writebackdata”负责孩子的实际计算
分别来自父母的数据或来自子女的父母数据(
数据流方面)。 PDL 核心确保在 piddle 时根据需要调用这些
数据被访问(惰性评估)。 确保 piddle 的通用 API 函数
最新的是

无效 pdl_make_physvaffine(pdl *it)

在从 XS/C 访问 piddle 数据之前应该调用它(参见 核心文件 对于一些
例子)。

“freetrans”根据需要释放与 trans 关联的动态分配的内存,并且
“复制”可以复制转换。 同样,使用 PDL::PP 构建的函数确保
通过这些回调复制和释放发生在正确的时间。 (如果他们做不到
我们有内存泄漏——这在过去发生过;)。

转换和 vtable 代码几乎不是手工编写的,而是由
PDL::PP 来自简明的描述。

可以非常有效地优化某些类型的转换,从而无需
明确的“readdata”和“writebackdata”方法。 这些转换被称为
pdl_vaffine. 大多数维度操作函数(例如,“slice”、“xchg”)都属于这个
类。

基本技巧是这种转换的父母和孩子在同一个
(共享)数据块,他们只是选择以不同的方式解释(通过使用不同的
相同数据上的“dims”、“dimincs”和“offs”,比较上面的“pdl”结构)。 每个
因此,以这种方式与另一个共享数据的 piddle 上的操作是自动的
从孩子飞到父母再飞回来——毕竟他们读和写的都是一样的
内存块。 这目前不是 Perl 线程安全的——因为整个 PDL 没有太大的损失
核心不可重入(Perl 线程“!= PDL 线程!”)。

签名: 穿线 超过 初级 操作
PDL 线程的大部分功能(基本操作的自动迭代)
over multi-dim piddles)在文件中实现 线程.

PDL::PP 生成的函数(特别是“readdata”和“writebackdata”
回调)使用这个基础设施来确保基本操作的实现
由 trans 执行与 PDL 的线程语义一致。

定义 PDL 功能 -- 胶水
请参阅 PDL::PP 和 PDL 发行版中的示例。 实现和语法是
目前远非完美,但它做得很好!

核心 结构
正如 PDL::API 中所讨论的,PDL 使用一个指向结构的指针来允许 PDL 模块访问
它的核心程序。 此结构(“核心”结构)的定义在 内核文件
(由...制作 pdlcore.h.PL in 基本/核心) 看起来像

/* 保存指针核心 PDL 例程的结构,以便由
* 多个模块
*/
结构核心{
I32 版本;
pdl* (*SvPDLV) (SV*);
无效 (*SetSV_PDL) (SV *sv, pdl *it);
#if 已定义(PDL_clean_namespace) || 定义(PDL_OLD_API)
pdl* (*new) ( ); /* 让它与 gimp-perl 一起工作 */
的#else
pdl* (*pdlnew) ( ); /* 由于 C++ 冲突而重命名 */
#ENDIF
pdl* (*tmp) ( );
pdl* (*create) (int 类型);
void (*销毁) (pdl *it);
...
}
typedef struct 核心核心;

结构体的第一个字段(“Version”)用于保证模块之间的一致性
在运行时; 下面的代码放在生成的xs代码的BOOT部分:

如果(PDL->版本!= PDL_CORE_VERSION)
Perl_croak(aTHX_ "Foo 需要针对新安装的 PDL 重新编译");

如果您添加一个新字段到 核心 结构你应该:

· 在 pdl 搬运工电子邮件列表上讨论它(pdl-porters@jach.hawaii.edu) [与
对 CVS 树的单独分支进行更改的可能性,如果它是
需要时间才能完成的更改]

· 将 $pdl_core_version 变量的值增加 1 pdlcore.h.PL. 这一套
用于填充版本字段的“PDL_CORE_VERSION”C 宏的值

· 如果它是外部模块的“有用”功能,则添加文档(例如到 PDL::API)
编写者(以及确保代码与 PDL 的其余部分一样有据可查;)

使用 onworks.net 服务在线使用 PDL::Internalsp



最新的 Linux 和 Windows 在线程序