趣味はvaporware造りです v.0.0.1
RubyKaigi2023に行ってきたのは別エントリにしたのでこのエントリでは LT で話しした内容の説明などをしていこうかと思います。
Presentation
LT申し込みまで
RubyKaigi2023 松本でやるし発表もしたいが、ネタがないなとおもってたら本編の方の CFP1 が閉じられてた2。 とはいえ今年は COVID-19 の制限も無くなるから LT あるとおもうので LT 出せたらいいなあと考えてた。 そうこうしているうちに Ruby30th で今はまだないものだけど…って matz がいったものを造ろうとなりました。
LT の応募サイト開くまでにネタが幸い見つけることができ、実装をはじめることにした。
実装
ということで、通るかどうかわからないですが、発表できるように準備をはじめました。 準備といっても実際は実装どこまでできましたーというような発表にする予定だったので そのまま実装をしています。
実装予定は発表にもあった通り、以下の予定ですすめて、最後に fiddle
で読み込めるように実装していました。
- 四則演算:
Integer
の計算ができること - 変数: 変数が使えること
- 制御構文:
for
やif
などの基本的な制御構文が使えること - メソッド: メソッドが定義でき、利用できこと
を目標に実装をすすめていましたのでそれぞれの進捗を
四則演算
とくに詰まる点もなく、AST をもとに assembler へ変換するだけでいけた。
変数
これが一番大変だった。教科書 を cherry-picking
しながら進めてたために、
実装でわからん部分が出てきたのでインターネット介して gdb
リモートデバッグの講習を受けながら実装。
もともとここか制御構文までで終わるだろうなあと予想してたが、SEGV
出しながらもなんとかくりあ。
制御構文
はじめは素直に if
文を実装。ここも上記の理由によりなんもわからんとなり、別の人の実装 を参考に実装。
if
文だけじゃつまんないというか、とくになにもできないのでループ文を追加しようとしました。
はじめは for
文を実装しようと考えましたが、なんとここで重大なことに気がつきました。
Ruby の for
のことはなにもしらない。ということに。そういえば for x in 1..10; puts x;end
とか書けるけど
そもそも Range
クラスや Array
作る必要あるよな?ということで一旦 for
はやめに。
ということでここは while
文を実装して、fibonacci
数 を求めることができるようになりました。
メソッド
ここは変数、制御構文ができれば作るだけなら問題はなかったように思えます。
ただほんとうに作るだけだったので、引数なし、定数を返すだけのメソッドの確認しか行なっていないです。
ここの実装にたどり着くとは思っておらず、最後のRubyKaigi直前の6連休に入ってから実装できてしまったので
あんまりブラッシュアップできていないです。
引数の扱い、変数の利用、などなど確認をとれておらずにいますが動いたので fiddle
で読み込めるように。
Fiddle で利用できるようにする
定義したメソッドを C言語 の関数として見えるようにしてあげればいいので、 そうしたら、あっさりとうごいてしまった3。この時点で連休の終わりの方だったのは覚えている
発表準備
もともとGW前の連休には実装を終らせてGWの連休中に発表資料作成とできたところまでの実装のブラッシュアップを 行なう予定でしたが、目標の実装が見えてるとなるとどうしても実装したくなって実装の方を進めてました。
もともと発表は Rabbit を利用して英語で行う予定でしたが、LTはとくに うちあわせのために資料を先に出すとかなかったのでとりあえず日本語で作成しました。 実装おわっていないというか実装に時間掛けすぎたのと、RubyKaigiへ持っていくPCをあたらしくしたので 冒険はできずに素直に Google スライド 利用して作成、 後日 Rabbit で清書するということにしました。
発表資料自体は 5/8 or 5/9 ぐらいに作成し終っていました。デモなしで発表練習するとちょうど時間通りに おわるのでこの資料作成完了時点ではこれでよしとしてました。 この時点でも資料はすくたなくとも英語にしようとは考えて資料は作成していました。
当日
当日の朝、資料作成というか資料の英語変換をやっていて、やっているうちに 実際発表してなにができてなにができてないみたいなこと言ったとしても なにがなんだかわからんよなとなったので急遽デモをやって、実行できること示すように少し変更しました。
当日はセッションききながらかなり緊張していました、直前のセッションのトラブルもあったので。 聞いた人はわかるとおもうのですが、発表が途中でおわったのはこういった事情があったのでああいう発表になりました。
後日
発表当日の昼飯時に、 @ukstudio が cookpad のイベントとして 手を動かして振り返る RubyKaigi 2023 が5/18にあるからきてよと誘われたのでいくことにしました。 この会では、デキテイナカったデモを最後までやりきっています。
書き直し
手を動かして~ のあとに Rabbit への清書を行ないました。 いままでもいくつかの発表は Rabbit でやってたのですが、 Rabbit テーマ を作りたい作りたいと思っていたので今回は時間もあるのでつくることにしました。 とはいってもほぼ @yu_suke1994 のリポジトリをコピーしただけなんでうが……
まとめ
RubyKaigi の LT で話ししたことをまとめました。 実装の話はもうちょっと詳しく書きたいけど、そもそも実装もすすめたいしでなかなか書けていないですね。
次回作もご期待ください 完
-
Call For Proposal。ずっと Call For Paper だとおもってたので知ったときえってなった。 ↩
-
2023-01-31 締切で完全にネタない状態だった。 ↩
-
もともとの発表予定ではここまでしかできなかったんですよーって濁す予定でしたが思っている以上に実装がすすんでしまった。 ↩
RubyKaigi2023 @MATZ本
行ってきたのでまとめます。LT やったのですが、 LT の話はまた今度。
Day -1
前日は恒例の Asakusa.rb の predrinkup に行ってました。ここでは、 nobu とかとはなししたりしてすごしました。 drinkup は次の日のため終了後すぐさま帰宅し、就寝した。
Day 0
この日は、 RubyistsOnRails というすばらしいイベント列車があったのでノータイム 1 8時の会に参加申し込み。6時に起床し、7時に出発。 会ではRubyistと話しながら、山見ながら、松本に到着。 松本に到着後飯として みよ田 という蕎麦屋に直行。 蕎麦を食べた直後、松本城へ。松本城では まつもとさん 集団を見つけたので、みんなで Matz本城 背景に記念撮影。まつもとさん一行は開発者会議があったらしく記念撮影後すぐにわかれました。 われわれは、この記念撮影後松本城観光し、もう一軒蕎麦屋へ昼食へ。二軒目の蕎麦屋は 蕎麦倶楽部佐々木 へ。
そんなことしてたら宿がチェックイン可能になったので、荷物置くのと下着類を購入しひとやすみ(10min程)。
Day 0 最大のイベント、 KeebKaigi へ!ここで豪華ノベリティを大量に頂きました!!!1とくによいのが城キーキャップで松本城を3DプリントしたISO エンターキーを頂きました。しかしながらこのキー差すキーボードがないので知人に譲る予定です。
KeebKaigi 自体もたいへん面白い Kaigi で楽しめました。とくに、 t-code の紹介のLT、頭文字Vなどよかったです。 KeebKaigi 後は懇親会には参加せず、友人とかるく夕飯をして宿へ。
Day 1
ここまででも大変楽しいものでしたが、RubyKaigi本番 はこの日から。
1日目の朝食は会場行く途中でもともと聞いていた店の 栞日 さんへ。 会場開いて登録したあとノベリティの列をみてげんなりしてたので一旦スポンサーブースをうろうろしてた。 この日聞いたセッションは以下です。
Matz Keynote
毎年恒例 Matz のキーノート。
昼飯
ランチはたまたま居合わせた6人で おきな堂 へ。名物のボルガライスとリンゴジュースを。シードルがあったので一緒にいったひとへオススメしてたりした。
The future vision of Ruby Parser
コミッターの spikelaf さんによるパーサーの開発話。魔窟じゃないよーなんかいってまいましたが、難しい問題を丁寧にひとつひとつ倒していった話をしていました。
Make Regexp#match much faster
去年 ReDoS の対応をしたコミッターの発表。正規表現の可能性を話してくれています。
High-performance real-time 3D graphics with Vulkan
Valkan を Ruby で利用できるようにした話。このあたりは LT への緊張感でほとんど覚えていないです。
Power up your REPL life with types
去年の TRICK Winner で今回の話は irb
を利用しているときにどういう感じで補完が出るのか、出すためにどのような型推論を行なっているのかの話だったとおもう。
LT
しゃべった
オフィシャルパーティー
つかれたのでそこそこで帰った
Day 2
朝飯は 珈琲茶房かめのや へ。ここもワンオペで配膳されるまで少し時間がかかった。というか、朝食べくるひといないのかどこもワンオペだったので Day 3 も早めに出るようにしようとなった。
On Ruby and ꝩduЯ, or How Scary are Trojan Source Attacks
昔他の言語で話題になった Ruby のコード的には正しいが、見た目ではわからない 不正な文字が入ったときの対策のはなしでした。
Build a mini Ruby debugger in under 300 lines
300 行といったなあれは嘘だ。という話。Ruby の Debugger を200 行くらいでつくったはなしでした。
昼飯
種村 で蕎麦と岩魚と日本酒。
Revisiting TypeProf - IDE support as a primary feature
Ruby に梱包されている TypeProf
が遅いので速くして、また書いている途中でもエラーがでたときどうするかとかの説明をしていました。まだ利用できていないので試したくなるものだった。
Ruby Implementation of QUIC: Progress and Challenges
Unasuke 大先生のライフワーク、 Ruby の QUIC 対応のはなし。Python での実装はどうとかのはなしで今回は実装がすすんでいるようでよかった。とはいえ道程はまだまだかかりそう。
afternoon break
お昼休憩後は Jeremy Evans や Maxime の話を聞いていたが疲れてなにも覚えていない。
Leaner Drinkup at RubyKaigi 2023
この日は Leaner さん主催の drinkup へ行きました。ここでは主に日本を飲んですごしました。でここで、spikelaf が kaigi でも話してた lrama が Ruby 本体のとりこまれ、 Ruby 3.3.0-preview1 がリリースされました!なんかさわいでるなと思たらリリース作業シテイタトハ… drinkup 終了後は onk とふたりで近所のワインバーへ行き、軽くのんで宿へ帰りました。
宿帰ったら ESM さんのノベリティの Rubyメソッドかるた でみんなでわいわいしながら遊んでました。
Day 3
この日の朝食は 山山食堂 というところでしました。
Committers and the Worldのあと
今回は Matz本 ということで会場にいる「まつもとさん」一同会して写真を撮ることを目的としてあつめてました。写真は以下になります
松本さん大集合に、当社の松本さんも混ぜていただきありがとうございましたっ🏯#RubyKaigi2023 pic.twitter.com/neYYer5e2A
— Yasunori Suzuki (@yasuzukisan) May 13, 2023
Ruby + ADBC - A single API between Ruby and DBs
須藤さん の発表で、 ADBC
で DB へ接続し、さらのその接続からのよみかきの高速化のはなしでした。あいかわらずすごいなと思いながら聞いていました。
Ruby JIT Hacking Guide
国分さん の新作 RubyJIT。PRみた感じだとやってるネタにてるなあとおもってたけど、アプローチがすこし違い、またこちらは直接ELFを書いているようなのでまだちがうようだ。とはいえ今やっていることは似ているのであとで参考にしよう。
After Party and Music Mixin
STORESさん の アフターパティー と pixivさん の ruby music mixin にいってきました。両方ともサイコーでした!!!!
Day 4
一人で野沢温泉村いって温泉はいって、ジンのんで、ビールのみいこうと一旦山おりたら温泉にしか飲むとこなかったのでもどったりして帰りました。
おわり
今回のRubyKaigi久しぶりのFull開催で大変楽しめたのですが、LTとは言えはじめての発表があり、心から楽しめたかというとあれですが、すごく楽しかったし、刺激になりました。来年の那覇は本編に出せるようになんとか精進します!!!
-
海外の人が登録して行ったほうがいいんじゃないかと一瞬思った ↩
趣味は vaporware 造りですv0.0.0
プログラマー三大勉強はしたけど、実装はしたことないものといえば
CPU、OS、コンパイラーなのです1が、先日 ruby30th 誕生日会のキーノートで
matz が “Static Compiler for Ruby” という今はまだない vaporware
として
挙げていたのでこの static compiler を作ろうとなりました。
Goal
Ruby のコードを static compile できるようにする。
コンパイル先のターゲットは x86 とします。 ARM や RISC-V などは今回の実装ではターゲットにしないです。
とはいえすべての Ruby の機能を実装すると時間がかかりすぎるので個人で無理のない範囲 で作ろうとします。無理のない実装範囲は以下なのかなと
- 四則演算
- 変数
- メソッド
- 制御構文
- プリミティブ型
この5つの機能を実装する予定です。
実現する機能以外のことについて
5つの機能を実現すること以上のことはやらない予定です。 やらないこととしては 最適化 、 GC 、 外部ファイルで定義したメソッドやクラスの読み込みは実装しない予定です。 実装しない個人的意見を以下に書いていきます。
最適化は Ruby のコードを単純に 機械語 におとしただけでは現在の RubyVM より速くならないと考えているからです(要確認)。 LLVM IR などへの変換ではなく、 機械語 なのは LLVM をインストールする必要があるなどして 面倒なのが大きいです2。あとバージョン毎に LLVM IR が異なるのも現状では対応しにくい点となっています。
GC (Garbage Collection: ガベージコレクション) についてはそもそもクラスをサポートできない、 変数などのメモリを確保しておく時間が長いプログラムを対象としないので今回はスコープ外としています。
外部ファイル読み込みについてですが、外部ファイルの読み込みして コンパイル するだけなら そこまで問題にはならないと考えていますが、外部ファイルで定義された メソッド や クラス を 事前に コンパイル して最後に リンク するのは型が不定になるのでサポートするのは難しいと考えています。
実装方針
Goal までの実装は 低レイヤを知りたい人のためのCコンパイラ作成入門 を参考にすすめていきます。 最初に コンパイラー を実装するものとして C 言語 がたぶん勉強してきてうかぶと思います3。 C 言語 だと 機械語 や VM のバイトコードへ落とすことのできる資料が多いので選択しています。 とは言っても Ruby からの脳内変換はある程度必要なので慣れているというのもあります。
実装としては AST(Abstract Syntax Tree: 構文抽象木) から愚直に x86 アセンブラ をファイルへ書き出し、
そこから C コンパイラー (gcc
or clang
) をつかって機械語へ コンパイル します。
フルフルの Ruby を実装するわけじゃないので依存する gem の依存も極力減らしたいです。
実装環境
- CPU: Ryzen Thread Ripper 1950x
- gcc: 12.2.1
- clang: 16.0.0
- OS: Gentoo Linux
- Linux Kernel: 6.2 系
パーサー
まず、AST を得るために パーサー が必要なのですが、 Ruby の構文は複雑なのでここは頑張らないようにします。ここをどうやって解決するのかというと RubyVM::AbstractSyntaxTree
や Ripper
をつかうのか、parser.gem
をつかうのかを決めるひつようがあります。今回というかしばらくは parser.gem
を利用して AST を得ることにします。
ゆくゆくは RubyVM::AbstractSyntaxTree
への置き替えはするよていです。
ここはそのまま parser.gem
のチュートリアルどおりにすれば AST が得られます。
require "parser/current"
puts Parser::CurrentRuby.parse("(1 + 2) * 3 / (5 - 4)")
きょうはここまで
とりあえず手を動かしはじめましたが、ななななんと、似たような機能が実は Ruby 3.3 向けに JIT として入ったようです4。 ということでねこの話はね、勉強の話しかないんですが一旦 Goal まで作ってみましょうね。
Update rubygems using Rust for Ruby 3.2
以前このブログで Rust を利用して rubygems を作成した rust_uuid が Ruby 3.2 がリリース によりコンパイルできなくなったのでその修正顛末。
環境
system | failed version | succeed version |
---|---|---|
ruby | 3.2.0 | 3.2.0 |
gem | 3.4.1 | 3.4.2 |
bundler | 2.3.19 | 2.4.2 |
rb-sys | 0.9.29 | 0.9.53 |
magnus | 0.3.2 | 0.4.4 |
what’s happened?
Ruby 3.2 が出てたのでアップデートして試してみるかーとおもいコマンドを実行!!!!
$ bundle exec rake build
.
.
...
error[E0425]: cannot find value `RUBY_ABI_VERSION` in the crate root
--> /path/to/cargo/dir/registry/src/github.com-1ecc6299db9ec823/rb-sys-0.9.29/src/ruby_abi_version.rs:14:73
|
14 | pub const __RB_SYS_RUBY_ABI_VERSION: std::os::raw::c_ulonglong = crate::RUBY_ABI_VERSION as _;
| ^^^^^^^^^^^^^^^^ not found in the crate root
For more information about this error, try `rustc --explain E0425`.
error: could not compile `rb-sys` due to previous error
warning: build failed, waiting for other jobs to finish...
gmake: *** [Makefile:564: target/release/librust_uuid.so] エラー 101
rake aborted!
Command failed with status (2): [/usr/bin/gmake...]
/path/to/rbenv/versions/3.2.0/bin/bundle:25:in `load'
/path/to/rbenv/versions/3.2.0/bin/bundle:25:in `<main>'
Tasks: TOP => build => compile => compile:x86_64-linux => compile:rust_uuid:x86_64-linux => copy:rust_uuid:x86_64-linux:3.2.0 => tmp/x86_64-linux/rust_uuid/3.2.0/rust_uuid.so
(See full trace by running task with --trace)
bundle exec rake build 63.55s user 10.61s system 541% cpu 13.705 total
おーなるほどなるほど、 cargo の rb-sys が Ruby 3.2 に対応していないバージョンつかってるんだなと理解。cargo を更新っするぞい。
うpだて cargo ぱっけーじ
ということで cargo build
が通るようにパッケージを更新するぞい。
cargo も bundler 同様、 cargo update
でいい感じにアップデートしてくれます。
$ cd ext/rust_uuid
$ cargo update
$ cargo build
Compiling magnus v0.3.2
error[E0432]: unresolved import `crate::ruby_sys::ruby_rstring_consts`
--> /path/to/cargo/dir/registry/src/github.com-1ecc6299db9ec823/magnus-0.3.2/src/r_string.rs:23:47
|
23 | use crate::ruby_sys::{rb_str_to_interned_str, ruby_rstring_consts::RSTRING_EMBED_LEN_SHIFT};
| ^^^^^^^^^^^^^^^^^^^ could not find `ruby_rstring_consts` in `ruby_sys`
error[E0599]: no variant or associated item named `RSTRING_EMBED_LEN_MASK` found for enum `ruby_rstring_flags` in the current scope
--> /path/to/cargo/dir/registry/src/github.com-1ecc6299db9ec823/magnus-0.3.2/src/r_string.rs:368:38
|
368 | f &= ruby_rstring_flags::RSTRING_EMBED_LEN_MASK as VALUE;
| ^^^^^^^^^^^^^^^^^^^^^^ variant or associated item not found in `ruby_rstring_flags`
error[E0599]: no variant or associated item named `RSTRING_EMBED_LEN_MASK` found for enum `ruby_rstring_flags` in the current scope
--> /path/to/cargo/dir/registry/src/github.com-1ecc6299db9ec823/magnus-0.3.2/src/r_string.rs:968:42
|
968 | f &= ruby_rstring_flags::RSTRING_EMBED_LEN_MASK as VALUE;
| ^^^^^^^^^^^^^^^^^^^^^^ variant or associated item not found in `ruby_rstring_flags`
Some errors have detailed explanations: E0432, E0599.
For more information about an error, try `rustc --explain E0432`.
error: could not compile `magnus` due to 3 previous errors
cargo update
しましたが、やっぱり駄目でしたね。今度は magnus が駄目そう。
Cargo.toml
を見てみると、 magnus = { version = "0.3", features = ["rb-sys-interop"] }
と指定してあり、 version 0.3 系が駄目そうということが類推されます。ということで公式ページを見ると新しいバージョンが出ているのでこちらにします。
--- a/ext/rust_uuid/Cargo.toml
+++ b/ext/rust_uuid/Cargo.toml
@@ -10,7 +10,7 @@ crate-type = ["cdylib"]
[dependencies]
rb-sys = "0.9"
rb-allocator = "0.9"
-magnus = { version = "0.3", features = ["rb-sys-interop"] }
+magnus = { version = "0.4", features = ["rb-sys-interop"] }
[dependencies.uuid]
version = "1.1.2"
再度ビルド!
$ cargo update
$ cargo build
Compiling rust_uuid v0.1.0 (/home/katsyoshi/Program/Ruby/rust_uuid/ext/rust_uuid)
Finished dev [unoptimized + debuginfo] target(s) in 0.18s
おおっ通りました!やったね!
build
ということ cargo build
通ったので gem install
しましよう。
$ cd /path/to/rust_uuid
$ bundle exec rake build
cd tmp/x86_64-linux/rust_uuid/3.2.0
/usr/bin/gmake
generating target/release/librust_uuid.so (release)
cargo rustc --target-dir target --manifest-path ../../../../ext/rust_uuid/Cargo.toml --lib --release -- -C linker=gcc -L native=/path/to/rbenv/versions/3.2.0/lib -L native=/path/to/rbenv/versions/3.2.0/lib -L native=/home/katsyoshi/.local/lib:-L/home/katsyoshi/.local/lib: -C link-arg=-lm -l pthread
Compiling libc v0.2.139
Compiling proc-macro2 v1.0.49
Compiling quote v1.0.23
Compiling unicode-ident v1.0.6
Compiling clang-sys v1.4.0
Compiling regex-syntax v0.6.28
Compiling syn v1.0.107
Compiling rb-sys-env v0.1.1
Compiling libloading v0.7.4
Compiling nom v7.1.2
Compiling aho-corasick v0.7.20
Compiling magnus v0.4.4
Compiling bindgen v0.60.1
Compiling getrandom v0.2.8
Compiling uuid v1.2.2
Compiling cexpr v0.6.0
Compiling regex v1.7.0
Compiling magnus-macros v0.3.0
Compiling rb-sys-build v0.9.53
Compiling rb-sys v0.9.53
Compiling rb-allocator v0.9.6
Compiling rust_uuid v0.1.0 (/path/to/rust_uuid/ext/rust_uuid)
Finished release [optimized] target(s) in 12.03s
cd -
mkdir -p tmp/x86_64-linux/stage/lib/rust_uuid
/usr/bin/gmake install target_prefix=
generating target/release/librust_uuid.so (release)
cargo rustc --target-dir target --manifest-path ../../../../ext/rust_uuid/Cargo.toml --lib --release -- -C linker=gcc -L native=/path/to/rbenv/versions/3.2.0/lib -L native=/path/to/rbenv/versions/3.2.0/lib -L native=/home/katsyoshi/.local/lib:-L/home/katsyoshi/.local/lib: -C link-arg=-lm -l pthread
Finished release [optimized] target(s) in 0.02s
installing rust_uuid.so to /path/to/rust_uuid/lib/rust_uuid
/usr/bin/install -c -m 0755 rust_uuid.so /path/to/rust_uuid/lib/rust_uuid
cp tmp/x86_64-linux/rust_uuid/3.2.0/rust_uuid.so tmp/x86_64-linux/stage/lib/rust_uuid/rust_uuid.so
rake aborted!
Running `gem build -V /path/to/rust_uuid/rust_uuid.gemspec` failed with the following output:
WARNING: description and summary are identical
WARNING: open-ended dependency on benchmark-ips (>= 0, development) is not recommended
use a bounded requirement, such as '~> x.y'
WARNING: open-ended dependency on rake (>= 13.0.0, development) is not recommended
if rake is semantically versioned, use:
add_development_dependency 'rake', '~> 13.0', '>= 13.0.0'
WARNING: open-ended dependency on rake-compiler (>= 0, development) is not recommended
use a bounded requirement, such as '~> x.y'
WARNING: open-ended dependency on rb_sys (>= 0, development) is not recommended
use a bounded requirement, such as '~> x.y'
WARNING: open-ended dependency on rspec (>= 0, development) is not recommended
use a bounded requirement, such as '~> x.y'
WARNING: See https://guides.rubygems.org/specification-reference/ for help
ERROR: While executing gem ... (Gem::InvalidSpecificationException)
You have specified rust based extension, but Cargo.lock is not part of the gem files. Please run `cargo generate-lockfile` or any other command to generate Cargo.lock and ensure it is added to your gem files section in gemspec.
/path/to/rbenv/versions/3.2.0/bin/bundle:25:in `load'
/path/to/rbenv/versions/3.2.0/bin/bundle:25:in `<main>'
Tasks: TOP => build
(See full trace by running task with --trace)
bundle exec rake build 32.87s user 4.33s system 291% cpu 12.741 total
なるほど? Cargo.lock
ファイルもあるし問題なさそうだな。よくわからんので rubygems のリポジトリでエラーメッセージを探してみます。
すると以下のようなコードが
def validate_rust_extensions(builder) # :nodoc:
rust_extension = @specification.extensions.any? {|s| builder.builder_for(s).is_a? Gem::Ext::CargoBuilder }
missing_cargo_lock = !@specification.files.include?("Cargo.lock")
error <<-ERROR if rust_extension && missing_cargo_lock
You have specified rust based extension, but Cargo.lock is not part of the gem files. Please run `cargo generate-lockfile` or any other command to generate Cargo.lock and ensure it is added to your gem files section in gemspec.
ERROR
end
なろほどなろほど、プロジェクトの root 直下に置いておく必要があるのね。でもそのファイルどういうものなの?
gem, bundler update
ってなわけで、今度は正式に rubygems で正式にサポートされ、 bundler でも gem を作成するときにも bundle gem --ext=rust gem_name
でスケルトンが作成されるようになりました。ということでこれを利用して Cargo.toml
を作ってみましょう
$ bundle gem --mit --ext=rust gem_name
ERROR: "bundle gem" was called with arguments ["rust", "gem_name"]
Usage: "bundle gem NAME [OPTIONS]"
ok, ok, これは bundler のバージョンが古いな
$ bundle version
Bundler version 2.3.19 (2022-07-27 commit 4f496f93e6)
さっきの PR は入ったのは Ruby 3.2 リリース直前なのでまだ入ってないよなとおもったけど、実際は Gemfile.lock
の BUNDLED WITH
が 2.3.19
を指定してるだけだったのです。
なので対象の 2 行を削除して bundle update
を実行し、続いて rust サポートした gem を生成して、 Cargo.toml
を見てみましょう。
$ bundle gem --mit --ext=rust gem_name
Creating gem 'gem_name'...
MIT License enabled in config
Changelog enabled in config
Initializing git repo in /path/to/rust_uuid/gem_name
create gem_name/Gemfile
create gem_name/lib/gem_name.rb
create gem_name/lib/gem_name/version.rb
create gem_name/sig/gem_name.rbs
create gem_name/gem_name.gemspec
create gem_name/Rakefile
create gem_name/README.md
create gem_name/bin/console
create gem_name/bin/setup
create gem_name/.gitignore
create gem_name/.github/workflows/main.yml
create gem_name/LICENSE.txt
create gem_name/CHANGELOG.md
create gem_name/Cargo.toml
create gem_name/ext/gem_name/Cargo.toml
create gem_name/ext/gem_name/extconf.rb
create gem_name/ext/gem_name/src/lib.rs
Gem 'gem_name' was successfully created. For more information on making a RubyGem visit https://bundler.io/guides/creating_gem.html
$ cat Cargo.toml
# This Cargo.toml is here to let externals tools (IDEs, etc.) know that this is
# a Rust project. Your extensions depedencies should be added to the Cargo.toml
# in the ext/ directory.
[workspace]
members = ["./ext/gem_name"]
resolver = "2"
というようなファイルが得られるので真似して作成しちゃいましょう。
[workspace]
members = ["./ext/rust_uuid"]
resolver = "2"
そうしたらもう一度ビルドしてしまいます!
$ cargo generate-lockfile
$ git add Cargo.toml Carog.lock
$ bundle exec rake build
/usr/bin/gmake install target_prefix=
generating target/release/librust_uuid.so (release)
cargo rustc --target-dir target --manifest-path ../../../../ext/rust_uuid/Cargo.toml --lib --release -- -C linker=gcc -L native=/path/to/rbenv/versions/3.2.0/lib -L native=/path/to/rbenv/versions/3.2.0/lib -L native=/home/katsyoshi/.local/lib:-L/home/katsyoshi/.local/lib: -C link-arg=-lm -l pthread
Compiling rust_uuid v0.1.0 (/path/to/rust_uuid/ext/rust_uuid)
Finished release [optimized] target(s) in 0.26s
installing rust_uuid.so to /path/to/rust_uuid/lib/rust_uuid
/usr/bin/install -c -m 0755 rust_uuid.so /path/to/rust_uuid/lib/rust_uuid
cp tmp/x86_64-linux/rust_uuid/3.2.0/rust_uuid.so tmp/x86_64-linux/stage/lib/rust_uuid/rust_uuid.so
rust_uuid 0.1.0 built to pkg/rust_uuid-0.1.0.gem.
$ bundle exec rake install
/usr/bin/gmake install target_prefix=
generating target/release/librust_uuid.so (release)
cargo rustc --target-dir target --manifest-path ../../../../ext/rust_uuid/Cargo.toml --lib --release -- -C linker=gcc -L native=/path/to/rbenv/versions/3.2.0/lib -L native=/path/to/rbenv/versions/3.2.0/lib -L native=/home/katsyoshi/.local/lib:-L/home/katsyoshi/.local/lib: -C link-arg=-lm -l pthread
Finished release [optimized] target(s) in 0.03s
installing rust_uuid.so to /path/to/rust_uuid/lib/rust_uuid
/usr/bin/install -c -m 0755 rust_uuid.so /path/to/rust_uuid/lib/rust_uuid
cp tmp/x86_64-linux/rust_uuid/3.2.0/rust_uuid.so tmp/x86_64-linux/stage/lib/rust_uuid/rust_uuid.so
$ bundle exec ruby -rrust_uuid -e 'puts RustUUID.v4'
2eb053de-8ae7-4669-853b-95f06c872300
ようやっと通ったああぁあ!!!!
conclusion
実際は11月の末あたりに head でコンパイルできないなあと気がついていたのですが、そのうち直るやろと思っててなにもしなかったのです。いざ Ruby 3.2 がリリースされたときに試して動かなかったのでやっと対応してみました。
ということで正式に Rust がサポートされるようになったのでまた YARUKI がでてきますね。
それはそうと今度は、jekyll が Ruby 3.2 で動かなくなった。
お名前ドットコムのメールがうざすぎたので DNS を Cloudflare に移行して快適生活
katsyoshi.org の登録先を お名前ドットコム にしてたけど、広告のようなメールとか届くし 更新案内と広告の違いがわからない感じのメールが大量にくるのでやめようやめようと思ってたのでいいかげん変えてみた話。
準備
準備として移行先のレジストラを選定します。 移行先としては普通のレジストラとクラウド業者がやっているレジストラがあると思いますが、今回は以下3つを候補にしました。
- Google Domains: Google がやっているやつ。メールとか Google なんで DNS まで Google にするのは心理的抵抗が強い。
- Route 53: みんなつかってる AWS のサービス。仕事で利用しているので、プライベートは別のがいいかな。
- Cloudflare の DNS: みんなだいすき低価格 CDN 業者の Cloudflare がやってる DNS サービス。
Google 、 AWS は言わずと知れた巨大企業でサービスがなくなるということはないとおもうが、 仕事で利用したり、情報全部預けたりしているところなので選択する理由が個人的には弱い。 個人利用でガンガン変えたり、 VPC でネットワーク構築するわけじゃないので Cloudflare でいいかなと。
お名前ドットコムでの作業
レジストラを移管する前に現在登録してある WHOIS 情報 を確認します。 これは移管作業で移管作業用コードが WHOIS 登録者にメールが送られてくるのでどのメールアドレスかの確認です。 ここでWHOIS 情報公開代行 を利用している場合は、 WHOIS を一旦登録時のものに変更します1。
移管
移管作業としては新規レジストラで移管依頼を参考に移管依頼ページを開きます。 開いたら、旧サービスから移管コードの発行を行ないます2。 移管コードを Cloudflare 側で入力してしばし待機。
しばらくしたら、レコードが登録されるので完了です。
おわり
ということで DNS の登録を Cloudflare へ移管しました。 お名前ドットコム はあのメールさえなければ続けたのかもしれない。 が更新警告と普通のメールの違いがあまりにもわからないので捨てることにしました。 Cloudflare で不満があったらまた変更すると思いますが、快適な生活になりました(たぶん
Communicate on IPv6 from home
家のネットワークからインターネットへ出るとき IPoE
を使ってたけど、家庭内 LAN のネットワークは IPv6
を off にしていた。
この LAN 内 IPv6
化していなかった理由としては、昔 Linux (だけじゃないかも)が IPv6
が利用できる状態だと
先に IPv6
で繋ぎにいこうとして IPv6
で通信できなかったら IPv4
にフォールバックするという挙動で、すごくストレスフルだった。
この挙動の対処として、カーネルレベルで IPv6
を off にしてました。
そうこうしているうちに契約している ISP が IPv6
オプションなしで IPoE
を利用できるようになったので、
とりあえず IPoE
だけを利用できるようにして、家庭内の LAN はそのままという状態にしていました。
いいかげんこの家庭内 LAN のネットワークを IPv6
化し、インターネットと IPv6
で通信できるようにした顛末をのこす。のこします。
家庭内 LAN 環境
- ゲートウェイ: NEC IP38X/1210 (YAMAHA RTX1200)
- PC: 6.0.0-gentoo
IPoE 化と家庭内 LAN の IPv6 化
設定は YAMAHA の設定例集に載ってあるのでそれを参考にします。
IPoE
化はこの設定例でいけるのですが、どうも設定したときにミスったらしく、 LAN の IP アドレスが IPv4
ではインターネットへ出ることができるが、
IPv6
ではインターネットへ出ることができない状態になってしまいました。
旧設定
どのような設定だったかは以下に
ip route default gateway tunnel 1
ipv6 prefix 1 ra-prefix@lan2::/64
ipv6 lan1 address ra-prefix@lan2::1/64
ipv6 lan1 prefix ra-prefix@lan2::/64
ipv6 lan1 rtadv send 1
ipv6 lan1 dhcp service server
ipv6 lan2 address auto
ipv6 lan2 secure filter in 1010 1011 1012 2000
ipv6 lan2 secure filter out 3000 dynamic 100 101 102 103 104 105 106
ipv6 lan2 dhcp service client ir=on
ngn type lan2 ntt
tunnel select 1
tunnel encapsulation ipip
tunnel endpoint address 2404:8e00::feed:100
tunnel enable 1
ipv6 filter 1010 pass * * icmp6 * *
ipv6 filter 1011 pass * * tcp * ident
ipv6 filter 1012 pass * * udp * 546
ipv6 filter 2000 reject * * * * *
ipv6 filter 3000 pass * * * * *
ipv6 filter dynamic 100 * * ftp
ipv6 filter dynamic 101 * * domain
ipv6 filter dynamic 102 * * www
ipv6 filter dynamic 103 * * smtp
ipv6 filter dynamic 104 * * pop3
ipv6 filter dynamic 105 * * tcp
ipv6 filter dynamic 106 * * udp
この設定ではインターネットとは IPv4
でしか通信できていない状態でした。
でこのトラブルシュートとして知人(@n_kane, @paina) のちからを借りてどこまで通じてどこから通じないかを確認しました。
troubleshooting
とりあえず ping6
、 traceroute6
で通じていないことを確認します。
$ ping6 -c 3 google.com
PING google.com(nrt13s55-in-x0e.1e100.net (2404:6800:4004:824::200e)) 56 データ長(byte)
--- google.com ping 統計 ---
送信パケット数 3, 受信パケット数 0, パケット損失 100%, 時間 2067ミリ秒
$ traceroute6 google.com
traceroute to google.com, 30 hops max, 80 byte packets
1 * * *
2 * * *
3 * * *
4 * * *
5 * * *
6 * * *
7 * * *
8 * * *
9 * * *
10 * * *
11 * * *
12 * * *
13 * * *
14 * * *
15 * * *
16 * * *
17 * * *
18 * * *
19 * * *
20 * * *
21 * * *
22 * * *
23 * * *
24 * * *
25 * * *
26 * * *
27 * * *
28 * * *
29 * * *
30 * * *
通ってないですね。
先述したように PC は IPv6
を on にしたばかりなのでクライアント側の設定でブロックしていないかを確認します。
まずクライアント側で考えられるのは iptables
でのパケットフィルタリングですね。
ルーターの下にある PC なので、ここでブロックすることは低いのですが、確認します。
# ip6tables --list
Chain INPUT (policy ACCEPT)
target prot opt source destination
Chain FORWARD (policy ACCEPT)
target prot opt source destination
Chain OUTPUT (policy ACCEPT)
target prot opt source destination
なにもフィルタリングしていないですね。 ということで、クライアント側には問題なさそうですね。 どこまで通じてどこまで通じていないのか別のエンドポイントで確認します。
まず、手元と相手で tcpdump
利用してパケットが送っているか届いているかを確認します。
# tcpdump -nei enp8s0f0 icmp6
dropped privs to pcap
tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
listening on enp8s0f0, link-type EN10MB (Ethernet), snapshot length 262144 bytes
22:35:41.158558 00:a0:de:69:40:9d > 33:33:ff:51:5e:63, ethertype IPv6 (0x86dd), length 86: fe80::212:e2ff:fe70:6144 > ff02::1:ff51:5e63: ICMP6, neighbor solicitation, who has 2409:10:a5c0:1f00:d903:ddb5:851:5e63, length 32
22:35:58.765783 ee:60:51:38:43:9c > 33:33:ff:00:00:01, ethertype IPv6 (0x86dd), length 86: 2409:10:a5c0:1f00:5ce3:afcb:9bb2:5c1b > ff02::1:ff00:1: ICMP6, neighbor solicitation, who has 2409:10:a5c0:1f00::1, length 32
22:36:09.249427 ee:60:51:38:43:9c > 33:33:ff:00:00:01, ethertype IPv6 (0x86dd), length 86: 2409:10:a5c0:1f00:5ce3:afcb:9bb2:5c1b > ff02::1:ff00:1: ICMP6, neighbor solicitation, who has 2409:10:a5c0:1f00::1, length 32
送れてはいるようで対向側にも届いていたらしく戻りのパケットは送ってたようですが、手元では戻ってくるパケットは見えていないですね。
ということはやはりルーターの設定が悪そうということが推察されますね。 設定のどこが悪いのかあやしいところを見ていきます。
最初に怪しいとおもったのはルーターのフィルターまわりです。一旦、no ipv6 interface secure filer in
で全部無効化しましょう。しかしとくに変りはないです。これはフィルターが原因ではなさそうですね。
次にあやしい点は以下2つの設定
上のコマンドは ISP から自動で振ってきている IP を割り当てる設定で、下のコマンドは ISP から振ってきた IPv6
のプレフィックスを付けるようにするための設定となります。
どうもこの下のコマンドが邪魔なようです。
no ipv6 interface prefix
で無効化できるので一旦外してみましょう。
すると
$ ping6 -c 3 google.com
PING google.com(nrt20s18-in-x0e.1e100.net (2404:6800:4004:81c::200e)) 56 データ長(byte)
64 バイト応答 送信元 nrt20s18-in-x0e.1e100.net (2404:6800:4004:81c::200e): icmp_seq=1 ttl=57 時間=6.53ミリ秒
64 バイト応答 送信元 nrt20s18-in-x0e.1e100.net (2404:6800:4004:81c::200e): icmp_seq=2 ttl=57 時間=2.73ミリ秒
64 バイト応答 送信元 nrt20s18-in-x0e.1e100.net (2404:6800:4004:81c::200e): icmp_seq=3 ttl=57 時間=2.81ミリ秒
--- google.com ping 統計 ---
送信パケット数 3, 受信パケット数 3, パケット損失 0%, 時間 2002ミリ秒
rtt 最小/平均/最大/mdev = 2.731/4.023/6.532/1.773ミリ秒
のように通りました!勝ったッ!第三部完!
おわり
以前中途半端に設定したおかげで IPv6
有効化に時間が掛ってしまった顛末をまとめました。
これで家のネットワークから IPv6
で通信できるようになりました。
ありがとうございました!
次回 katsyoshi.org お名前.COM やめるってよ
RubyKaigi 最高!
RubyKaigi 2022 に行ってきました! ので感想を
DAY 0
今回も やんちゃハウス に楽をして泊まることにしてたのでなにも気にせず宿へ。 着いて他メンバーが来るまで宿で待機してたが、最寄り駅の周りが思った以上になにもなく、途方に暮れてました。
夕飯
もうひとり宿に着いたので入れ変わりで夕飯へ出かけたが、そもそも最寄り駅前にはなにもないので伊勢駅まで出ることにした。
この判断が間違えてたようで、伊勢駅に出てもとくに変らなかった。しかし幸い伊勢駅前に担々麺屋があったのでそこで食料摂取した。
DAY 1
この日の聞いたセトリと感想は以下のとおり
Ruby meets WebAssembly: presentation
この日のキーノートは Ruby
の wasm
対応の話で wasi
を通じてブラウザで Ruby
が動かすまでのたのしいことをしゃべってたようです。
おなかいたくてトイレ行ってたら面白いところ聞き逃がしてしまった。
ランチ
適当に幕の内弁当を最初に選択。
Making *MaNy* threads on Ruby: presentation
笹田さんの発表で如何に Ruby
で CPU をフルに使えるようにするかの話。 Ruby
での Thread
のはなしから Ractor
を利用してフルフルに CPU 利用するための書きかたとかの話していました。
Building a Lightweight IR and Backend for YJIT: presentation
Ruby 3.1
で YJIT
が入って Ruby 3.2
でサポートされる CPU アーキテクチャが増えたはなし。RubyVM(?) に YJIT
用 Ruby IR
を作成して JIT
を実行するという話。
聞いてて疑問になった点としては、 Ruby IR
では直接機械語に翻訳しているようだったが、このバックエンドに LLVM
を利用していないのはなんでなんだろう。聞き逃がしているという話はあるので、詳しいひと教えて。
Tools for Providing rich user experience in debugger: presentation
debug.gem
を VS Code
だけじゃなく、 Chrome
のリモートデバッグ機能を利用したデバッグの話でした。
この機能自体はすごく便利だと思うのですが、そもそも両方とも宗教上の理由で利用できない体なので聞くだけでした。
TRICK 2022 (Returns): repos
いつも通り 変な エキセントリックなプログラムしか出てこなかったし、いつもの面子が賞もらってたのですごいなーという感想。
夕飯
この日は津駅があまりなさそうだったので、駅近くで飲むのではなく、宿寄りの場所松坂駅で飲むことにした。 ここで、津の夜情報を聞き込んだり、朝ごはん情報自体を仕入れてました。
昨日の夕飯 #rubykaigi pic.twitter.com/G8lmetYX9J
— katsyoshi (@katsyoshi) September 8, 2022
DAY 2
2日目は、ネットワークが不安定になったりしてこちらの集中力が切れたりしたのであまり覚えていない。
朝ごはん
前日の夕飯に教えてもらった市場の人向け食堂が松坂駅前にあり、その食堂で食事をした。
朝メッシ #rubykaigi pic.twitter.com/PvbaUrRUIf
— katsyoshi (@katsyoshi) September 8, 2022
Matz Keynote: presentation
Do Pure Ruby Dream of Encrypted Binary Protocol?: presentation
ノーコメント。
ランチ
この日も一番に弁当を取得。弁当はみんな大好き松坂牛ローストビーフ丼にした。感想としては、もう肉はいいかな。
昼飯 #rubykaigi pic.twitter.com/vpOf6Uwf0K
— katsyoshi (@katsyoshi) September 9, 2022
自キKaigi
きょうも #自キKaigi やります。ランチ食べたらスポンサーエリアの手前あたりにお集まりください #rubykaigi
— 🇺🇦hasumikin🇺🇦 (@hasumikin) September 8, 2022
誘われたので参加しました。iPad で活動しようと思い corne 持っていってたので参加してた。
Ruby x BPF in Action: How important observability is: presentation
Hunting Production Memory Leaks with Heap Sampling: presentation
このあたりネットワークが不安定になり、セッションが中断されたりしたためあまり覚えていない。
立ち話
久し振りの対面での参加だったのでセッションあまり聞かずに受け付け前で立ち話している人たちがいたので、その立ち話に参加した。 たまたま、最近読んでる本の翻訳者がいたのでこれからの話などを聞いたりしていた。
Caching With MessagePack: presentation
Ruby Committers vs The World
夕飯
夕飯は、駅近くのお店でかるく3人で。
いまどこ
— tagomoris (@tagomoris) September 9, 2022
こんなリプライがきてたようなので一緒にいたひとにまかせた。ギリギリまで粘ってたので帰宿後すぐ就寝。
DAY 3
3日目はそもそも前2日の疲れが出たりしたのかほとんど覚えていない。あと ruby puzzle をやっていてセッションをあまり聞けていない。
朝ごはん
今日も一日 #rubykaigi pic.twitter.com/PKnj8sHxwE
— katsyoshi (@katsyoshi) September 9, 2022
Megaruby - Running mruby/c programs on Sega Mega Drive: presentation
これに間に合うように出たはずだったが、間に合わず聞いていた。メガドライブすげえ。
Automatically Find Memory Leaks in Native Gems: presentation
Fast data processing with Ruby and Apache Arrow: presentation
Fixing Assignment Evaluation Order: presentation
Stories from developing YJIT: presentation
まとめ
3年ぶりの現地開催だったので参加してきました。対面での参加はやはりよいもので新しい出会いや、疑問を本人、解っている人に直接聞くことができてすばらしい体験でした。
三重について
三重県の酒のイメージは、行く前は「作」「 而今」「伊勢角谷屋麦酒」というイメージでした。 とくに日本酒は美味しいイメージがあり、知らない日本酒もあったりするんだろうなあと思っていました。 行ってみると日本酒はイメージと変わらないのですが、だいたい似た味の傾向で飽きるというかなんというか 美味しいんだけど、好きじゃないという感想になってしまった。 ビールについては瓶ビールしか飲まなかったので感想はなしで。
三重は大きな街が比較的分散しているようで、津も松坂も伊勢も鳥羽も駅前が小さく感じました。 とくに0日目の食事が大変で、調達が難しかった。
Ruby gem で Rust をつかって爆速にしたい!!!!!!11
Ruby Gems で Rust が Native として利用可能になった のでとりあえず UUIDv4
を生成してみた。
リポジトリ
準備
Ruby 側の gem
に Rust を利用する準備として rb_sys
と rake-compiler
を利用します。この二つの gem
は native compile するためにインストールしておきます。
Rust 側から Ruby へ関数を公開するために rb-sys
と magnus
を利用します。
gem install
とりあえず cargo
で Rust のパッケージを作って Rust を書いてみます。
> bundle gem rust_uuid --mit --ext rust_uuid # --ext を指定してnative build する gem を作成
> cd rust_uuid # 作成した gem のディレクトリへ移動
> cd ext/rust_uuid # ビルドするディレクトリへ移動
> cargo init . --lib # cargo を初期化
> rm -f *.c *.h # C のファイルが生成されるので削除
> cargo add rb-sys rb-allocator
> cargo add magnus --features rb-sys-interop
> cargo add uuid --features v4 # uuid v4 を指定
ext/rust_uuid/extconf.rb
を以下のように編集します。
@@ -1,5 +1,6 @@
# frozen_string_literal: true
require "mkmf"
+require "rb_sys/mkmf"
-create_makefile("rust_uuid/rust_uuid")
+create_rust_makefile("rust_uuid/rust_uuid")
次に ext/rust_uuid/src/lib.rs
を以下の様に変更します。
use magnus::{define_module, function, prelude::*, Error};
use rb_allocator::ruby_global_allocator;
use uuid::Uuid;
ruby_global_allocator!();
// UUIDv4 を文字列として公開
fn v4() -> String {
Uuid::new_v4().to_string()
}
#[magnus::init]
fn init() -> Result<(), Error> {
let module = define_module("RustUUID")?;
// RustUUID.v4 と利用するようにシングルトンメソッドを定義
module.define_singleton_method("v4", function!(v4, 0))?;
Ok(())
}
これまでできたら一旦 Rust をコンパイルしましょう。
> cd ext/rust_uuid
> cargo build
> git add .
> rake build
.... # install cargo dependencies and packages
cp: '/home/katsyoshi/Program/Ruby/rust_uuid/tmp/x86_64-linux/rust_uuid/3.1.2/target/release/librust_uuid.so' を stat できません: そのようなファイルやディレクトリはありません
gmake: *** [Makefile:551: foo_bar.so] エラー 1
rake aborted!
Command failed with status (2): [/usr/bin/gmake...]
Tasks: TOP => build => compile => compile:x86_64-linux => compile:foo_bar:x86_64-linux => copy:rust_uuid:x86_64-linux:3.1.2 => tmp/x86_64-linux/rust_uuid/3.1.2/rust_uuid.so
とエラーになります。
これは ext/rust_uuid/Cargo.toml
の設定が足りていません。そこで以下を追加してみてください。
[lib]
crate-type = ["cdylib"]
追加したら gem
をビルド&&インストール&&試してみましょう!
> rake install
....
> ruby -rrust_uuid -e 'puts RustUUID.v4'
2be6f4d2-200b-4d08-9a1a-11fa523b316b
べんちまーく
以下、 SecureRandom.uuid
との比較用のベンチマークコードを示します。
require "benchmark/ips"
require "securerandom"
require "rust_uuid"
Benchmark.ips do |x|
x.report("standard") { SecureRandom.uuid }
x.report("rust lib") { RustUUID.v4 }
x.compare!
end
結果発表〜
Rust を利用することでだいたい 5 倍ほど速くなっています。
> ruby bentimark.rb
Warming up --------------------------------------
standard 36.437k i/100ms
rust lib 177.585k i/100ms
Calculating -------------------------------------
standard 365.407k (± 1.4%) i/s - 1.858M in 5.086491s
rust lib 1.793M (± 1.8%) i/s - 9.057M in 5.053179s
Comparison:
rust lib: 1792925.9 i/s
standard: 365407.3 i/s - 4.91x (± 0.00) slower
ruby bentimark.rb 9.88s user 4.30s system 99% cpu 14.175 total
TOO HAYAI
まとめ
簡単に Rust を利用して速くしてみました。
思った以上に速くなっていたので重い処理をする場合に C
や C++
以外でも簡単に利用できるようになって
選択肢が増えたのはよいことでした。
実はこの uuid
crate の features に fast-rng
を追加すると 10 倍速くなるんですが、 ruby 側の終了時に SEGV
してしまうので載せていないです。 SEGV
しないように原因を調査などはまた今度。
Hello, Wezterm
tmux
+ Allacritty
が疲れてきたのではてぶで流れてきた wezterm
が sixel
を利用できてよさそうだったので試してみることにした。
設定
設定ファイルが lua でカスタマイズがいろいろとできるのでまずは色を代えてみます。
local wezterm = require 'wezterm'
return {
color_scheme = "Dracula",
}
プログラミング言語でカスタマイズができるので以下のようにアクティブなタブへの移動のキーバインドのカスタマイズができます。
local wezterm = require 'wezterm'
local move_keys = {}
for i = 1, 9 do
table.insert(move_keys, {
key = tostring(i),
mods = "CTRL",
action = wezterm.action{ ActivateTab = i - 1, },
})
end
return {
color_scheme = "Dracula",
disable_default_key_bindings = true, -- 初期のキーバインドは利用しない場合
keys = move_keys,
}
こんな感じで設定できるので便利です。
このキーバインドは任意のイベントも設定でき、任意のイベントを利用してアクションを定義できます。以下の例では、Paneを開い監視用のプログラムを開きます。
local wezterm = require 'wezterm'
wezterm.on("open-nvtop-and-ytop", function(win, pane)
win:perform_action(wezterm.action{ SplitHorizontal = { domain = "CurrentPaneDomain", args = { "nvtop", }, }, }, pane)
win:perform_action(wezterm.action{ SplitVertical = { domain = "CurrentPaneDomain", args = { "ytop", "-ps", }, }, }, pane)
end)
return {
keys = { { key = "r", mods = "CTRL", action = wezterm.action{ EmitEvent = "open-nvtop-and-ytop", }, }, },
}
とすると以下のようになります。 便利!
こんな便利なものということで systemd
でデーモン化しています。
[Unit]
Description=GUI Accellarated terminal
Documentation=
[Service]
Type=forking
ExecStart=/usr/local/bin/wezterm-mux-server --daemonize
Restart=on-failure
[Install]
WantedBy=default.target
で起動しておいています
問題点
と設定ファイルの例書いてみたのですが、とても大きな問題点にブチあたったので書いておきます。
wezterm
には wezterm-mux-server
というマルチプレクサ(tmuxのように扱うため)のサーバーモードプログラムがあるのですが、こいつがどうも wezterm
とは挙動が異なり、前述した監視用のキーバインドが微妙に異なった挙動となってしまっています。サーバーモードに接続した場合の挙動は以下のようになります。
1つ目はpaneの位置が期待したとおりになっていない。2つ目は ytop
が起動していないというので2つ目の方は気にしなければいいのでまあいいかと思っている。1つ目の問題は許容できていないので一旦はこのキーバインドは封印となっています。
おわり
長年利用してた tmux
を捨てて wezterm
を利用しはじめた。
設定が lua
で書けるのが体験的にとても良いのでこれからも利用するかなと。
wezterm
で sixel
利用した画像表示ができるようになったのが便利なので「よしっ!」
冬やすみ
冬やすみの間、やりたいこと、やっといたほうがいいやつをやってました。 ひとつは xremap の設定ともうひとつは CO2-mini から CO2 を見える ようにした。
今回は、 mackerel で見えるようになった CO2 の値を Slack へ定期的に投げるようにします。今回も Rust を利用しています。
準備
準備として、 mackerel 1 と Slack 2 両アプリケーションの投稿 API 用 Token をそれぞれ用意します。 各公式ページにあるように生成、取得するだけでよいです。
mackerel 側は ホストメトリック API を利用します。
Slack 側は chat.postMessage API を利用します。
各 API に対して取得した API Token を用いて curl
で確認しておきます。
実装
今回は対話式の bot ではないので、 RTM を利用せずに、HTTP クライアントだけで構成しています3。 Rust の HTTP クライアントとして hyper4 を利用します。 TLS は hyper_tls を利用しています。
実装とは言っても対象の mackerel の APIを叩き値を取得して、 その値を元に Slack へポストするだけです。
mackerel での値取得時に気をつける点としては、ホストメトリック API では host名
ではなく、
host id
がパラメーターとなっていますので注意が必要です。
まずレスポンスを入れる構造体を定義します。
#[derive(Deserialize)]
pub struct Metric {
pub time: i64,
pub value: i16, // 今回は co2 の値なので i16 としている
}
#[derive(Deserialize)]
pub struct ResponseMetrics {
pub metrics: Vec<Metric>,
}
つぎに以下のようにしてリクエストを組みたてて、値を取得しています。
let https = HttpsConnector::new();
let req = hyper::Request::builder()
.method(hyper::Method::GET)
.uri(url)
.header("X-Api-Key", api_key)
.body(hyper::Body::empty())?;
// https として request する
let client = hyper::Client::builder().build::<_, hyper::Body>(https);
let res = client.request(req).await?;
let body = hyper::body::aggregate(res).await?;
let json: ResponseMetrics = serde_json::from_reader(body.reader())?;
let metrics = json.metrics;
値を取得したら、今度は同じように Slack の方も構造体を定義します。
// リクエスト用構造体
#[derive(Serialize)]
pub struct SlackMessage {
pub channel: String,
pub sub_type: String,
pub text: String,
pub username: String,
pub as_user: bool,
}
// レスポンス用構造体
#[derive(Deserialize)]
pub struct PostMessage {
#[allow(unused)]
channel: String,
}
リクエストを組みたてて、POSTします。 見てわかると思いますが、ほとんど mackerel と変わらないです。
// リクエスト body を json に変換
let json = serde_json::to_string(&SlackMessage {
channel: "channel".to_string(),
sub_type: "bot_message".to_string(),
text: "message".to_string(),
username: "botname".to_string(),
as_user: true,
})?;
let https = hyper_tls::HttpsConnector::new();
let req = hyper::Request::builder()
.method(hyper::Method::POST)
.uri(url)
.header("Content-Type", "application/json")
.header("Authorization", format!("Bearer {}", get_env("SLACK_API_KEY")))
.body(hyper::Body::from(json))?;
let client = hyper::Client::builder().build::<_, hyper::Body>(https);
let res = client.request(req).await?;
let body = hyper::body::aggregate(res).await?;
let _json: PostMessage = serde_json::from_reader(body.reader())?;
ポストするメッセージを作る際に2つのことをしています。
まずは time
の UNIX EPOCH TIME からローカルの時間を表示するようにしています。
それと CO2 の値に依って絵文字を追加するかどうかを入れています。 -1
とか予定していない値が入ってきた場合は panic!
するようにしています。
// chrono を利用して unix time からローカルの文字列へ変換
let t = chrono::Local.timestamp(time);
let v = match value {
0..=700 => ":large_green_circle:",
701..=1000 => ":large_yellow_circle:",
1001.. => ":red_circle:", // なんで slack は :large_red_circle: を用意していないんだろうか
_ => panic!("unexpected number!!"),
};
こうやってポストされたメッセージは以下のようになります。
絵文字つきでポストされましたね。
まとめ
Rust で bot を作ってみました。 と言ってもただの HTTP クライアント な bot なだけですけど。 一旦 Slack でも見えるようになったので今度は Nature Remo と連携して気温や湿度での自動化ができたらいいな。
-
https://mackerel.io/ja/api-docs/ ↩
-
https://slack.com/intl/ja-jp/help/articles/215770388-API-%E3%83%88%E3%83%BC%E3%82%AF%E3%83%B3%E3%81%AE%E7%94%9F%E6%88%90%E3%81%A8%E5%86%8D%E7%94%9F%E6%88%90 ↩
-
公式の README にあるようにこの場合は reqwest を利用するほうがよかったかもしれない。TLS は直接 hyper が対応していなかったりしてすこし面倒です。 ↩