k0kubun/ruby-jit-challenge 完了報告

16.85倍はやいベンチマーク結果
16.85倍はやい僕のJIT

RubyKaigi 2023 お疲れ様でした。非常に面白かったですね。k0kubun さんのRJITについての発表が面白かったので ruby-jit-challange をやりました。無事完走できたので感想記事を書こうと思います(激ウマギャグ)

※: ベンチマークから rjit が抜かれてる理由は後述

RJIT / ruby-jit-challenge ってなに

k0kubun さんが RubyJIT を書けるような機構を作ってくれました。それをRJITと呼びます。RJIT を手を動かして体験できるチュートリアルが公開されています。親切なREADME、命令列をJITするところ以外の部分、機械語生成用のヘルパーも用意されておりおもてなしの精神がすごい。みんなぜひやりましょう

github.com

なお冒頭で16倍はやいっていっていますが、特定ベンチマークにしか対応しておらず、そのへんの ruby スクリプトのうち1%もまともに動かないような状態だと思います。これをちゃんと全部カバーするとどんどん遅くなるんでしょうね……。

やろうかなと思っている人へ

やりましょうとは言いましたがやる人が挫折しないようにアドバイスを。

ヒントは見るべき

README にヒントがありますがヒントを見るのをためらわないでください。私はCコンパイラ自作ちょっとやってみたし、MRI の実装眺めたり適当にいじったりしたこともあるからヒントなしでいけるか〜とか思ったけど全然いけませんでした。ヒントなしで要求されることは例えば以下のようなことになります。

  • YARV の命令列を見て意味がわかる
  • 命令の name 以外の情報がどこにあるかわかる
  • nil や false や Integer が RVALUE としてどういう byte 表現になっているかわかる
  • スタックマシンを前提としたアセンブリがどんなもんかわかる
  • RJIT Compiler 上から必要なデータに触れればいいかわかる

(他にもあったような気がするがぱっと出てこない)

上述の知識がないという自覚がある人はヒントをガンガン読んでそこから学べばよいと思いました

やってみたらプロセスが返ってこなくなった

実質アセンブリ書いてるようなもんなのでSEGVとかcfp不一致とかでひたすら怒られるわけですが。ruby/ruby とかでそんなことが起きても特に問題なくプロセス終了してくれるじゃないですか。今回はそういう安全機構も自分で書かないとだめらしいです(というのを飲み会で聞いた気がするが間違ってたらすみません)。そんなことやってられないので踏んだら docker コンテナをまるごと落としましょう。だいたい以下のようなコマンドをやってました

# bin/ruby の引数とかオプションは開発状況に合わせてお好みで。
$ docker run --platform linux/amd64 -it -v "$(pwd):/app" k0kubun/rjit bash -c "bin/ruby --rjit-dump-disasm test/lt.rb" 

# かえってこなかったら別タブでこんな感じに。grep とかは一意に指定できる程度にどうぞ
$ docker ps | grep k0kubun | awk '{print $1}' | xargs docker kill

bin/bench で rjit が落ちる

なんか僕の環境だと落ちました。test/fib.rb の時点で落ちるけど。k0kubun さんに報告しているのであんまり深追いしていません。

どうだった?

面白かったですね。Cコンパイラ自作頓挫勢ですが、アセンブリDSLがよくできていて、文字列操作で書いてたのに比べてずいぶんきもちよかったです。実際何もかもを捨て去ったJITコンパイラだととりあえず10倍くらいはやいのがかけるってのは素直にすごいなーとなりました。そういえばCコンパイラよりもより局所的にアセンブリを書くことになるのでそういう意味でもこっちから入門したほうが楽かもしれません。

コンテンツ的に面白かったし自分がJIT書くってことが人生であると思ってなかったので id:k0kubun さんに感謝。また、こんなに面白かったのに完走報告がいままでなかったらしいのでみんなやればいいのに〜という感じで記事を書いています。

↓僕の実装です。まあ特に見る必要はなく本家の正解ブランチを見ればいいと思います。

github.com

オフラインリアルタイムどう書く問題作成リハビリ #0 の回答

nabetani.github.io

解きました。

github.com

初回回答が70分経過時点。全部パスするけど結構遅い。 そのあと10分くらい経過して全テストケースを27秒(遅いが……)で通しました。Rubyでの実装です。

以下方針について

続きを読む

社内で英語の技術発表をした

しました、というだけなのですが一応備忘程度に書いておきます。詳細な内容については社外に出せないのでこの記事で紹介することはありません。ご了承ください。

