#RubyKaigi 2025 で「Improvement of REXML and speed up using StringScanner 」というタイトルで発表しました

2025/04/16〜2025/04/18にかけて松山で開催されたRubyKaigi 2025に参加し、今回は RubyKaigi 本編に登壇するという実績を解除することができました🎉

rubykaigi.org

昨年の RubyKaigi 2024 のLT でお話した話

naitoh.hatenablog.com

の続きで、今回、発表した資料は下記になります。

slide.rabbit-shocker.org

REXML 3.2.6 (Ruby 3.3.0 添付のもの) から最新の REXML 3.4.1 の間で、StringScanner を使った実装に置き換える事で、XMLパース処理が最大6割速くなったので、 StringScanner を使うと何故パース処理が速くなるのかどのような点に気をつけてパース処理を書けば速くなるのか などを、REXML の実装を踏まえながら高速化のポイントを紹介させて頂きました。

高速化のポイント

  1. XMLのパース処理にRegexpクラスの代わりにStringScanner を使うと速い
    • 単純な性能比較で StringScanner の方が5割程度速い
  2. StringScanner を効果的に使うことで StringScanner (C拡張部分) の処理を最適化する
    • StringScanner のRegexpマッチの代わりにStringマッチを使うとより高速
      • 正規表現は便利だが処理が重いので、文字列でのマッチで代替可能な場合はStringマッチ(内部処理は memcmp()rb_memsearch() での文字列比較)を使う事で速く処理できる
    • Stringマッチならマッチ対象文字列が長くなっても、性能劣化を最小限に抑えられる
      • 正規表現の場合、マッチ対象文字列が長くなるとパフォーマンス低下に繋がりやすい
    • String Object の生成を避けると速い
      • マッチ結果を返すために Object 生成するのにコストがかかるので、マッチ結果を Integer で返すと Object 生成しないので速くなる
  3. Ruby 実装部分で遅い処理には定数やメモ化を使うことでボトルネックを解消する

という感じで、細かな改善の積み重ねで最大6割の高速化を実現した内容をベンチマークコードとその結果を用いて説明させて頂きました。

また、上記の高速化の取り組みを実施後、YJITを有効化すると、Ruby 実装部分が JIT コンパイルされるので、さらに高速化されます。

