日記 2024-01
公開
2024-01-23 06:04

14日ぐらいから、アイディアのPoC(Proof of Concept)がしたくて4つのプログラミング言語処理系をCで書いてたんだけど、どれもこれも複雑さが気に食わなくなって、Cが一番頭使わなくて楽なんじゃ……となってしまう。まあずっとC書いてるからなあ


PicoLispの異常な速さ何やねん……といろいろ分析している。
といってもS式の独自アセンブリという読める可能性0.00001%のコードで書かれてるからマニュアルから推測するしかない。
whileでcontinue/breakする手段が無いらしい、かつcatchとthrowはあるので、おそらくステートを返したりする構造ではない。
多分setjmp/longjmpかsignalでエラー処理をしていると思われる。


ConservativeGCのLispと並行して書いてるシンボル化したTcl処理系
なかなか(やや無駄に)気合入れて整理している。
あんまり最初にこだわりすぎるのは良くない……とはわかってるけど、まあ構造が整理されてると後で自分が楽になるし。


午前中はなんとなくConservativeGCを書いてた。
GC部分はできて、Lispのシンボルパース周りでちょっとダルくなって中断。

その後、Tclのコマンドなどをシンボル化したら速くならんかな?と思ったので夜から実装。
Tclの文字はありとあらゆる場所がSubstされる……と言っても、結局procの本体などは変わらないし、同じキーワードは何度も出てくる。
文字列や配列が変更されることは無い言語仕様なので、一度読み取ってしまえば大抵のものはシンボル(ハッシュテーブルに登録された不変のデータ)として扱えるんじゃないか、という発想。
特にコマンド実行などがポインタ比較のハッシュテーブルで済むのは速いはず。
ネームスペースなど無視すればシンボルにそのまま登録してしまってもいいかな。
Tcl8ぐらいからバイトコードらしいので、まあ多分同じことはしてるんでしょう。

で、ひたすらC打ってたらこんな時間。
久々にメモリ関連でバグ踏みまくってセグフォ出まくってValgrind先生のお世話になりまくりまくった。
Valgrind無かったらどうなってたか想像つかねェ


Arkam
昔のプロジェクト整理してちょっとずつアップしていってる。
GitHub使いたくないからFossilで。


限定継続で例外機構作れるか試してみてたんだけど、try/catchの中にもう1段resetを入れてしまうと、throwのshiftがそれに引っかかって出てこれない。
dynamic-windはそのおかげで作れるんだけど、coroutineなど別のresetを挟むとよくわからない挙動になってしまう。
これは継続を呼んだ場所に戻ってくるからで、呼ぶとある範囲を完全に脱出できる手続き、call/ecが必要になる。
するとcall/ecを防ぐdynamic-windはcall/dcでは作れなくて……うーん……?



olegの限定継続でdynamic-unwind実装であれっ?となった動作確認した。
dynamic-unwindの外側をreset/shiftで囲み(k1)とする、dynamic-unwindの中でk1を呼び出したら意味なくない?と思ったけど、そんなことは無かった。理解不足だった。

k1を呼び出したら、外側のresetで継続が切られたあとk1の呼び出し元に戻る。
つまり、dynamic-unwindの中に戻るので、その後のafterなどの処理はちゃんと行われる。
無限のcall/ccだとどこまで飛ぶのかよくわからなくて、継続を呼び出した元に戻るという動作を忘れがちだった。

これが限定継続がcall/ccより推奨される理由かな。
例えばシングルスレッド非同期サーバーを作る場合は、ハンドラをdynamic-windでくるんでソケットを閉じてやればいいのか。
まあ外側から持ち込まれた限定継続で一瞬外に出ちゃうかもだけど、最終的に中に戻る。



スティーヴン・キングの「書くことについて」
好き過ぎるから、逆に影響を受け過ぎないように読み返す間隔を空けてるんだけど、新年だし読み返し始めた。
こんな記述あったっけ!?となったところを引用。


(息子がサックスをねだり習い始めたが、7ヶ月経ってそろそろ潮時だとやめさせてあげた、本人もほっとしたという話)

私にはわかっていた。オーウェン(息子)が練習をしなかったからではない。
ミスター・ボウイ(教師)に言われただけの練習しかしなかったからだ。
(中略)新境地が開けることも、忘我の状態にひたれることもない。
練習が終わり、ケースにしまいこまれた楽器は、次の練習時間までそのままになっている。
これでは成果は出ない。練習がいつまでも続くだけだ。
それはよくない。楽しくなければ、やらないほうがいい。
もっと才能があり、もっと楽しめる道に進んだほうがいい。

才能は練習の概念を変える。
どんなことでも、自分に才能があるとわかると、ひとは指から血が出たり、目が飛び出しそうになるまで、それに没頭する。
聞いている者や、読んでいる者や、見ている者がいなくても、それは素晴らしいパフォーマンスになる。
ひとはクリエーターとして幸せになる。エクスタシーを感じさえするかもしれない。


まあ明らかに没頭できるものとできないものあるよね。
才能というとちょっと語弊がある気がする。他人より優れてることだと受け取る人が多そう。
没頭できることそのものを才能だと言い換えてもいい。