内容は、前Qくらいまで開発していた新サービスと、今開発してる新サービスについてのご紹介。product requirement の話もちょっとしたけど主にはテクニカルにどういう課題があってどういう設計にしてどこが大変だったのかとかそういう話でした。ボリューム的には40分くらいで発表が終わり、質疑応答を追加で、という感じ。スライド枚数でいうと33枚でした。

会自体はそもそも開発チームのもちまわりで社内 Tech Talk を順番に回していくような会があり、うちのチームからも誰かやってくれ〜という話になってやりました。あんまり自分からこういうのに立候補するようなタイプじゃないので上からやってくれっていうのが振ってきたのはいい機会だったような気がします。自分からやらないという理由は、やったこと・理解したことについてブログ書いたり資料作ったりすると、「いやこれもう知っててつまんねえな」「なにを当然のことをぐだぐだと」という気持ちが先立ってしまい興味が消えていくというのが主因です。今回の依頼はそもそもチーム外の人への説明なのでオーディエンスが明らかであり、またチームメイトと話してたら確かに最近やってたことはおもしろいなと認識できたのでなんとかやりきることができました。

発表については、日本語だと早口になりがちという問題がありましたが、英語だと文章が出てくるまで詰まることがあるので、たぶん、そうでもないんじゃないでしょうか。しらんけど。いや、振り返ったほうがいいのは知ってるけど自分で自分の英語を聞くととてもつらくなるので全然聞けていません。「ときどき詰まるけど詰まってないところは早口」という最悪ケースをいま思いつきましたがまあ忘れます。

日本語の資料だと、文字を少なめに、口頭での説明でカバーするスタイルを目指しているのですが、英語だし、話すよりも書くほうが得意なので割と文章をスライドに書くスタイルにしちゃいました。zoom 時代だから資料が見にくいこともあんまりないし。図がばーん、みたいなページもいくつかはあったのですが、スピーカーノートにカンペを書くことによりだいたい読んでればOKという状態にもっていけました。まあ実際の発表だとテンションがあがってあんまりカンペを見なかったりするんですが……。

全体としてはよくできたと思っていて、内容ももりだくさんだったし質問もたくさんもらってたくさん答えました。1回、質問者の英語を正しく聞き取れず、とんちんかんな答えをしてしまったのが悔やまれますね。チームメイトが表れて正しく回答して去っていったのに助けられました。

割と楽しかったのでまた*1やってもいいなと思いました。

*1:1年後くらいに

AWS Lambda Function と Go でシュッとやってバーンってしたい

前提

  • AWS Lambda Function でWEBリクエストをうけとってなんかしたい
  • Go 使う。ふつーのWEBアプリケーションはかけるものとする
  • 設定少なくさっさと立ち上げたい

最終形

Lambda Function + Function URL w/CORS を立てる。バイナリは↓で作っておく。

なお以下のソースコードは既存のものから不要なものを載せないよう切り貼りしたものであり、これ単体での動作確認は行っていないので雰囲気で読み取ってください。

// cmd/lambda/main.go
package main

import (
    "github.com/aws/aws-lambda-go/lambda"
    "github.com/awslabs/aws-lambda-go-api-proxy/httpadapter"

    "github.com/hkdnet/tekitou/handler"
)

func main() {
    mux := handler.NewHandler()
    lambda.Start(httpadapter.NewV2(mux).ProxyWithContext)
}
// handler/handler.go
package handler

import "github.com/gorilla/mux"

func NewHandler() http.Handler {
    // なんでもいいけどとりあえず gorilla にしておく。
    r := mux.NewRouter()
    // てけとーにルーティング足す
    return r
}

ローカル確認はこんな感じにしておくとよい。

// cmd/local/main.go
package main

import (
    "fmt"
    "net/http"
    "os"
    "strings"

    "log"

    "github.com/hkdnet/tekitou/handler"
)

var corsHeaders map[string]string

func init() {
    corsHeaders = make(map[string]string)
    corsHeaders["Access-Control-Allow-Origin"] = "*"
    corsHeaders["Access-Control-Allow-Methods"] = "GET,PUT"
    corsHeaders["Access-Control-Allow-Headers"] = "content-type"
}

// CORS header. This is set by Lambda Function URL in production. Our lambda handler doesn't need to care CORS.
// But in local we do need it.
func setCorsHeaders(h *http.Header) {
    for k, v := range corsHeaders {
        h.Set(k, v)
    }
}

