gpt4 book ai didi

performance - 为什么 Raku 在使用多维数组时表现如此糟糕?

转载 作者:行者123 更新时间:2023-12-03 18:46:30 24 4
gpt4 key购买 nike

我很好奇为什么 Raku 在操作多维数组时表现如此糟糕。我在 Python、C# 和 Raku 中做了一个初始化二维矩阵的快速测试,而后者的耗用时间非常高。

对于乐

my @grid[4000;4000] = [[0 xx 4000] xx 4000];
# Elapsed time 42 seconds !!

对于 Python
table= [ [ 0 for i in range(4000) ] for j in range(4000) ]
# Elapsed time 0.51 seconds

C#
int [,]matrix = new int[4000,4000];
//Just for mimic same behaviour
for(int i=0;i<4000;i++)
for(int j=0;j<4000;j++)
matrix[i,j] = 0;
# Elapsed time 0.096 seconds

我做错了吗?好像差别太大了。

最佳答案

初步直接比较

我将从与您的 Python 代码比您自己的翻译更接近的代码开始。我认为与您的 Python 最直接等效的 Raku 代码是:

my \table = [ [ 0 for ^4000 ] for ^4000 ];
say table[3999;3999]; # 0

这段代码声明了一个无印记的标识符1。它:
  • 删除“塑造”( [4000;4000] 中的 my @table[4000;4000] )。我放弃它是因为你的 Python 代码没有这样做。整形具有优势,但会影响性能。2
  • 用途 binding而不是 assignment .我切换到绑定(bind)是因为您的 Python 代码正在执行绑定(bind),而不是赋值。 (Python 不区分两者。)虽然 Raku 的赋值方法为通用代码带来了值得拥有的基本优势,但它对性能有影响。3


  • 我开始回答的这段代码仍然很慢。

    首先,2018 年 12 月通过 Rakudo 编译器运行的 Raku 代码比使用 2019 年 6 月的 Python 解释器在相同硬件上的 Python 代码慢约 5 倍。3

    其次,Raku 代码和 Python 代码都很慢,例如与您的 C# 代码相比。我们可以做得更好...

    速度快一千倍的惯用替代方案

    以下代码值得考虑:
    my \table = [ [ 0 xx Inf ] xx Inf ];
    say table[ 100_000; 100_000 ]; # 0

    尽管此代码对应于名义上的 100亿元素数组而不是单纯的 1600万元素在您的 Python 和 C# 代码中,运行它的挂钟时间不到 Python 代码的一半,仅比 C# 代码慢 5 倍。这表明 Rakudo 运行 Raku 代码的速度是等效 Python 代码的一千多倍,是 C# 代码的一百倍。

    Raku 代码看起来要快得多,因为表是通过使用 xx Inf 懒惰地初始化的。 .4 唯一重要的工作是运行 say线。这会导致创建 100,000 个第一维数组,然后仅用 100,000 个元素填充第 100,000 个第二维数组,以便 say可以显示 0保存在该数组的最后一个元素中。

    有不止一种方法可以做到

    您的问题背后的一个问题是,总是有不止一种方法可以做到这一点。5如果您遇到速度很关键的代码性能不佳,那么像我所做的那样以不同的方式对其进行编码,可能会产生巨大的差异。6

    (另一个非常好的选择是问一个 SO 问题......)

    future

    Raku 被精心设计为高度可优化的,也就是说,如果 future 几年有足够的编译器工作,它有一天能够运行得更快,比说 Perl 5 或 Python 3 理论上可以运行,除非它们经过重新设计重新设计和多年相应的编译器工作。

    过去 25 年来 Java 的性能发生了什么事,这有点可以类比。 Rakudo/NQP/MoarVM 大约完成了 Java 堆栈已经经历的成熟过程的一半。

    脚注

    1 我可以写 my $table := ... .但声明形式为 my \foo ...消除对印记的考虑并允许使用 =而不是 :=这将需要带有 sigil'd 标识符。 (作为奖励,“削减印记”会产生无印记的标识符,许多不使用印记的语言(当然包括 Python 和 C#)的编码人员都很熟悉。)

    2 整形可能有一天会为某些代码带来更快的数组操作。与此同时,正如在对您的问题的评论中所提到的,它目前显然相反,显着减慢了速度。我想这在很大程度上是因为目前每个数组访问都被天真地动态边界检查,慢慢地一切都结束了,而且也没有努力使用固定大小来帮助加快速度。此外,当我试图为您的代码提出一种快速解决方法时,由于当前未实现对固定大小数组的许多操作,我未能找到使用固定大小数组的方法。同样,这些有望在某一天实现,但到目前为止,对于任何致力于实现它们的人来说,这可能不是一个足够的痛点。

    3 在撰写本文时, TIO从 2019 年 6 月开始使用 Python 3.7.4,从 2018 年 12 月开始使用 Rakudo v2018.12。随着时间的推移,Rakudo 的性能随着时间的推移显着提高,比官方 Python 3 解释器快得多,所以我预计最新的 Rakudo 和 Rakudo 之间的差距最新的 Python,当 Rakudo 速度较慢时,比本答案中所述的要窄得多。特别是,当前的工作显着提高了任务的绩效。

    4 xx默认为延迟处理,但由于语言语义或当前编译器的限制,某些表达式会强制进行急切求值。在旧版 v2018.12 Rakudo 中,为 [ [ foo xx bar ] xx baz ] 形式的表达式保持懒惰而不是被迫急切地评估,两者都是 barbaz必须是 Inf .相比之下, my \table = [0 xx 100_000 for ^100_000]懒惰不使用 Inf . (后面的代码实际上在第一个维度中存储了 100,000 Seq s 而不是 100,000 Array s -- say WHAT table[0] 显示 Seq 而不是 Array -- 但大多数代码都无法识别差异 -- say table[99_999;99_999] 仍会显示 0 。)

    5 有些人认为接受不止一种方式来思考和编写给定问题的解决方案是一种弱点。实际上,它至少在三个方面具有优势。首先,一般来说,任何给定的非平凡问题都可以通过许多不同的算法来解决,这些算法在性能方面存在巨大差异。这个答案包括一个已经存在一年的 Rakudo 已经可用的方法,在某些情况下,它在实践中比 Python 快一千多倍。其次,像 Raku 这样的故意灵活和多范式的语言允许编码员(或编码员团队)表达他们认为优雅和可维护的解决方案,或者只是完成工作,基于他们认为最好的,而不是什么语言强加。第三,乐堂作为优化编译器的性能目前显着变化。幸运的是,它有一个很棒的分析器6,因此可以看到瓶颈在哪里,并且具有很大的灵活性,因此可以尝试替代编码,这可能会产生更快的代码。

    6 当性能很重要时,或者如果您正在调查性能问题,请咨询 the Raku doc page on performance ;该页面涵盖了一系列选项,包括使用 Rakudo 分析器。

    关于performance - 为什么 Raku 在使用多维数组时表现如此糟糕?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59176954/

    24 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com