おれはまあ皆さん知っての通り、興味出たものにポンポン手を出すんだけど、やっぱり「没頭できるもの」と「我慢が必要なもの」に分かれる。
3年ほど手を動かしたけど、絵は結局我慢しないといけない。我慢して取り組み続けて、我慢して練習しないと進まない。
プログラミングや楽器は没頭できる分野もあれば、どれだけ我慢して続けても好きになれない分野もある。

一切我慢せず努力に没頭できることを生業にできた人達がいる。
大谷翔平とか藤井聡太とかは多分そうなんでしょう。
一方で、没頭できない努力はしないと割り切ったことで成功した人達もいる。
多分ちいかわのナガノ先生はその最たる例。「苦手で描けない部分は諦めて、自分が描けるものを描くようにした」という言葉に感銘を受けたらしい。

憧れの分野で成功するために、意思の力で必死になって進むのもまあ良い人生かもしれない。
しかし、自分が向かい風の中摩耗しながらじりじり進むのに対し、同じ方向なのに追い風でスイスイ進む人は必ずいる。
風向きが変わって、努力や作業が好きでたまらなくならない限り、ずっと苦しい戦いになる。
他人はどうでもいいとしても、人生の最期にひたすら我慢して良かったー!とはならない。おれは。

そんな苦しい生き方を選ぶよりは、いろんな事に手を出して追い風が吹いている道を探し出し、そこでスイスイやってもいいのでは無いでしょうか。
それが犯罪や人に迷惑をかける行為でなければ、例えばSlay the SpireでA20Hにひたすら挑み続けることでもいいと思う。それで稼ぐ道が見つかれば最高だし、そうじゃなくてもまあ人生の最期で楽しかったー!となると思う。

まあ他人から全然評価されない道かもしれないけど、それはもう諦めるしかない。運と世間が悪い。
自分が成功したかった分野で、あんまり気に食わないやつがスイスイやってるかもしれないけど、それも仕方ない。運とそいつが悪い。

ということをここ2年ぐらいずっと感じていたのでスッポリ来る文章が入ってきて興奮してしまった。
割となんでも我慢して続けたら少しは上手くなる、という成功体験を下手に積んだせいで、没頭できることに背を向け続けてしまった。
もう既に無限に没頭できる分野は見つけているので、これからは新しいことが気になっても、ちょっと手を出して向いてなかったらすぐ諦めていきたい。


なんとなく気になってStackful Coroutineの実装を探っていた。
バイトコードにコンパイルするVM系なら、大抵コールフレームは簡単に操作できるので特に難しいことは無さそう。
SECDでcall/ccやdelimited continuation作ったことあるけど、そんな雰囲気でしょう。

Cやアセンブリなどならコールスタックとレジスタをいじるしかない。気合。

変わり種で言えばForthで、VMとも言えるし機械語とも言える。
これはやったことあるけど簡単だった。
大抵はスタックポインタ2種とインストラクションポインタぐらいを保存すればいいだけ。

で、問題はAST系のインタプリタ。
自前でコールフレーム用意してステップできるようにコード変換すると、それはもうVMの出来損ないなんよ、になる。
末尾呼び出しのみでループを表現するSchemeみたいな処理系なら、CPS変換からのcall/ccでどうにかなるかも。ただ例外周りはあんまり考えたくない。
あとはCやアセンブリみたいにホストのコールスタックをいじるか。

というわけで、できるだけ楽に実装するならSECDがさいつよになってしまうわけですね。
ForthはCoroutine系の実装は楽だけどそれ以外が 悪 夢 なので……


wappを完全なstatic binaryにできないかアレコレ試していたけど、tcllib周りが面倒すぎて諦めた。
Tclの思想的には徹底的に相対パスかファイル埋め込みさせて欲しいわね……
ただdockerとalpineでクリーンな環境作って実験するコツは掴めた。



wappを全体的にcoroutineで書き換えた。
特にヘッダとCONTENTのパース関連が劇的にシンプルになった。

What Color is Your Functionで言われてるように、全てをasyncで包んだり、関数の色を意識する必要がないcoroutineはかなり楽。
しかし一方でイベントのタイミングを把握するのが少し面倒。
goroutineみたいな解決法が一番わかりやすい。
幸いやってる人はいるので真似してみよう。


TclでImagemagickのconvertなど、時間がかかる処理をパイプのみで行い、さらにcoroutineで非同期処理したいとき
- fileeventのreadableにinfo coroutineを登録
- chan close channel-id writeで書き込みを閉じる
- その直後にyield
- その後パイプからread、close
という手順になる。chan close ...がわからずハマった。


相変わらずTclと格闘中。coroutineの動作だいたい掴んだからとりあえずexecだけはlistenループの外に追い出したい。


正確な内容は忘れてしまったんだけど、どこかの軍では2つ以上の欠勤理由を許さないらしい。
例えば、「親戚が無くなって、しかも風邪です」というのは嘘だと判断する。
どちらか1つなら本当なんだろうけど、たしかに2つ以上あれば嘘っぽく感じる。
何にでも当てはまるわけじゃない事はわかっているけど、この話を読んでから「本当の、ただ1つの理由は何だろう」と考える事が多い。