type respWriterWithCors struct {
    w http.ResponseWriter

    wroteHeader bool
}

func (w *respWriterWithCors) Header() http.Header {
    return w.w.Header()
}
func (w *respWriterWithCors) Write(b []byte) (int, error) {
    if !w.wroteHeader {
        w.WriteHeader(http.StatusOK)
    }
    return w.w.Write(b)
}
func (w *respWriterWithCors) WriteHeader(statusCode int) {
    h := w.Header()
    setCorsHeaders(&h)
    w.w.WriteHeader(statusCode)
    w.wroteHeader = true
}

func main() {
    h := handler.NewHander()

    http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        if strings.EqualFold(r.Method, "options") {
            h := w.Header()
            setCorsHeaders(&h)
            fmt.Fprintf(w, "")
            return
        }
        w = &respWriterWithCors{w: w, wroteHeader: false}
        h.ServeHTTP(w, r)
    })

    log.Println("start listening")

    err := http.ListenAndServe(":3001", nil)
    if err != nil {
        fmt.Fprintf(os.Stderr, "%s", err)
        os.Exit(1)
    }
}

紆余曲折部分

普通にやってると aws-lambda-go にいきつくと思うんだけど、これはそもそもリフレクションバリバリつかっていてちょっと追いにくい。

github.com

そして Function URL を使えば API Gateway なくてもいいらしいという風のうわさを聞いたものとしては API Gateway をはさみたくない。ついでにいうとルーティングも自前で解析しないで Go の既存のWEBフレームワークとかにお願いしたい。

そのへんを解決してくれるのがこれ。

github.com

Function URL 対応とは一言も書いてないが API Gateway V2にすれば普通に動く(ということはおそらく Function URL + aws-lambda-go の APIGWv2でも動くはずだが未検証)。でもとりあえず http.Handler なものを返せば動いてくれるので考えることが減って楽

次の問題はどうやってローカル確認するか。これも http.Handler があるので上述のように cmd を2つに分けてローカル確認用と実際にLambdaに置く用でそれぞれエントリポイントを用意すればよい。

ここまできて最後の問題がCORS設定。Function URL は CORS の設定があるのでこれをそのまま使えたほうが楽。だけどローカルでは当然そんな便利なやつはないので書く必要がある。それが respWriterWithCors 部分。ローカルでサーバを立てる前に ResponseWriter をラップしてあげればよい。

これでだいたい動くものができるので、あとは中身をどうにかするだけ。

謝辞

本記事の構成にいたるまで、takeshinoda さんにアドバイスをたくさんいただきました。ありがとうございました!

GitHub + Terraform Cloud で不幸にも workspace が大量にあるときに変更がある workspace だけピックアップするやつ

workspace が大量すぎて困っている人は通常あんまりいないと思うが弊社では非常に役に立っている。

以下のブックマークレットを作って叩けばよい。

javascript:(function(){ [...document.querySelectorAll('.merge-status-item')].filter(e => e.textContent.includes("Terraform plan has no changes") || !e.textContent.includes("Details") ).forEach(e => {e.className = '';  e.style.height = 0; e.style.padding = '0'; e.innerHTML = ''}) })()

2021年12月の近況

この記事は

この記事は Rubyist近況[1] Advent Calendar 2021 - Adventar の5日目の記事ですが、現在2021年12月23日18時くらいです。これは、5日目の投稿をブッチしたわけではなく、空いているので埋めているのであり、私が怠惰であることを示すわけではないということを念の為書いておきます*1

4日目は、zunda さんの記事でした。

zenn.dev

近況

近況は99日前のこの記事でだいたい書きましたが、重複することも含めてつらつら書きます。

hkdnet.hatenablog.com

仕事

相変わらずトレジャーデータというところに勤めています。Ruby も書いています。ここ2週ほど手元でゴニョゴニョしてるのは Go と JavaScript ですが、チームのメインリポジトリRails ですし、主な仕事もそっちです。Ruby のお仕事は弊社でもまだたくさんありますので、ご興味がある方は僕にご一報のうえ、こちらからどうぞ。無事採用となった場合にはリファラルボーナスがあった気がするのでとてもよいご飯を還元できると思います。

仕事内容としては、エンタープライズ〜という感じの機能を作ったり、英語を読んだり英語を書いたりしています。せっかくなのでビッグデータ〜という感じに近い機能についてもガリガリやりたい気持ちはありますが、めぐり合わせとかの関係もありやってませんね。あんまり自分のキャリアや好みの領域に明確な指針がなく、会社としてこれやりたいんですね、じゃあ空いてるんで僕入りますか、みたいな感じになりがちです。

