Ruby 为什么map比每一个都更有效?

Ruby 为什么map比每一个都更有效?,ruby,performance,Ruby,Performance,当你只有一把锤子时,一切看起来都像钉子。因此,在发现Arraymap和Arrayselect以及其他可移植方法的实用性、优雅性和语法乐趣之前,可以说Ruby中的Arrayeach方法。我好奇的是: 为什么在使用更精确的iterable方法时,性能会有实际的提高?一般来说这是真的吗 例如,在 需要“基准” 数组=1..100000.0到_a 衡量{ 100.5倍 array.map{| el | el.even?} 终止 } 衡量{ 100.5倍 新的_数组=[] array.each do|el

当你只有一把锤子时,一切看起来都像钉子。因此,在发现Arraymap和Arrayselect以及其他可移植方法的实用性、优雅性和语法乐趣之前,可以说Ruby中的Arrayeach方法。我好奇的是:

为什么在使用更精确的iterable方法时,性能会有实际的提高?一般来说这是真的吗

例如,在

需要“基准” 数组=1..100000.0到_a 衡量{ 100.5倍 array.map{| el | el.even?} 终止 } 衡量{ 100.5倍 新的_数组=[] array.each do|el|
新数组在两个例子的第二个例子中,每次迭代都有赋值。第一个例子没有赋值。

在两个例子的第二个例子中,每次迭代都有赋值。第一个例子没有赋值。

在两个例子中,第二段代码分配了100次与第一段代码一样多的内存。它还对数组执行大约log_1.5100的大小调整,假设一个增长因子为1.5的动态数组的标准教科书实现。调整数组大小的代价是分配一个新的内存块,然后将所有元素的一个副本复制到新的内存块中。更一般地说,是垃圾收藏家讨厌变异,他们在收集大量短命的小物体时比保存几个长寿命的大物体时效率要高得多


换句话说,在第一个示例中,您分别测量Arraymap和Arrayselect,而在第二个示例中,您不仅测量ArrayAch,还测量ArrayAch。在这两个示例中,第二段代码分配的内存是第一段代码的100倍。它还执行大约log_1.5100的大小调整假设一个增长因子为1.5的动态数组的标准教科书实现。调整数组大小的代价很高:分配一个新的内存块,然后将所有元素的一个副本复制到新的内存块中。更一般地说,垃圾收集器讨厌变异,它们在收集大量小的短期内存时效率更高d对象比保持几个大型长寿命对象的存活时间长


换句话说,在第一个例子中,分别测量ARRAYMAP和ARARESELL,而在第二个例子中,在分析任何算法时,不仅要测量数组,而且还要考虑数组

,我们主要考虑时间复杂度和空间复杂度。最重要的是设计不同的算法来执行相同的任务并返回相同的期望输出

让我们编写一个程序,执行相同的任务,在数组中迭代100次。仅此而已。不存储任何结果,因为我不确定您想要什么样的输出

下面是bench.rb文件的代码片段

我已经运行了这段代码3次,结果如下:

Output: 

Attempt 1:
0.548562   0.021844   0.570406 (  0.571088)
0.457079   0.000345   0.457424 (  0.457774)
0.516487   0.010758   0.527245 (  0.527843)

Attempt 2:
0.544863   0.021756   0.566619 (  0.568487)
0.458062   0.000514   0.458576 (  0.459249)
0.508665   0.010847   0.519512 (  0.520401)

Attempt 3:
0.583084   0.022554   0.605638 (  0.606023)
0.509447   0.000665   0.510112 (  0.511088)
0.548483   0.012212   0.560695 (  0.561534)


我可以看到Arrayeach是基于书面示例的明确的赢家。输出可以根据您的需求而变化,但是接地规则应该是相同的,算法应该返回相同的期望输出。

< P>在分析任何算法时,我们主要考虑时间复杂度和空间复杂度。在分析不同算法之前。为了解决一个特定的任务,首要的事情是设计不同的算法来执行相同的任务并返回相同的期望输出

让我们编写一个程序,执行相同的任务,在数组中迭代100次。仅此而已。不存储任何结果,因为我不确定您想要什么样的输出

下面是bench.rb文件的代码片段

我已经运行了这段代码3次,结果如下:

Output: 

Attempt 1:
0.548562   0.021844   0.570406 (  0.571088)
0.457079   0.000345   0.457424 (  0.457774)
0.516487   0.010758   0.527245 (  0.527843)

Attempt 2:
0.544863   0.021756   0.566619 (  0.568487)
0.458062   0.000514   0.458576 (  0.459249)
0.508665   0.010847   0.519512 (  0.520401)

Attempt 3:
0.583084   0.022554   0.605638 (  0.606023)
0.509447   0.000665   0.510112 (  0.511088)
0.548483   0.012212   0.560695 (  0.561534)


根据书面示例,我可以将ArrayAch视为明显的赢家。输出可能会根据您的要求有所不同,但基本规则应该是相同的,即算法应该返回相同的期望输出。

FWIW,在您的第二个示例中,在这两种情况下,新的_数组将比map和select返回的数组大100倍基准测试运行结束的时间,因为它不会在运行之间重置。不知道这是否解释了性能差异,但您可能需要检查。我认为我们可以得出结论,专门构建的方法总是比以特定方式使用的更通用的方法更快,或者至少不慢,原因很简单,一个选择对于前者的作者来说,最重要的是在后者上加上一个包装器,Ruby的核心方法的作者努力优化性能。我想有人可能会说,由于内存的考虑,一些核心方法可能不会针对速度进行优化,但它们仍然会针对某些性能指标进行优化,因此不会更糟相同的指标,比一般方法更合适。新的方法不应该吗
rray=[]是否在100.times块内以获得相同的结果?你目前正在比较两个不同的任务。哦!谢谢你的提醒。固定的FWIW,在这两种情况下的第二个示例中,新的_数组将比map和select返回的数组大100倍,因为它不会在运行之间重置。不知道这是否解释了性能差异,但您可能想检查一下。我认为我们可以得出结论,专门构建的方法总是比以特定方式使用的更通用的方法更快,或者至少不慢,原因很简单,前者的作者的一个选择是在后者上加上一个包装器,Ruby的核心方法编写者努力优化性能。我想有人可能会争辩说,出于内存考虑,某些核心方法可能不会针对速度进行优化,但它们仍然会针对某些性能指标进行优化,因此,按照相同的指标,它们不会比经过调整的常规方法更差。new_array=[]不应该在100.times块内以获得相同的结果吗?你目前正在比较两个不同的任务。哦!谢谢你的提醒。固定的这是一个非常简单的答案。请参阅另一个答案以获得一个好答案的示例。这是一个非常省力的答案。请参阅另一个答案,以获得一个好答案的示例。让我们编写一个执行相同任务的程序。给定的每段代码在示例中执行不同的任务。array.map{| el | el.even?}将检查数字是否为偶数,并输出一个大小为100000的新数组,其中包含真值和假值。array.each{| el | el.even?}将检查数字是否为偶数,仅此而已。array.select{| el | el.even?}将检查数字是否为偶数,并输出一个大小为50000且只有偶数的新数组。这些任务远不是相同的。是的。这是正确的,它定义了映射和选择之间的差异。在内部,它们的工作方式不同,这就是为什么它们的时间复杂度也会不同。我只是想用同样的逻辑来执行循环?以及不将结果存储在任何位置或基于输出执行任何进一步的逻辑。根据需求,这些方法出现在图中。让我们编写一个程序来执行相同的任务。在您的示例中,给定的每段代码执行不同的任务。array.map{| el | el.even?}将检查数字是否为偶数,并输出一个大小为100000的新数组,其中包含真值和假值。array.each{| el | el.even?}将检查数字是否为偶数,仅此而已。array.select{| el | el.even?}将检查数字是否为偶数,并输出一个大小为50000且只有偶数的新数组。这些任务远不是相同的。是的。这是正确的,它定义了映射和选择之间的差异。在内部,它们的工作方式不同,这就是为什么它们的时间复杂度也会不同。我只是想用同样的逻辑来执行循环?以及不将结果存储在任何位置或基于输出执行任何进一步的逻辑。根据需求,这些方法应运而生。