5

我正在尝试在 Ruby 中快速/高效地实现 Mandelbrot。很久很久以前,加速它的一种方法是使用定点整数而不是浮点数。

所以我做了以下基准,使用乘法或平方**操作数将浮点数和整数提升到平方比较。

require 'benchmark'

Benchmark.bmbm(10) do |x|  
  x.report("float-multip") do
    for z in 0..100000 
      zf = z.to_f
      y = zf*zf
    end
  end  

  x.report("float-square") do
    for z in 0..100000 
      zf = z.to_f
      y = zf**2
    end
  end  

  x.report("int-multip") do
    zo = 0
    for zi in 0..100000 
      y2 = zo*zo
      zo += 1
    end
  end   

  x.report("int-multip") do
    for zi in 0..100000 
      y2 = zi**2
    end
  end  
end

这会生成以下输出:

Rehearsal ------------------------------------------------
float-multip   0.125000   0.000000   0.125000 (  0.125000)
float-square   0.125000   0.000000   0.125000 (  0.125000)
int-multip     0.250000   0.000000   0.250000 (  0.250000)
int-multip     0.282000   0.000000   0.282000 (  0.282000)
--------------------------------------- total: 0.782000sec

                   user     system      total        real
float-multip   0.110000   0.000000   0.110000 (  0.110000)
float-square   0.125000   0.000000   0.125000 (  0.125000)
int-multip     0.219000   0.016000   0.235000 (  0.235000)
int-multip     0.265000   0.015000   0.280000 (  0.282000)

这清楚地表明 Fixnum 乘法几乎是浮点数的两倍。

我有两个问题:

  • 谁能解释一下?我可以想象的一个原因是 Fixnum 乘法较慢,因为内部检查是否需要将其转换为 Bignum。
  • 其次,红宝石是否有一个快速的整数乘法?
4

4 回答 4

5

我想到了几件事。您没有指定您正在使用的 Ruby 实现。由于您在 Windows 上运行 Ruby 1.8.6,我将假设您正在使用通过 Windows One-Click Installer 安装的 MRI。

这是一种最坏的情况:

  1. MRI 是所有 Ruby 实现中最慢的
  2. Windows 上的 MRI甚至比 Linux 或 OSX 上的 MRI还要慢
  3. One-Click Installer 使用来自 Ruby-Lang.Org 的预编译二进制文件,这些二进制文件从 1996 年开始使用 Microsoft Visual C++ 6.0 编译,因此比使用 Microsoft Visual C++ 10.0 或 GCC 4.x 编译的 Windows 上的 MRI甚至更慢甚至 GCC 3.x。

以下是您可以尝试提高性能的一些提示:

  • 使用RubyInstaller项目,它使用使用 GCC 3.x 而不是 MSVC6 编译的解释器,
  • 也许自己重新编译解释器( RubyInstaller项目提供的 Rakefile 并不难)使用 GCC 4.x 和/或不同的优化选项(RubyInstaller 使用适度的优化选项和通用 386 CPU 编译),
  • 使用比 1.8.6 更新的 MRI 版本,
  • 使用不同的 Ruby 实现:

    • YARV 比 MRI 快得多(不幸的是,它只实现了 Ruby 1.9,因此您可能需要更改代码),
    • JRuby在很多场景中都比 YARV 快得多,并且它同时实现了 Ruby 1.8 和 Ruby 1.9(它还有一个-fast命令行选项,它与 Ruby 略有不兼容,但提高了性能,包括算术性能)和
    • IronRuby也可能比 YARV 更快,具体取决于工作负载。

在后两种情况下,您可能需要稍微修改一下基准。两者最终都可以将 Ruby 代码编译为本机机器码,但这可能需要一段时间。例如,JRuby 在方法执行 20 次后编译为 JVM 字节码,HotSpot Server 在执行 20000 次后将 JVM 字节码编译为本机机器码。此外,编译本身需要时间,因此程序需要运行一段时间才能通过提高性能来收回成本。

特别是,JRuby 首席开发人员之一 Charles Oliver Nutter 表示,根据工作负载,JRuby 可能需要 5-15 秒才能加速到全速。您的基准测试速度快了大约 100 倍(这是您每天都不会听到的一句话……)。

于 2009-11-25T02:42:12.377 回答
3

1.8.6 只是在这方面比较慢。1.8.7 稍微好一点,1.9.1 做得更好。我不能说为什么,但 rvm 同意你和 Pavel 的观点,即 1.8.6 慢得奇怪。

1.8.6:
排练------------------------------------------------
浮点乘法 0.140000 0.000000 0.140000 (0.141560)
浮点平方 0.150000 0.000000 0.150000 ( 0.146286)
整数倍数 0.220000 0.000000 0.220000 (0.223255)
整数倍数 0.180000 0.000000 0.180000 (0.183850)
--------------------------------------- 总计:0.690000 秒

1.8.7:
排练------------------------------------------------
浮点乘法 0.090000 0.000000 0.090000 (0.092346)
浮点平方 0.080000 0.000000 0.080000 ( 0.080335)
整数倍数 0.070000 0.000000 0.070000 (0.068012)
整数倍数 0.080000 0.000000 0.080000 (0.081713)
--------------------------------------- 总计:0.320000 秒

1.9.1:
排练------------------------------------------------
浮点乘法 0.070000 0.000000 0.070000 (0.065532)
浮点平方 0.080000 0.000000 0.080000 ( 0.081620)
整数倍数 0.060000 0.000000 0.060000 (0.065371)
整数倍数 0.070000 0.000000 0.070000 (0.065761)
--------------------------------------- 总计:0.280000 秒
于 2009-11-24T21:02:30.220 回答
0

jruby 可能有更快的算术(或 1.9.x),也可以在 C 中执行(例如:http ://segment7.net/projects/ruby/inline_optimization.html )显然有助于提高速度

于 2011-04-14T17:32:25.027 回答
0

我无法解释你的桌子。但我可以解释一下我的(ruby 1.8.7):

                   user     system      total        real
float-multip   0.600000   0.000000   0.600000 (  0.612311)
float-square   0.650000   0.000000   0.650000 (  0.649399)
int-multip     0.450000   0.010000   0.460000 (  0.457004)
int-multip     0.690000   0.000000   0.690000 (  0.692879)

哎呀。整数乘法优于浮点乘法。

由于你的处理器比我的慢 5 倍(我将你的基准测试中的重复次数增加了 10 倍),所以肯定有一些与 ruby​​ 无关的东西。

**运算可能使用了浮点运算(exp(x*ln(2)),所以它和其他浮点运算一样慢。

于 2009-11-24T18:29:12.933 回答