英語は相変わらずそんなにできないわけですが、英語がどうこうというより多数の言語、タイムゾーンに属するメンバーがいる中でプロジェクトを前にすすめるというところをちゃんとやっていく必要があり、それには胆力が必要となることが多く、やっていき〜という感じです。ちゃんとやっていけてないからこんなことを書いているわけですが。規模的には当然自分でなにかやったら全部終わるみたいになることが少なく、開発側だけでもチーム横断になるし、プロダクト側ともいい感じにやっていく必要があり、それをちゃんと終わらせるというのはやはり労力がいるものだなあと思います。

仕事2

副業のほうも続けているわけですが、こっちもこっちでありがたいことに人が増えてきており、ただの開発者としてではなく、どうやったら全体としてのアウトプットがいい感じになるかということを素人ながら考えたりしています。

人が増えているとは言いましたが、フロントエンドの人がまだまだ足りてないので募集中です。副業とか業務委託からお願いすることになると思うのでこのへんを読んで興味がありましたらぜひ。なおこちらについても応募前に僕にご一報いただけるとリファラルボーナスがありますのでなんらかの還元ができると思います。

生活

相変わらず料理をずっとしています。まだ飽きないですね。仕事でヘロヘロだったりすると作る気にならなかったりしますが。最近はあまりに日常になりすぎたせいで特に写真撮ったりもしなくなりました。趣味であり、生活の一部であるという。

家ではビールをやたらと飲んでいたのですが、特に飲むぞうおおおという気持ちでないときにはご飯と一緒ならビアリーでよい気がしてきました。もちろん酒を味わうぞというときには物足りないのですが。ノンアル系はどうにも苦手なのですが、ビアリーには慣れたのでこのまま進めばいけるようになるかもしれません。というか別に常備されてればジンジャーエールとかでもいいという説はあります(未検証)。健康のためにはアルコールが少ないほうがいいのはそのとおりなのですが、特に財布への負荷が軽くなってないのが気になるところではあります。別にお金のかかる趣味があるわけでもないので食と酒には好きにお金を使っていいとは思っているのですが、低アルの割には高いですよねという気持ちになってしまう*2。ビール以外にも日本酒をよく飲んでおり、近くに好きな酒屋も出来たのですが、いかんせん酒自体を記憶するのが非常に苦手で、ブログに書くほどの何かはありません。

そういえば家がほしい気持ちが高まり、いろいろなんやかんやしています。2人暮らしが1年経ちだいたい必要な家の感じとかがわかってきたこと、今の家の家賃を考えると買ってしまってもだいたいなんとかなること、結婚式も終わり新しいことを検討する余裕ができたことなどからです。家、何もかもがわからず、ひたすらブログやら情報サイトを眺めたりしてなるほど〜という気持ちになったりしています。

娯楽系でいうと、一時期SEKIROブログになっていたようにせきろにかなりの時間を費やしていました。トータル120時間だそうです。ゲームはじゃあしばらくいいかな、と思っていたところハロウィンセールかなにかで兄がダクソ3を始めたらしく、じゃあやるかと思っていまはダクソ3をやっています。こっちはまだ43時間程度なので安心ですね。1周目を脳筋ビルドでラスボス倒してDLCがまだ、といったところですが、歯ごたえが足りなく感じてきたので新プロフで始めているところです。難易度については、SEKIROよりかんたんな気がしますね。SEKIRO は基本的にボスに勝つためにはボスを理解する必要がありますが、ダクソ3のほうではそもそも探索真面目にやってたらレベル上がってゴリゴリ殴ってたら勝てるみたいになりがちでした。完成がはやい脳筋ビルドなせいもあるとは思います。ちゃんと理解していかないと勝てないくらいのレベルに抑えたい。

終わりに

本記事の序文は敬愛する id:takeshinoda パイセン*3の記事を参考にさせていただきました。

また、こうした近況を書いて投稿する機会を作ってくれた、id:takkan_m さんもありがとうございました。といっても忘年会に参加したことあったかなかったかあやしいですが……。

*1:なお当然ながら私が怠惰でないことを示すわけでもありません

*2:それを突き詰めると工業用アルコール飲んでろ、になるのもわかっています

*3:このフレーズも https://twitter.com/highwide/status/1470691534681313281 を参考にさせていただいています