是否可以使用CompUnit模块来收集数据



>我开发了一个用于处理文档集合的模块。

该软件的一次运行会收集有关它们的信息。数据存储在两个结构中,称为%processed%symbols.需要缓存数据,以便在同一组文档上后续运行软件,其中一些文档可能会更改。(文档本身使用 CompUnit 模块进行缓存)。

目前,数据结构的存储/恢复如下:

# storing
'processed.raku`.IO.spurt: %processed.raku;
'symbols.raku`.IO.spurt: %symbol.raku;
# restoring
my %processed = EVALFILE 'processed.raku';
my %symbols = EVALFILE 'symbols.raku';

将这些结构输出到文件中(可能非常大)可能会很慢,因为哈希被解析以创建字符串化表单,并且输入速度很慢,因为它们正在重新编译。

它不用于检查缓存文件,仅用于保存软件运行之间的状态。

此外,虽然这对我的用例来说不是问题,但据我所知,这种技术通常不能使用,因为字符串化(序列化)不适用于 Raku 闭包。

我想知道是否可以使用 CompUnit 模块,因为它们用于存储模块的编译版本。因此,也许它们可以用来存储数据结构的"编译"或"内部"版本?

已经有办法做到这一点了吗?

如果没有,是否有任何技术原因可能是不可能的?