TwitterとPixivのアカウントを消した。
多くの人の目があったり、大量の情報に晒される場から、しばらく距離を取ることにした。

上に書いたように、本当の1つの理由は何だろうと考えると、自分が一番好きなことに時間を注ぎたくなったから。
1/1の地震でデマや金稼ぎが酷いことになったり、いろいろSNSから離れたい理由もあるんだけど、本当の理由ではない気がする。

30歳になったのを切っ掛けに絵を描き始めた。
まあ最初は上手く行かなかったけど、運良く仲間が出来て結構続けられた。
その後、仕事で1年や半年の中断もあったけど、最終的には二次創作のおかげで身に余るほどの評価をもらえるようになった。

それなりに取り組み自体も楽しかったんだけど、ただやっぱり違う気がする。
子供の頃から絵を描いてたりなど、創作系の趣味として絵が何よりも大事な人と違って、自分は音楽とプログラミングにかなりの時間を捧げてきたし、心から好きだ。
しかも悪いことに、多くの人にウケるとは思えない音楽ジャンルや、ごく僅かな人しか興味を示さない技術ばかり好きになってしまう。

他人の目を気にせず自分の好きな物だけを追い求める、孤高な芸術家みたいな人間であれば良かったんだけど、残念ながら全くそうじゃないし、そうなれなかった。
音楽やプログラミングと比べて他人に評価されやすい絵に、どうしても時間を注いでしまう。
誰かに喜んでもらえることが特に快感で、絵の依頼のために技術を維持向上しようと時間を作り、一方で楽器を弾く時間が削られることにかなり苛立ちを覚えていた。
(それでも依頼のために描くこと、喜んでもらえることはかなり楽しかったし嬉しかった。ありがとう!)

どんどんその事を思い詰めるようになり、嫁にも心配されるようになったので、新年を機に一旦区切りをつけることにした。
WebサイトのギャラリーのコードをTclというマイナーな言語で書いてみたりして、ああやっぱり自分は妙な技術で四苦八苦するのが好きだな、と再確認したのも大きい。

絵に限らず、音楽やプログラミングでもやっぱりウケてる人を羨んで真似しようとしたり、万が一評価されたら意識してしまいそうなので、アカウントの作り直しなどもしないことにした。

というわけで、自分が心から好きな、妙な活動に精を出していきたいと思う。
それがまた絵だったりするかもしれないけど、少なくとも自分の衝動だけに従いたい。
ああこいつはまたよくわからんことをやっとるな、と温かい目で見守ってくれたら嬉しい。



スレッド対応しようと四苦八苦していたけど、DB関連のコードが致命的に使いづらくなるのでやめた。
node.jsみたいにSingle threadでNon-blocking I/Oでやったほうが良さそう。
相当がんばらないとだけど……
ただasyncに必要なパーツは揃ってるっぽい


Wappでマルチスレッド対応しようとしたけど、Tclのスレッドはそれぞれ独立したインタープリタが起動して、親で定義された手続きは共有されないらしい。
ギェーめんどくさ!!
namespaceでどうにかできるかもしれないので気合でがんばってみる。


Tclを使うようになった影響の一人、Redisの作者がかなり昔からNN触ってたらしくて一人で盛り上がっている。
(ちなみにもう一人はSQLiteの作者)

- gguf-tools
- simple-language-model
- neural-redis
- nn-2003

最後のやつは2003年頃(20年前!)の実験らしいんだけど、C+Tclのお手本にもなっている。



Tcl+SQLiteの楽さのおかげで絵置き場かなりガンガン書けて楽しい。
しかしこれDBファイル2つ用意して、1つをデータ、もう1つをコードとして、ブラウザでコード書けるようにしたらどうかな、楽しいかな。
バージョン管理とsync内蔵して……みたいな妄想が捗る。
Smalltalkのコードブラウザみたいに、実行環境内でコードを書きたい欲求が強い。
まあaceエディタみたいなやつ入れないとキツいから、シングルファイルは無理があるか。


ImageMagick脆弱性に不安あるし、変換でサーバーに負担かけたくないからブラウザで画像リサイズしよっかな!
と思ってアレコレ試したけど、とてもじゃないけど使えないクオリティだった。
一応リサイズがんばるライブラリあるっぽいけど、クライアントサイドの依存を極力減らす方向なので終。
いや〜ImageMagickってすごいんだね。



自サイトのギャラリーをTcl+Wappで作った。
画像ファイルは全てsqliteに放り込んでいる。

で、一覧ページの生成が異常に重たかった。
最初はimage_entriesが各画像のセットで、それぞれに複数のimagesを持つという構成。
imagesに直接blobでサムネイルと画像を入れてたんだけど、これが原因だった。
詳細はわからないけど、おそらくクエリ時にキャッシュに乗らない・巨大なBLOBがあるとメモリに全てをロードはしない、あたりだと思う。
生成速度は一覧ページが0.100秒から0.003秒に、画像ページが0.050秒から0.001秒になった。劇的な変化。



ァケてる
言及