RubyからSwiftの関数をつかう → はやい

  • 23
    Like
  • 0
    Comment
More than 1 year has passed since last update.

はじめに

去る2015年12月3日、Swift がオープンソース化され、Linux 版も公開されました。すばらしい。
Swift.org - Welcome to Swift.org

ちょっと無理矢理にですが、Ruby から呼んでみました。

元ネタ: RubyからGoの関数をつかう → はやい

注意: 2015年12月現在、Swift のコードを Objective-C 以外の言語から呼ぶ方法は公式にはアナウンスされていません。
本記事は、独自に試した結果を書いています。

環境

  • Ubuntu 14.04 LTS(Trusty Tahr) 64bit
$ ruby -v
ruby 2.2.3p173 (2015-08-18 revision 51636) [x86_64-linux]

$ swiftc -v
Swift version 2.2-dev (LLVM 46be9ff861, Clang 4deb154edc, Swift 778f82939c)
Target: x86_64-unknown-linux-gnu

Swift、はやい

最初にRubyとSwiftの計算速度を比べるため、フィボナッチ数を単純に計算するやつをそれぞれ書きました。

fib.rb
def fib(n)
  return n if n <= 1
  fib(n - 1) + fib(n - 2)
end

puts fib(40)
fib.swift
func fib(n: Int) -> Int {
    if n <= 1 {
        return n
    }
    return fib(n - 1) + fib(n - 2)
}

print(fib(40))

実行結果は以下です。

Ruby
$ time ruby fib.rb 
102334155

real    0m22.581s
user    0m22.558s
sys 0m0.041s
Swift
$ time swift fib.swift 
102334155

real    0m1.077s
user    0m1.040s
sys 0m0.029s
ビルド済みSwift
$ swiftc -O fib.swift
$ time ./fib
102334155

real    0m0.374s
user    0m0.374s
sys 0m0.000s

Swift はいい感じにはやいです。

Swift を共有ライブラリにする

print 文を取り除き、fib 関数を public 宣言します。

fib.swift
public func fib(n: UInt) -> UInt {
    if n <= 1 {
        return n
    }
    return fib(n - 1) + fib(n - 2)
}

Linux 上では現状、次のようにして Swift の共有ライブラリを作れるようです。 (注: 今後変わる可能性はあります)

$ swiftc -emit-library -emit-object -module-name fib -O fib.swift
$ clang -shared -o libfib.so fib.o

生成された共有ライブラリの中身を見てみます。

$ nm -D libfib.so
                 w _ITM_deregisterTMCloneTable
                 w _ITM_registerTMCloneTable
                 w _Jv_RegisterClasses
0000000000000680 T _TF3fib3fibFSuSu
0000000000201038 B __bss_start
                 w __cxa_finalize
                 w __gmon_start__
0000000000201038 D _edata
0000000000201040 B _end
00000000000006bc T _fini
0000000000000528 T _init

fib 関数の名前はマングリングされ _TF3fib3fibFSuSu というシンボルになっています。
なんとなく呼べそうです。

Ruby から Swift の関数を呼ぶ

さきほどの共有ライブラリを ffi を用いて呼ぶわけですが、今の所、マングリングされたシンボル名を使って呼んでやる必要があります。

swift-fib.rb
require "ffi"

module Fib
  extend FFI::Library
  ffi_lib "libfib.so"
  attach_function :_TF3fib3fibFSuSu, [:uint], :uint
end

puts Fib._TF3fib3fibFSuSu(40)

Ruby meets Swift

Ruby+Swift
$ time LD_LIBRARY_PATH=.:$LD_LIBRARY_PATH ruby swift-fib.rb
102334155

real    0m1.044s
user    0m1.009s
sys 0m0.037s

無事に呼べました。そして、はやいですね。

どの言語も仲良く平和にやっていきましょう。

補足

冒頭にも書きましたが、この記事で示した Swift の共有ライブラリを外部から呼ぶ方法は、公式のものではありません。
したがって、何らの保証もできません。良い子はマネしないでね!

ただ、Swift は元々 Apple の Objective-C1 との相互運用を考慮して設計されていることや、Swift 3.0 で ABI の安定化が図られるという話があり、Objective-C 以外との相互運用をするための下地は整ってきていると思います。
いずれちゃんとした形で Objective-C 以外との相互運用が実現されるだろう、と予想しています。

今後の Swift の発展が楽しみです。

参考


  1. Objective-C 自体 C との相互運用性が考慮されている