• Github 中文镜像
Sign inSign up
Watch966
Star102.4k
Fork61.8k
Tag: rust
Switch branches/tags
Branches
Tags
K / Python Lua Rhai 性能对比测试.md
移动浏览 Clone
加载中...
到移动设备上浏览
145 lines 7.76 kB
First commit on 16 May 2021

    目标

    前段时间想把一些常用的 Rust 库绑定到 Lua 上,以便在 Lua 里复用 Rust 的各种库。

    相比 Python,这样做的好处很近似 Go,成品只是一个可执行文件 + Lua 脚本,不需要解决环境的各种依赖安装,并且生成的 binary 很小;而相比 Go,这样又近似 Python,打开个记事本写完就跑。

    于是我着手开始绑定,尽管我第一步就是将一些模板代码写成宏,尽量降低重复劳动,但实际要写的代码还是太多了,自虐了几天开始寻找其他解决方案,看了一些由 Rust 实现的脚本语言,选择了 Rhai 作为备选方案。

    将 Rust 里的类型传递给 Rhai 很方便,可以少写很多代码,但是 Rhai 只是相当于一个语法翻译器,Github 上主要贡献者也谈到了性能大概在 Python 的水平,只适合做插件,所以我做了个简单的性能测试。

    脚本语言对比

    看了很多易与 Rust 集成的脚本语言,作为资料,列举我排除的理由。我需要的是一个小型的、易用的、可嵌入的语言。

    1. Rune ⭐3.2k:本来这似乎是最好的选择,但是开发已经很不活跃了。
    2. Dyon ⭐1.3k:完成度比较高,但也更复杂,适合作游戏脚本层。
    3. Gluon ⭐2.3k:函数式,写起来与平时的思维方式不同。
    4. Ketos ⭐628:Lisp 方言,似乎已停止开发。
    5. Mun ⭐1.1k:编译型脚本语言,用起来有点不方便,适合作游戏脚本层。

    测试

    测试很简单,计算斐波那契数列,测试了 5 种情况。

    1. Rhai 实现的斐波那契。
    2. 在 Rust 里实现斐波那契,由 Rhai 调用。
    3. Lua 实现的斐波那契。
    4. 在 Rust 里实现斐波那契,由 Lua 调用。
    5. Python 实现的斐波那契。

    由于 Lua 速度太快 😂,而 Lua 自带的时间戳只能精确到毫秒,所以我的测试方式是笔记本在完全节能模式下跑 3 次,每次循环 5 次 fibonacci(32)(代码里函数命名简写为 fib)。

    代码

    1. Rust 代码,绑定到 Lua 和 Rhai 环境里,用 fib_rs 命名以区分。
      fn fib_rs(n: i64) -> i64 {
          if n < 2 { return n; }
          fib_rs(n - 2) + fib_rs(n - 1)
      }
      
    2. Lua 测试代码
      function fib(n)
          if n < 2 then return n end
          return fib(n - 2) + fib(n - 1)
      end
      
      local start = os.clock()
      for i = 1, 5 do
          fib(32)
      end
      io.write(string.format("elapsed: %f\n", os.clock() - start))
      
      local start2 = os.clock()
      for i = 1, 5 do
          fib_rs(32)
      end
      io.write(string.format("elapsed: %f\n", os.clock() - start2))
      
    3. Rhai 测试代码
      fn fib(n) {
          if n < 2 { return n; }
          fib(n - 2) + fib(n - 1)
      }
      
      let now = timestamp();
      for i in range(0, 5) {
          fib(32);
      }
      print(now.elapsed());
      
      let now2 = timestamp();
      for i in range(0, 5) {
          fib_rs(32);
      }
      print(now2.elapsed());
      
    4. Python 测试代码
      from __future__ import print_function
      
      import time
      
      def fib(n):
          if n < 2: return n
          return fib(n - 1) + fib(n - 2)
      
      start = time.process_time()
      
      for i in range(0, 5):
          fib(32)
      
      print("elapsed: " + str(time.process_time() - start))
      
      # 尾递归版,仅几十毫秒。
      import time
      
      def fib(n, acc1, acc2):
              if n < 1: return acc1
              return fib(n - 1, acc1 + acc2, acc1)
      
      def loop_fib(n):
          for i in range(0, n):
              fib(90, 0, 1)
      
      start = time.process_time()
      
      loop_fib(2000)
      
      print("elapsed: " + str(time.process_time() - start))
      

    测试结果

    • Lua:上面是 Lua 实现,下面是调用 Rust。
      elapsed: 1.081000
      elapsed: 0.038000
      
      elapsed: 1.085000
      elapsed: 0.040000
      
      elapsed: 1.203000
      elapsed: 0.047000
      
    • Python:三次都是纯 Python 实现。
      elapsed: 6.34375
      elapsed: 5.0
      elapsed: 4.984375
      
    • Rhai:上面是 Rhai 实现,下面是调用 Rust。
      79.4438942
      0.1073184
      
      111.5409529
      0.1158327
      
      46.1241106
      0.0424334
      

    结论

    Rhai 还是只适合作为插件系统调用 Rust 实现的 API,当成一个解释器用还是不太行。尽管绑定 Lua 代码超多,但似乎别无选择。