StringScanner の 新機能(#scan_until(String))追加による Stringマッチ新メソッド(#peek_byte)追加による String Object 生成回避 といった高速化手法が増えた事も、今回の高速化に繋がっているので、StringScanner を使ってパース処理を実装してみたいという方は、ぜひトライしてみてください。

最後に

今回、RubyKaigi 本編初登壇で、当日朝まで資料を修正してたりしていたので、やり切った感が半端ない感じでした。

自分の発表が終わった後に、質問に来てくれた方が 5人ぐらいおられました。 その中で、内藤の発表を受けて、「モチベーションが非常に上がったので、とりあえずその感動を伝えたい」とその事だけを伝えに来てくれた方もおられたので、RubyKaigi で「何かを持ち帰る」だけでなく「何かを持ち帰って頂ける」ようになれたんだなと思いました。

来年も登壇できるように頑張ります!

ruby/rexml のメンテナーになりました

というわけで、ruby/rexml のメンテナーになりましたので、この1年間の REXML (と StringScanner) への取り組みをまとめてみます。

1. rexml 高速化

Ruby 3.3.0 添付の rexml 3.2.6 gem から、Ruby 3.4.0 添付の rexml 3.4.0 gem の間で最大4割の高速化を実現しました。🎉

Add 3.4.0 entry · ruby/rexml@19d8ebf · GitHub

ruby 3.3.6 (2024-11-05 revision 75015d4c1f) [x86_64-linux]
Calculating -------------------------------------
                     rexml 3.2.6      master  3.2.6(YJIT)  master(YJIT) 
                 dom       8.073       9.400       13.512        16.667 i/s -     100.000 times in 12.386407s 10.638806s 7.400877s 5.999736s
                 sax      13.416      15.955       19.910        25.617 i/s -     100.000 times in 7.453542s 6.267482s 5.022630s 3.903620s
                pull      15.971      19.289       23.253        30.003 i/s -     100.000 times in 6.261289s 5.184190s 4.300510s 3.332993s
              stream      14.660      18.499       20.502        28.797 i/s -     100.000 times in 6.821191s 5.405752s 4.877656s 3.472597s

rexml 3.2.6 → 3.4.0 の比較

YJIT=OFF YJIT=ON
dom +16.4% +23.3%
sax +18.9% +28.6%
pull +20.7% +29.0%
stream +26.1% +40.4%
  • YJIT=OFF 状態で 16.4%〜26.1% の高速化
  • YJIT=ON 状態で 23.3%〜40.4% の高速化

という感じで、最大4割の高速化を実現しましたので、やった事を説明しようと思います。

パース処理の内部実装を Regexp クラスから StringScanner に変更

RubyRegexp クラスを用いた正規表現でのパース処理を StringScanner を用いたパース処理に置き換えました。 この辺りは RubyKaigi 2024 LT で発表しましたので詳細は下記を参照ください。

naitoh.hatenablog.com

なお、上記blog でStringScanner#checkRegexp#match とのマイクロベンチマーク単純なケースでは StringScanner は Regexp より 1.67 倍速い と言いました。

この点、LT 発表では詳細には触れなかったのですが、Regexp クラスでは Onigmo という正規表現エンジンが使用されており、StringScanner の正規表現もOnigmoを使用しています。 つまり同じ正規表現エンジンを使って処理しているのですが、Regexp クラスの場合、マッチ時に MatchData Object を生成して返すのでその分遅くなっているようです。 (StringScanner はマッチした文字列だけを返すので速い。)

似たような StringScanner#match?Regexp#match? の下記マイクロベンチマークでは、両方ともマッチデータは応答しないため、ほぼ同じ性能です。

prelude: |-
  require "strscan"
  str = "test string"
  s = StringScanner.new(str)
  re = /\A\w+/
benchmark:
  s.match?(/\w+/): |
    s.match?(/\w+/)
  re.match(str): |
    re.match?(str)
$ benchmark-driver benchmark/test.yaml 
Warming up --------------------------------------
     s.match?(/\w+/)    12.391M i/s -     12.828M times in 1.035236s (80.70ns/i)
       re.match(str)    12.888M i/s -     12.964M times in 1.005849s (77.59ns/i)
Calculating -------------------------------------
     s.match?(/\w+/)    14.362M i/s -     37.174M times in 2.588407s (69.63ns/i)
       re.match(str)    14.672M i/s -     38.665M times in 2.635401s (68.16ns/i)

Comparison:
       re.match(str):  14671575.6 i/s 
     s.match?(/\w+/):  14361690.4 i/s - 1.02x  slower

StringScanner#match? はパターンマッチの値 self[nth] は変更され、Regexp#match?$~ などパターンマッチに関する組み込み変数の値は変更されないため、Regexp#match? が若干速いようです。

同じ正規表現エンジンを用いているのでどこまで高速化できるのか? が疑問点だったのですが、下記の観点で最適化した結果、 最終的な REXML の高速化に繋がったと考えています。

正規表現をなるべく使わないようにする (文字列のみのマッチで代替する)

正規表現を使うと処理に時間がかかるため、XMLパース処理を見直し、このタイミングではこの文字列かこの文字列が来る、違う文字列が来たらエラー といった感じで文字列比較でパース可能な処理は正規表現を使わないように変更しました。

StringScanner は文字列の先頭から順番にパースしていくので StringScanner らしい処理に書き換えることで高速化できました。

不要な文字列 Object の生成を抑える形で最適化する

StringScanner#scan はマッチした文字列を返すのですが、返された文字列を使用しないケースでは文字列 Object 生成処理が無駄なため、マッチした文字列長を返す StringScanner#skip を使うように変更しました。

繰り返し使う正規表現オブジェクトやエンコード処理をキャッシュして生成コストを抑える

頻繁に呼ばれる double quotationsingle quotation正規表現オブジェクト生成やエンコード処理に時間がかかっていたためキャッシュしました。

2. 不正XMLチェックの強化

下記のようなXMLは不正で、処理を続行するとパース処理で不要な考慮が必要になるためエラーするように変更しています。

  • 複数のルートタグ
<root1></root1><root2></root2>
  • 開始ルートタグ前の文字列
foo<root></root>
  • 終了ルートタグ後の文字列
<root></root>bar
  • ルートタグ無し文字列
404 error

アプリケーション側も不正なXMLはエラーになってくれると嬉しいと思います。

3. 各パーサー間でのパース処理結果の統一

REXML はパース処理方法の違いから DOM/SAX2/Pull/Stream の各パーサーがあり、それぞれ使い勝手が異なり用途に合わせて使って頂く事を想定しているのですが、パース処理結果に差異があったので揃えました。

4. REXML の SAX2 & Pull パーサ使用時のDoS脆弱性を報告 & 修正

CVE-2024-41946: REXML内のDoS脆弱性 で報告したのですが、内容的には DOM の REXML におけるエンティティ展開に伴うサービス不能攻撃について (CVE-2013-1821)CVE-2014-8080: REXML におけるXML展開に伴うサービス不能攻撃について と同じになります。

上記 3 の各パーサー間でのパース処理結果の統一の一環で、各パーサー間の実装差を減らしたいなと処理を見てると、DOM だけ対処されて、なんで SAX2 & Pull パーサの場合、この処理無いんだろう?と気づいてDOMの再現用テストコードを流用して SAX2 & Pull パーサで試したら問題があったので横展開する形で治しました。 テストコード大事。

セキュリティ報告ってやった事が無かったので新鮮で勉強になりました。(久々に patch コマンド使った。)

5. StringScanner の改善

REXMLを高速化するために StringScanner を使い出したのですが、StringScanner もいい感じにする必要があったので改善しています。

上記の 2〜4 は RubyKaigi 2024 follow up で話させて頂きましたので、下記の資料が参考になると思います。

speakerdeck.com

6. まとめ

という感じで REXML をいい感じに改善していたら ruby/rexml のメンテナーに推薦頂き、REXMLが大分わかってきたのでメンテナーになりました。

REXML をもっと良い感じにしていくぞ💪

#RubyKaigi 2024 LTで「Improved REXML XML parsing performance using StringScanner 」というタイトルで発表しました。

RubyKaigi 2024 LT6年振り にLT発表させて頂きました。

今回の内容は、おおよそ naitoh.hatenablog.com を5分に縮めた内容になります。

具体的には、下記になります。

  • 自分が作成している RBPDF gemSVG 画像(XMLで記述されています。)を処理したいので、XML処理ライブラリの中でインストールの容易な REXML を使いたい。
  • REXML は C拡張の gem の libxml-ruby と比較して dom で65倍、sax でも 21倍遅いので、速くしたい。
  • Ruby 3.3 で YJIT を有効にするだけで dom で65倍→44倍、sax で 21倍→14倍の性能差まで改善されるが、まだ遅いのでさらに改善したい。
  • RubyKaigi 2019 の Better CSV processing with Ruby 2.6 で、StringScanner を用いって CSV gem を速くした話で REXML も速くしたいという話があった。
  • REXML は Regexp クラスを用いて実装されているので、StringScanner で書き換えを実施すれば良いか検討。
    • 単純なケースでは StringScanner は Regexp より 1.67 倍速い
    • StringScanner の処理手順(先頭文字列から順次処理)と Regexp の処理手順(複数一括マッチ)を考慮すると、性能の単純比較は無理だが、メモリの効率の観点から StringScanner の方が最適化され速そう。
  • StringScanner への書き換えは下記の手順で実施。
    1. ベンチマーク追加
    2. プロファイラで最適化ポイントを調査し、XMLパース処理がボトルネックであることを確認
    3. XMLパース処理をStringScannerで書き換え
      • StringScanner のバグ修正
      • REXML の XML 仕様違反の修正
    4. ベンチマークで効果を確認
    5. 上記の繰り返し
  • 結果、下記まで性能改善することができた。
    • YJIT無効状態で、dom: 65倍→60倍に短縮、sax: 21倍→17倍に短縮。
    • YJIT有効状態で、dom: 44倍→25倍に短縮、sax: 14倍→8.6倍に短縮。🎉

speakerdeck.com

※ 資料で計測したコードは https://gist.github.com/naitoh/abc5134fdf37bb3952e36f1fb77163b0 になります。 (YJIT 無効時は、RubyVM::YJIT.enable の行をコメントアウトして計測。)

Ruby 1.8.7 の頃の絶望的(300倍くらい?)なREXMLの遅さ から比較すると、同じオーダの性能差まで短縮できたので、用途によっては本番環境でも使えるのではないでしょうか。

今回のLTは無事、時間内に終わりましたが、緊張して声が震えてしまいました。いろんな場でLTして緊張しないようになりたいですね。 今年のRubyKaigi も楽しかったです。感謝!

RubyKaigi 2024 に出したCFP

久々にCFP出したけど通らなかったので公開します。

REXMLは Ruby 1.8.7 の頃に比べると本当に速くなってるし、YJITは Pure Ruby のライブラリにとってゲームチェンジャーだと思うんですよ。

※ 実際に出したCFP は英語で記述しています。

Title

REXMLのXML解析処理の改善

Abstract

REXML は Ruby で標準で使える XMLライブラリ(Bundled Gem)で、Ruby で実装されています。

Pure Ruby のためインストールし易い特徴があるのですが、逆に処理性能が遅いです。 今回、この REXML のパース処理を高速化したので、どのような手法を用いて実施したのかをお話しします。

Details

intended audience(対象読者)

このトークは下記の方を対象に考えています。

  • Pure Ruby でパース処理を書いてみたい人向け。
  • インストールが容易なPure Ruby 環境でXMLを処理したい人向け
  • Ruby WASM 環境でXMLを処理したい人向け

outcomes(成果)

REXML の XMLパース処理が正規表現(Regexp)で実装されているのを StringScanner (1) を用いたXMLパース処理に書き直し(2)、XML パース処理を最大 32%高速化(3)しました。

REXML と StringScanner の解説をまじえながら、私がどのような手法を用いてREXMLのXMLパース処理を高速化したのかを説明する事で、XMLの処理方法や StringScanner のパース処理方法を理解して欲しいと考えています。

  1. https://docs.ruby-lang.org/ja/latest/class/StringScanner.html
  2. PR
  3. https://github.com/ruby/rexml/actions/runs/7723085598/job/21052458823
ruby 3.3.0 (2023-12-25 revision 5124f9ac75) [x86_64-linux]
Calculating -------------------------------------
                     rexml 3.2.6      master  3.2.6(YJIT)  master(YJIT) 
                 dom       4.902       5.224        7.664         8.755 i/s -     100.000 times in 20.400673s 19.140694s 13.047998s 11.422036s
                 sax      13.011      15.593       18.846        23.533 i/s -     100.000 times in 7.685622s 6.413001s 5.306133s 4.249434s
                pull      15.512      19.271       22.121        29.095 i/s -     100.000 times in 6.446480s 5.189123s 4.520615s 3.436964s
              stream      14.188      17.217       19.395        24.327 i/s -     100.000 times in 7.048419s 5.808095s 5.155877s 4.110616s

Comparison:
                              dom
        master(YJIT):         8.8 i/s 
         3.2.6(YJIT):         7.7 i/s - 1.14x  slower
              master:         5.2 i/s - 1.68x  slower
         rexml 3.2.6:         4.9 i/s - 1.79x  slower

                              sax
        master(YJIT):        23.5 i/s 
         3.2.6(YJIT):        18.8 i/s - 1.25x  slower
              master:        15.6 i/s - 1.51x  slower
         rexml 3.2.6:        13.0 i/s - 1.81x  slower

                             pull
        master(YJIT):        29.1 i/s 
         3.2.6(YJIT):        22.1 i/s - 1.32x  slower
              master:        19.3 i/s - 1.51x  slower
         rexml 3.2.6:        15.5 i/s - 1.88x  slower

                           stream
        master(YJIT):        24.3 i/s 
         3.2.6(YJIT):        19.4 i/s - 1.25x  slower
              master:        17.2 i/s - 1.41x  slower
         rexml 3.2.6:        14.2 i/s - 1.71x  slower

outlines(概要)

  1. 最初にXMLの概要とXMLの処理方式(DOM, SAX)の違いを説明します。
  2. REXML の処理性能を他のRuby C拡張 gem と比較しながら現状の処理速度を説明します。
  3. 文字列スキャナである StringScanner の使い方の説明をします。
  4. 処理速度を改善するために行った事(ベンチマークの整備、プロファイラを用いたボトルネックの確認、StringScannerのバグ修正等、StringScannerを用いた処理の書き換え)を説明します。
  5. まとめ

Pitch

Explain why this talk should be considered (この講演を考慮すべき理由)

PythonPHP などと比較して、Ruby の標準 XML パーサーが遅いのが気になっていました。

Ruby 1.8.7 の記事ですが、1万行程度のXMLの(DOM)パース処理 で C拡張gem の libxml-ruby と比較して数桁遅く、本番環境では使い物にならない状況でした。

https://suer.hatenablog.com/entry/20110126/xml_parse

この当時と比較して、Ruby の高速化、YJIT による高速化、REXML 自身の高速化などにより状況が改善されつつあるため、Pure Ruby 実装である REXML を再評価できればと考えています。

what makes you qualified to speak on the topic. (このテーマを話す資格がある理由)

仕事(SMI-S XML, PubMed XML)やプライベート(SVG)でXMLを処理する事が多く、複数の種類のXMLファイルを処理した経験があります。 また、nokogiri, ox, REXML と複数の XMLパーサーを使用した経験があり、今回 REXML のパース処理をStringScannerを用い高速化できた事で、REXML や StringScanner について解説できると考えています。

  1. Storage Management Initiative Specification (SMI-S)
  2. https://www.nlm.nih.gov/bsd/licensee/data_elements_doc.html
  3. https://developer.mozilla.org/en-US/docs/Web/SVG

Spoken language in your talk

日本語

Ruby 2.6 の新機能の endless range と Range#% を Numo::NArray と Cumo で対応しました。

自分が参加している Red Data ToolsElement@mrkn さんから、

ArithmeticSequence と Range から begin, end, step, exclude_end を取り出す C API を trunk に追加しました。 ruby/ruby@914a290 だれか、これを使って NArray などの slicing に対応するプルリクエストを作ってみませんか?

という提案を頂いたので、 Numo::NArrayCumo (※ Numo::NArray のGPU 版)で 上記 C API を使用して Ruby 2.6 の新機能の endless rangeRange#% に対応を実施しました。

endless range とは、下記のように 末端の "-1" 指摘を省略できる書き方です。
また、step の alias として % を使用できると、より直感的に書く事ができます。

> a = [0, 1, 2, 3, 4]
 => [0, 1, 2, 3, 4] 
> a[1..-1]
 => [1, 2, 3, 4] 
> a[1...-1]
 => [1, 2, 3] 
> (1..4).to_a
 => [1, 2, 3, 4] 
> (1..4).step(2).to_a
 => [1, 3] 
  • Ruby 2.6 (endless range, Range#%)
> a = [0, 1, 2, 3, 4]
 => [0, 1, 2, 3, 4] 
> a[1..]
 => [1, 2, 3, 4] 
> a[1...]
 => [1, 2, 3, 4] 
> (1..4).to_a
 => [1, 2, 3, 4] 
> ((1..4) % 2).to_a
 => [1, 3] 

このように便利な記法が Numo::NArray や Cumo でも使えると嬉しいので、Ruby 2.6 の環境で同様に書けるように対応しました。

  • Numo::NArray
> require 'numo/narray'
> a = Numo::Int32.new(5).seq
 => Numo::Int32#shape=[5]
[0, 1, 2, 3, 4]
> a[1..]
 => Numo::Int32(view)#shape=[4]
[1, 2, 3, 4] 
> a[1...]
 => Numo::Int32(view)#shape=[4]
[1, 2, 3, 4] 
> a[(1..) % 2]
 => Numo::Int32(view)#shape=[2]
[1, 3] 
  • Cumo
> require 'cumo'
> a = Cumo::Int32.new(5).seq
=> Cumo::Int32#shape=[5]
[0, 1, 2, 3, 4]
> a[1..]
=> Cumo::Int32(view)#shape=[4]
[1, 2, 3, 4]
> a[1...]
=> Cumo::Int32(view)#shape=[4]
[1, 2, 3, 4]
> a[(1..) % 2]
=> Cumo::Int32(view)#shape=[2]
[1, 3]

numo-narray の endless range、Range#%の PR は master に 取り込まれている ので次のリリース(0.9.1.5以降?)で使用可能になると思われます。
cumo は リリース版ですでに 取り込まれている ので 0.2.5 以降で使用可能です。

Ruby 本体側でデータサイエンス向けの機能が取り込まれていくと、データサイエンス用ライブラリ側も便利になっていきますので嬉しいですよね。

Redmineのpdf出力機能で使われているRBPDF gem ライブラリについて

(この記事は Redmine Advent Calendar 2018 - Adventar の13日目の記事です。)

Redmine の PDF 出力機能で使われている 私が作成した RBPDF gem ライブラリについてのお話です。

github.com

RBPDF は下記のような特徴があります。

実際にどのような事ができるかは Examples をご覧下さい。

なお、RBPDFは一から作成したものではなくPHPのPDF出力ライブラリTCPDFを、Redmineで使用するために Ruby に移植したものになります。

Redmine の PDF出力機能の歴史

RBPDFを作成することになった経緯を Redmine の PDF出力機能の歴史と合わせてお話しします。

Redmine の PDF出力機能は 1.0 の頃は FPDF という PHPのライブラリを Ruby に移植した RFPDF が使われていました。 このころは日本語が文字化けするなど多くの問題を抱えていましたが、Defect #61: Broken character encoding in pdf export - Redmine のチケットで edwinmoss さんが開発された 同じくPHP由来のTCPDFベースの RFPDF に置き換え & 修正作業を行うことで無事、Redmine 1.2 で CJK(日本・韓国・中国語)と多くの言語の文字化けが解決しました。

github.com

なお、これが私のRedmine での初の採用パッチになります。 詳細は、第2回勉強会 - redmine.tokyo (当時は shinagawa.redmine)で LTをさせて頂きました。

naitoh.hatenablog.com

RFPDF(TCPDFベース)の機能で画像出力や(Textileから変換された)HTMLの変換などにも対応可能な事がわかったので、Redmine 1.3 で Textileによる書式設定 Feature #69: Textilized description in PDF - Redmine や画像出力 Feature #3261: support attachment images in PDF export - Redmine の対応を実施しました。

ただ、この対応時に多くの課題が見えてきたので、抜本的に解決を図るため edwinmoss さんから RFPDF の開発を引き継ぎ、ベースのTCPDFのバージョンを5.2まで上げる形でPHPからRubyへ移植を実施、RBPDF 1.18 として gem を作成しました。 これは Redmine 2.6 の PDFエクスポートの改善として取り込まれました。

TCPDFをベースとしてgem パッケージを開発した理由は下記になります。

  • Redmine コミッターの @marutosijp さんから、PDF出力機能は複雑なので Redmine 本体から分離したいと伺っていた。
  • Redmine にパッチが採用されるためにはWindows/Linux OS環境で動作しなければならない。
    • Redmine のインストールは Rails アプリを配布する形式であるため Pure Ruby であればインストール(環境構築)のハードルを上げないため、採用される可能性がある。
    • Ruby で HTML対応の PDF出力ライブラリで実用的なものは PDFKit ベースのライブラリがいくつかあったが WebKit に依存しているため採用されそうになかった。
  • Redmine のライセンス(GPL2)と互換のあるLGPL 2.1ライセンスのTCPDF 5.2(PHP)をRubyに移植できれば、RTL言語対応や埋め込みフォント対応、HTMLサポートの改善などが見込める。
    • RedminePHP に移植した CandyCane があるなら、その逆も可能と思った。

詳細は、LTthon | RubyHiroba 2014で発表させて頂きました。

www.slideshare.net

その後、RBPDF 1.19 で埋め込みサブセットフォントをサポートし、Defect #19017: Wiki PDF Export: <pre> not rendered with monospaced font - RedmineRedmine 3.2 で取り込まれCJK 以外の言語の PDFファイルで発生していた(対象言語の全フォント埋め込みによる)肥大化問題が解決されました。 これで自分の当初のゴールとしていた内容の対応は完了しました。

Redmine の PDF出力機能で改善されるといいなと思っている点

PDF出力機能の基盤部分の改善は実施しましたが、使い勝手の部分では時間が取れず手が回っていないのですが、今後、下記が改善されるといいなと思いっています。

以上です。

#RubyData_tokyo Meetupで「Usability of Numo::NArray in Numerical Computing of Ruby.」というタイトルで発表してきました

speee.connpass.com

上記のRubyData Tokyo Meetup で「Usability of Numo::NArray in Numerical Computing of Ruby.」というタイトルで発表してきました 発表資料は下記になります。

www.slideshare.net

Red Chainer の開発中に気づいた Numo::NArray のわかりにくかった所や、Numo::NArray に対して自分が取り組んだ、Inplace/Broadcast性能改善SIMD演算対応dot(transpose)性能改善 などを踏まえ、現在の計算速度の改善状況などをお話しました。

自分でも驚きだったのが、ベンチマークの結果、 加算と減算 (Inplace計算は除く)は Numo::Narrayの方が numpy より計算が速い という事ですね。 資料中のベンチマークのコードと結果は下記にあります。

Numo::NArray vs numpy performance. (CentOS 7(x64) Ruby 2.5.3 Numo::NArray 0.9.1.3, Python 3.6.5 numpy 1.15.4) · GitHub

Deep Learning From Scratch 部分の測定内容(p26,p27)は資料中のURLを参照ください。

Numo::NArray の使い方で自分が理解できていなかったところが、今回の発表で整理できてスッキリしたので Red Chainer の開発に生かしていきたいと思います。