ThinkChat2.0新版上线,更智能更精彩,支持会话、画图、阅读、搜索等,送10W Token,即刻开启你的AI之旅 广告
## 26.基准 基准是一种度量。 你可以测量代码运行所需的时间。 所以,为什么这么重要? 随着你成为更认真的编码人员,完成一件工作就不再重要了。 重要的是你编写代码的程度以及代码在实时环境中的性能。 你必须知道编写快速运行的代码。 要检查你的代码片段是否比其他代码片段更快,可以使用基准测试。 以下面的示例为例,输入并运行 ```rb # benchmark.rb require 'benchmark' Benchmark.bm do|b| b.report("+= ") do a = "" 1_000_000.times { a += "." } end b.report("<< ") do a = "" 1_000_000.times { a << "." } end end ``` 输出量 ```rb user system total real += 55.030000 7.320000 62.350000 ( 62.303848) << 0.160000 0.000000 0.160000 ( 0.168452) ``` 因此,让我遍历代码,在`require 'benchmark'`行中,基准测试库作为 Ruby 标准发行版的一部分包含在内,因此你可以要求此代码在文件中不必大惊小怪。 现在让我们看一下这个块 ```rb Benchmark.bm do|b| …………. end ``` 它有什么作用? 首先,我们在`Benchmark`类中调用名为`bm`的函数,并在`do`和`end`之间传递一个块。 现在,让我们看看该块中的内容 ```rb Benchmark.bm do|b| b.report("+= ") do a = "" 1_000_000.times { a += "." } end b.report("<< ") do a = "" 1_000_000.times { a << "." } end end ``` 参见上面的代码。 我们正在使用`b.report("+= ")`准备报告,可以将将在输出中打印的任何字符串传递给报告函数。 如果查看输出的第二行`+= 55.030000 7.320000 62.350000 ( 62.303848)`,则会打印`+=`,因为`“+=”`已传递到`b.report()`。 `b.report()``打开一个代码块,你可以将任何需要基准标记的内容传递给该代码块。 在这里,我们传递了如下所示的代码片段 ```rb b.report("+= ") do a = "" 1_000_000.times { a += "." } end ``` 因此,我们为 a 分配了一个空字符串,并使用`+=`运算符对其添加了一百万次。 我们得到这个 ```rb user system total real += 55.030000 7.320000 62.350000 ( 62.303848) ``` 输出显示总共花费 62.35 秒,这是非常安静的。 现在让我们来看看第二个块 ```rb b.report("<< ") do a = "" 1_000_000.times { a << "." } end ``` 在这里,我们执行的操作与第一个相同,但是我们使用`&lt;&lt;`运算符而不是`+=`,这将产生以下输出,如下所示 ```rb user system total real += 55.030000 7.320000 62.350000 ( 62.303848) << 0.160000 0.000000 0.160000 ( 0.168452) ``` 因此,使用`&lt;&lt;`仅需 0.1685 秒,因此在字符串连接方面,`&lt;&lt;`远优于`+=`。 现在,让我们看看其他内容。 你们都知道计算机有内存。 当程序运行时,它需要记住事情并消耗一些内存,有时当可用内存变少时,Ruby 解释器将清理内存。 这称为垃圾收集 &lt;sup class="footnote"&gt;[ [62](#_footnotedef_62 "View footnote.") ]&lt;/sup&gt; 。 就像你的城市或市政当局收集垃圾一样,城市运行正常。 现在想想,如果不收集垃圾,而你遇到的垃圾会流到街道上,整个城市步履蹒跚,事情真的会变慢,那将会发生什么。 同样,如果程序运行足够长的时间可以更好地收集垃圾,否则,运行的新代码可能会变慢,并且如果对它进行基准测试,则可能会显示错误的结果。 现在,在文本编辑器中键入以下程序并运行它。 ```rb # benchmark_2.rb require 'benchmark' puts "Testing without cleaning up" Benchmark.bm do|b| b.report("+=") do a = "" 100_000.times { a += "." } end b.report("<<") do a = "" 1_000_000.times { a << "." } end end GC.start puts puts "Testing with cleaning up" Benchmark.bmbm do|b| b.report("+=") do a = "" 100_000.times { a += "." } end b.report("<<") do a = "" 100_000.times { a << "." } end end ``` Output ```rb Testing without cleaning up user system total real += 0.550000 0.220000 0.770000 ( 0.773730) << 0.150000 0.010000 0.160000 ( 0.159381) Testing with cleaning up Rehearsal -------------------------------------- += 0.520000 0.180000 0.700000 ( 0.687914) << 0.010000 0.010000 0.020000 ( 0.018958) ----------------------------- total: 0.720000sec user system total real += 0.530000 0.120000 0.650000 ( 0.650013) << 0.010000 0.000000 0.010000 ( 0.015668) ``` 如果你看到此代码产生的第一个基准测试 ```rb puts "Testing without cleaning up" Benchmark.bm do|b| b.report("+=") do a = "" 100_000.times { a += "." } end b.report("<<") do a = "" 1_000_000.times { a << "." } end end ``` 在此,我们使用`Benchmark.bm`并像往常一样运行它,它将生成以下输出: ```rb Testing without cleaning up user system total real += 0.550000 0.220000 0.770000 ( 0.773730) << 0.150000 0.010000 0.160000 ( 0.159381) ``` 基准总计分别为 0.77 和 0.16 秒。 在此块之后,我们有这些行 ```rb GC.start puts ``` 在这些行中,我们正在收集垃圾或释放该程序已使用的内存。 现在我们运行另一个由该代码块定义的基准 ```rb puts "Testing with cleaning up" Benchmark.bmbm do|b| b.report("+=") do a = "" 100_000.times { a += "." } end b.report("<<") do a = "" 100_000.times { a << "." } end end ``` 那么`Benchmark.bmbm`的作用是什么? 直到这次我们使用`Benchmark.bm`! 在上述代码中,我们有两个基准正在运行。 `bmbm`确保在完成第一个基准测试之后,进行垃圾回收并释放内存,以便下一个工作在垃圾回收环境中运行,从而获得更好的准确结果。 这是第二个此代码生成的输出 ```rb Testing with cleaning up Rehearsal -------------------------------------- += 0.520000 0.180000 0.700000 ( 0.687914) << 0.010000 0.010000 0.020000 ( 0.018958) ----------------------------- total: 0.720000sec ``` 如果你可以比较不带 GC 的`100_000.times { a &lt;&lt; "." }`的输出和带 GC 的`100_000.times { a &lt;&lt; "." }`的输出,分别为 0.16 秒和 0.02 秒。 现在,我想你会意识到垃圾收集的需要,无论是在城市中还是在编程中。