Raku :有没有一种超级快速的方法可以将数组转换为字符串而不用空格分隔元素?

lis*_*tor 8 arrays string binary raku

我需要将数千个二进制字节字符串(每个大约 1 兆字节长)转换为 ASC 字符串。这是我一直在做的,似乎太慢了:

sub fileToCorrectUTF8Str ($fileName) { # binary file
    my $finalString = "";
    my $fileBuf = slurp($fileName, :bin);    
    for @$fileBuf { $finalString = $finalString ~ $_.chr; };    
    return $finalString;
}
Run Code Online (Sandbox Code Playgroud)

~@b 将@b 转换为所有元素以空格分隔的字符串,但这不是我想要的。如果@b = <abcd>; ~@b 是“abc d”;但我只想要“abcd”,而且我想做得非常快。

那么,最好的方法是什么?我不能真正将 hyper 用于并行性,因为最终的字符串是按顺序构造的。或者我可以吗?

rai*_*iph 10

TL;DR在旧的 rakudo 上,.decode速度大约是其 100 倍。

以更长的形式匹配您的代码:

sub fileToCorrectUTF8Str ($fileName) { # binary file
  slurp($fileName, :bin).decode
}
Run Code Online (Sandbox Code Playgroud)

性能说明

首先,这是我为测试而写的:

# Create million and 1 bytes long file:
spurt 'foo', "1234\n6789\n" x 1e5 ~ 'Z', :bin;

# (`say` the last character to check work is done)
say .decode.substr(1e6) with slurp 'foo', :bin;

# fileToCorrectUTF8Str 'foo' );

say now - INIT now;
Run Code Online (Sandbox Code Playgroud)

在 TIO.run 的2018.12rakudo 上,上述内容的权.decode重约为.05每百万字节文件的5秒数,而不是您的解决方案的约秒数。

您当然可以/应该在您的系统上进行测试和/或使用更高版本的 rakudo。我希望差异保持在相同的顺序,但随着岁月的流逝,绝对时间会显着改善。[1]

为什么它快 100 倍?

嗯,首先,@Buf/Blob明确的力量乐观看昔日的单个项目一个缓冲区)作为复数的东西(一个列表又名元素的多个项目小号)。这意味着高级迭代,对于一百万个元素缓冲区,立即是一百万次高级迭代/操作,而不仅仅是一个高级操作。

其次,使用.decode不仅避免了迭代,而且每个文件只会导致相对较慢的方法调用开销,而在迭代时,每个文件可能会.chr调用一百万次。方法调用(至少在语义上)是后期绑定的,与例如调用而不是方法通常是早期绑定)相比,原则上成本相对较高。

那都是说:

  • 记住警告空[1]。例如,rakudo 的标准类生成方法缓存,编译器可能只是内联方法,因此方法调用方面的开销可能可以忽略不计。

  • 另请参阅文档的性能页面,尤其是使用现有的高性能代码

Buf.Str错误消息LTA

更新请参阅 Liz++ 的评论。

如果您尝试.Str在 a 上使用BufBlob(或等价物,例如~在其上使用前缀),您将得到一个异常。目前消息是:

Cannot use a Buf as a string, but you called the Str method on it
Run Code Online (Sandbox Code Playgroud)

a /目前的文档.StrBufBlob说:

为了转换为 Str,您需要使用.decode.

可以说是 LTA 错误消息没有暗示同样的事情。

再说一次,在决定对此采取什么措施之前,如果有的话,我们需要考虑人们可以从任何出错的事情中学习什么以及如何学习,包括有关它的信号,例如错误消息,以及他们在什么以及如何事实上,目前正在学习,并将我们的反应偏向于建立正确的文化和基础设施。

特别是,如果人们可以轻松地将他们看到的错误消息与对其进行详细说明的在线讨论联系起来,则需要考虑并鼓励和/或简化这一点。

例如,现在有这个 SO 用错误消息覆盖了这个问题,所以谷歌很可能会在这里找到人。依靠这一点可能比更改错误消息更合适。或者它可能不会。改变会很容易...

请考虑在下面发表评论和/或搜索现有的rakudo 问题,以查看是否Buf.Str正在考虑改进错误消息和/或您是否希望打开一个问题以建议对其进行更改。移动的每一块岩石至少都是很好的锻炼,并且随着我们的集体努力变得越来越明智,提高了(我们对山的看法)。

脚注

[1]正如众所周知的拉丁语“ Caveat Empty”所说的那样,任何特定 raku 功能的绝对和相对性能,更一般地说是任何特定代码,总是会因包括系统能力、其在运行期间的负载在内的因素而发生变化。运行代码,以及编译器完成的任何优化。因此,例如,如果您的系统是“空的”,那么您的代码可能运行得更快。或者,再举一个例子,如果你等待一到三年让编译器变得更快,那么 rakudo 性能的进步看起来仍然很有希望

  • 有几种方法可以进一步优化这一点。在本例中,“slurp”只是“IO::Path.slurp”的包装,因此调用“.IO.slurp”可以使我的基准测试速度提高约 2%。如果您使用 :enc&lt;latin1&gt;` 读取文件,您最终会得到一个字符缓冲区,就像使用默认 UTF-8 编码一样,但会跳过检查读取的文件是否实际上是有效的 UTF-8无论如何,您在解码为 UTF-8 字符串时都会这样做,这也使速度提高了约 10%。 (2认同)