(很有可能你已经尝试过这个和/或它不适合你的用例,但我想我会提到它,以防万一它对你或其他发现这个问题的人有帮助。

您是否考虑过使用 JSON::Fast 将数据序列化到 JSON 或从 JSON 序列化数据? 它已针对(反)序列化速度进行了优化,这是基本字符串化没有/不可能的方式。 这不允许存储Block或其他闭包——正如你提到的,Raku 目前没有很好的方法来序列化它们。 但是,由于您提到这不是问题,因此 JSON 可能适合您的用例。

[编辑:正如您在下面指出的,这可能会使对某些 Raku 数据结构的支持更加困难。 通常(但并非总是)可以通过将数据类型指定为序列化步骤的一部分来解决此问题:

use JSON::Fast;
my $a = <a a a b>.BagHash;
my $json = $a.&to-json;
my BagHash() $b = from-json($json);
say $a eqv $b # OUTPUT: «True»

对于难以在 JSON 中表示的数据结构(例如具有非字符串键的数据结构),这会变得更加复杂。 JSON::Class 模块也可能有所帮助,但我还没有测试它的速度。

在查看了其他答案并查看了预编译的代码后,我意识到我最初的问题是基于误解。

Rakudo 编译器生成一个中间"字节码",然后在运行时使用该字节码。由于模块是用于编译目的的独立单元,因此可以对其进行预编译。可以缓存此中间结果,从而显着加快 Raku 程序的速度。

当 Raku 程序use已经编译的代码时,编译器不会再次编译它。

我曾认为预编译缓存是程序内部状态的一种存储,但事实并非如此。这就是为什么 - 我认为 - @ralph对这个问题感到困惑,因为我没有问正确的问题。

我的问题是关于数据的存储(和恢复)。JSON::Fast,正如@codesections所讨论的那样,非常快,因为它被 Rakudo 编译器在低级别使用,因此进行了高度优化。因此,在还原时重组数据将比还原本机数据类型更快,因为缓慢的速率确定步骤是从"磁盘"存储和还原,JSON 可以非常快地执行此操作。

有趣的是,我提到的 CompUnit 模块使用低级 JSON 函数,使 JSON::Fast 如此之快。

我现在正在考虑使用优化例程存储数据的其他方法,也许使用压缩/存档模块。这将归结为测试哪个最快。可能是 JSON 路由是最快的。

所以这个问题没有明确的答案,因为问题本身是"不正确的"。

更新正如@RichardHainsworth所指出的,我对他们的问题感到困惑,尽管觉得像我一样回答应该会有所帮助。根据他的反应,以及他不接受@codesection的回答的决定,当时这是唯一的另一个答案,我得出结论,最好删除这个答案,以鼓励其他人回答。但是现在理查德提供了一个提供了良好解决方案的答案,我正在取消删除它,希望现在更有用。

<小时 />

里拉;DR不使用EVALFILE,将数据存储在一个模块中,然后use。有一些简单的方法可以做到这一点,这些方法对EVALFILE进行了最小但有用的改进。有更复杂的方法可能会更好。

EVALFILE略有改进

我决定首先提出一个小的改进,这样你就可以巩固你从EVALFILE思维的转变。它在两个方面很小:

  • 实施只需几分钟。

  • 它只给你一个小小的改进EVALFILE.

我建议您在实际实现我在第一部分中描述的内容之前,正确考虑此答案的其余部分(它描述了更复杂的改进,可能会带来更大的回报,而不是这个小的回报)。我认为这个小小的改进可能会被证明是多余的,而不是作为通往后面部分的心理桥梁。


编写一个程序,比如store.raku,创建一个模块,比如data.rakumod

use lib '.';
my %hash-to-store = :a, :b;
my $hash-as-raku-code = %hash-to-store .raku;
my $raku-code-to-store = "unit module data; our %hash = $hash-as-raku-code";
spurt 'data.rakumod', $raku-code-to-store;

(我对.raku的使用当然过于简单化。以上只是一个概念证明。

这种写入数据的形式将具有与当前解决方案基本相同的性能,因此在这方面没有任何好处。


接下来,编写另一个程序,例如using.raku,使用它:

use lib '.';
use data;
say %data::hash; # {a => True, b => True}

use模块将需要编译它。因此,第一次使用这种方法而不是EVALFILE读取数据时,它不会更快,就像写入数据一样。但是对于后续读取来说,它应该要快得多。(直到您下次更改数据并且必须重新生成数据模块。

本节也不涉及闭包字符串化,这意味着您仍在执行可能不必要的数据写入阶段。

串化闭包;一个黑客

可以扩展上一节的方法,以包括闭包的字符串化。

你"只需要"访问包含闭包的源代码;使用正则表达式/解析来提取闭包;然后将匹配项写入数据模块。容易!;)

现在,我将跳过填写细节,部分原因是我再次认为这只是一个心理桥梁,并建议您继续阅读,而不是尝试按照我刚才描述的那样做。

使用复合单位

现在我们到达:

我想知道是否可以使用 CompUnit 模块,因为它们用于存储模块的编译版本。因此,也许它们可以用来存储数据结构的"编译"或"内部"版本?

我对你在这里问的问题有点困惑,原因有两个。首先,我认为您指的是文档("文档本身是使用 CompUnit 模块缓存的"),并且文档存储为模块。其次,如果您确实意味着文档存储为模块,那么为什么您不能存储您想要存储在其中的数据呢?您是否担心隐藏数据?

无论如何,我假设您询问的是将数据存储在文档模块中,并且您对"隐藏"该数据的方法感兴趣。


一个简单的选择是像我在第一部分中所做的那样编写数据,但在实际文档之后的末尾插入our %hash = $hash-as-raku-code";etc 代码,而不是在开头。

但也许这太丑/不够"隐藏"?


另一种选择可能是在文档模块的末尾添加带有 Pod 块配置数据的 Pod 块。

例如,将所有代码放入文档模块并放入say作为概念验证:

# ...
# Document goes here
# ...
# At end of document:
=begin data :array<foo bar> :hash{k1=>v1, k2=>v2} :baz :qux(3.14)
=end data
say $=pod[0].config<array>; # foo bar

也就是说,这只是在模块中执行的代码;我不知道模块的编译形式是否保留了配置数据。此外,您需要使用"Pod 加载器"(参见另一个 Raku 文件中的 Access pod)。但我的猜测是你对这些事情了如指掌。

同样,这可能不够隐藏,并且存在约束:

  • 数据只能是类型为StrIntNumBool文字标量,或以Array秒或Hash秒为单位的聚合。

  • 数据中不能有实际的换行符。(您可能有双引号字符串,其中有ns。

修改乐道

Aiui,假设 RakuAST 落地,编写可以使用 Raku 模块进行任意工作的 Rakudo 插件相对容易。这似乎是从 RakuAST 宏到基本is parsed宏的短跳,而这反过来又看起来像是提取源代码(例如闭包源)的短跳,因为它通过编译器,然后将其作为数据吐回编译的代码中,可能附加到 Pod 声明器块,而这些块又作为代码附加到代码。

所以,也许只是等待一两年,看看 RakuAST 是否登陆并获得您需要通过乐道做你需要做的事情的钩子?

相关内容

  • 没有找到相关文章

最新更新