GitHubスポンサーの収入がありそうな気がしたので(注文時点では未取得)、デスクトップPCを購入することにした。
意外かもしれないが、初デスクトップ。今回はサイコムというところで注文した。自作という選択肢もあったが、自分の不器用さをよく承知しているので(最近では液漏れで止まった時計を修理しようと分解して、再起不能にした)、BTOに。スペックは、
で、先週届いて、早速Linux (Mint XFCE)をインストールしたのだが、しばらく使っていると突然落ちる。どうもグラフィック関係らしく、sshでログインして使っていると落ちない。GPUのベンチマークプログラムglmark2を実行すると即死。
で、購入先に相談したら、グラフィックボードの不良の可能性がありますね、と交換品を送ってくれた。慣れないなりにケースを開けて交換したら(思ったよりもずっと簡単だった。はまりそう)、問題は改善しなかった。ナンデ?
どうもハード不良ではなく、ドライバーに問題があるらしい。AMDからダウンロードしたドライバーも使ってみたのになあ。で、あきらめて近所のパソコン工房で購入した RX570 に交換。こっちはちゃんと動いた。私としては(若干損した気はするけど)ちゃんと動作するPCが入手できたので満足。
手元にはおそらく不良品ではない RX560 が2枚。これを購入先に送り返せばいいのかなあ。問い合わせ中。
サイコムから返事来た。送料着払いでボード2枚を返却したら、ボードぶんの代金を返金するとのこと。やさしい。
tDiary 5.1.0にアップデートした(前田修吾さんが)。あまり久しぶりなので、設定変更と確認しようとしたら、なにもかも忘れていて情けない。
Herokuに入社してもうすぐ8年になるのだが、ようやくアカウントを取得した。各方面で呆れられていた状態が解消した。
しかし、まだ使い方がよくわからないのであった。
いやあ、Ruby開発者の日記だというのにまだRuby 1.8で動作していて、恥ずかしい状態だったこの「Matzにっき」だが、前田修吾さんのご尽力で Heroku に移行しました。バージョンも 2.6.2 へ。
これで恥ずかしくない。 最近は、主に Twitter か、Quora で書いてたけど、そろそろここも更新を再開するべきか。
久しぶりの更新
GitHubの <URL:http://github.com/matz/streem> を公開したら驚くべき反響の大きさなので、本人もびっくりしている。
ので、ここでちょっとまとめておく。
自分でもなにを言っているのかと思うけど、雑誌という出版物と比較して、超スピードとかでは説明のつかないネットとOSSコミュニティのバズり方を見た思いがする。
一方、雑誌連載のサンプルであるStreemだが、言語仕様についてはパッとした思いつき、 というわけでもなくて、数カ月間、このような言語はあまりないし、実用的価値もありそうと思って作っている。
特徴
雑誌の連載のネタだとはいえ、今後も開発をやめるつもりもないので、ほそぼそと進歩し続ける予定。
っていうか、現在 C でタスクスケジューリングを実装することの困難さに絶望しつつあるので、 マジで streeem を参考に Go で実装するかも。
Twitterで質問を受けたので、 mrubyのmrb_gc_arena_save()/mrb_gc_arena_restore()の使い方 という解説を行った。が、1つ140文字のTwitterでの解説にはどうしても無理があるので、 こっちでまとめることにする。
まずは、Twitterの発言*1はこんな感じ。
arenaの目的。利用中のオブジェクトはGCに回収されないよう保護する必要がありますが、Cのスタックはポータブルに参照できません。そこでC関数実行中に生成したオブジェクトは全部「生きている」とみなす事で保守的に保護します。 yukihiro_matz 2013-07-31 08:16:45
arenaの目的(2)。この保護のためにオブジェクトを記録しておく領域がarenaです。mrubyではデフォルトで100個のオブジェクトを登録できます。 yukihiro_matz 2013-07-31 08:19:02
save/restoreの仕事。現状arenaのサイズは固定なのでC関数実行中にあまり沢山オブジェクトを生成するとarenaが溢れます。そこで沢山オブジェクトを生成する前後にsave/restoreを置くことでarenaのサイズを復元し、溢れを回避します yukihiro_matz 2013-07-31 08:27:02
save/restoreの使い方。オブジェクトを生成する領域の前後をsave/restoreで囲みます。ただし、囲まれた範囲内のオブジェクトが保護されなくなりますから、どうしても必要なものはrestore後mrb_gc_protect()で改めて保護してください。 yukihiro_matz 2013-07-31 08:33:61
これを再度まとめてみよう。
mrubyをCで拡張していると「arena overflow error」という「謎のエラー」に悩まされることがある。 これはmrubyで「保守的GC」を実現している「GC arena」という領域があふれたというエラーだ。
GC(ガーベージコレクター)は、オブジェクトがまだ「生きている」、 つまり、プログラムのどこかから参照されているかどうかを判定する必要がある。 これはルートと呼ばれる参照から直接・間接に参照可能かどうかで判別する。 ルートには、ローカル変数・グローバル変数・定数などが含まれる。
プログラムの実行がmruby VMの中でおさまっている時にはこれは問題ない。 VMの持つルートはすべてGCからアクセス可能だからだ。
問題はCで記述された関数を実行中の時。 Cの変数から参照されたオブジェクトも「生きている」わけだが、 mrubyのGCはCの変数の内容を感知できないので、 C変数からしか参照されていないオブジェクトは死んでいると誤解してしまう。
まだ生きているオブジェクトを回収してしまうのは、GCとしてもっともやってはいけないバグだ。
CRubyでは、Cのスタック領域を無理やりスキャンすることで、 Cの変数をルートとしてチェックしている。 もちろん、Cのスタックを単なるメモリ領域としてアクセスするわけだから、 それが整数を意味する値なのか、ポインタ値を意味する値なのか判別することはできない。 しかし、まあ、そこは「ポインタのように見える値は安全側に倒してポインタだと思って処理する」という 方針で処理している。この「安全側に倒す」というポリシーのことを「保守的」と呼ぶ。
され、このようなCRubyの「保守的GC」にはいくつか問題がある。
その最大のものは「移植性のある方法でスタック領域にアクセスする方法がない」ということだ。 つまり、移植性の高さを実現しようとするmrubyのような処理系では使えないってこと。
そこで、mrubyは別の方法で「保守的GC」を実現した。
問題なのは、C関数実行中に生成されたオブジェクトで、 Rubyの世界から参照されてないオブジェクトのうち、 C変数からは参照されているのでまだゴミ扱いしてはいけないものが存在する、 ということだ。
既に述べたようにCRubyは、Cスタックをスキャンしてゴミのように見えるがゴミでないものを保護している。
しかし、その方法が使えないmrubyは、より保守的なポリシーを採用した。 つまり、C関数実行中に生成されたオブジェクトは、極端に安全側に倒して、いっそ全部生きているとみなせば、 少なくともゴミでないものを回収してしまう問題は回避できるんじゃないかと。
この結果、本当はゴミであるものを回収できないので、若干効率が下がることになるが、 移植性が高いまま、保守的なGCを実現できることになる。 CRubyで時々発生する「最適化で参照が削除されてゴミでないのにGCされた」問題とも無縁になる。
このポリシーで、「C関数実行中に生成されたオブジェクト」を登録しておくテーブルが 「GC arena」である。arenaはスタック状になっていて、 C関数の実行が終わるとその間に登録されたオブジェクトはポップされる。
原則としてはこれだけで、普通の場合は、これでめでたしめでたしなのだが、 GC arenaの存在は別の問題を引き起こすことがある。 これが前述した「arena overflow error」だ。
メモリが少ない環境でも動作することを考慮したmrubyはarenaのサイズを固定長にしており、 しかも、そのサイズはデフォルトで100とかなり小さめに設定されている。
実は当初はサイズ1000とかちょっと大きめにしていたのだが、 このテーブルサイズが厳しい環境があったのと、 後述するようなテクニックを使い、適切にarenaを管理すれば100でも普通に動くので、 現状は100にしている。
その結果、C関数の実行中に数多くのオブジェクトを生成すると、 arenaがあふれることになる。
その対策に用いるのが、表題のmrb_gc_arena_save()とmrb_gc_arena_restore()というふたつの関数である。
int mrb_gc_arena_save(mrb)はGC arenaの現在のスタック位置を返し、 void mrb_gc_arena_restore(mrb, idx)はarenaのスタック位置を保存された位置に戻す。
int arena_idx = mrb_gc_arena_save(mrb); ...なんかオブジェクトを作る処理... mrb_gc_arena_restore(mrb, arena_idx);
というような使い方をする。
もともとのC関数の実行は、このようにsave/restoreに囲まれているのだが、 一時的にオブジェクトを作り、その後は不要になる領域を 明示的にsave/restoreを囲むことにより、arena overflowを避けるわけだ。
とはいうものの、具体例を見ないとわからないケースもあるだろう。 ここでは、Array#inspectのソースを見てみよう。
static mrb_value inspect_ary(mrb_state *mrb, mrb_value ary, mrb_value list) { mrb_int i; mrb_value s, arystr; char head[] = { '[' }; char sep[] = { ',', ' ' }; char tail[] = { ']' }; /* check recursive */ for(i=0; i<RARRAY_LEN(list); i++) { if (mrb_obj_equal(mrb, ary, RARRAY_PTR(list)[i])) { return mrb_str_new(mrb, "[...]", 5); } } mrb_ary_push(mrb, list, ary); arystr = mrb_str_buf_new(mrb, 64); mrb_str_buf_cat(mrb, arystr, head, sizeof(head)); for(i=0; i<RARRAY_LEN(ary); i++) { int ai = mrb_gc_arena_save(mrb); if (i > 0) { mrb_str_buf_cat(mrb, arystr, sep, sizeof(sep)); } if (mrb_array_p(RARRAY_PTR(ary)[i])) { s = inspect_ary(mrb, RARRAY_PTR(ary)[i], list); } else { s = mrb_inspect(mrb, RARRAY_PTR(ary)[i]); } mrb_str_buf_cat(mrb, arystr, RSTRING_PTR(s), RSTRING_LEN(s)); mrb_gc_arena_restore(mrb, ai); } mrb_str_buf_cat(mrb, arystr, tail, sizeof(tail)); mrb_ary_pop(mrb, list); return arystr; }
実際のコードをそのまま引用してきたので、若干複雑になっているが、 Array#inspectの処理の本質は、 配列の各要素をinspectメソッドを用いて文字列化した上で、 それらを結合して配列全体のinspect表現を作ることにある。
全体のinspect表現の文字列を作ってしまえば、 処理途中に生成した各要素の文字列はもはや不要になる。 ということは、GC arenaにこれらのオブジェクトを登録しておく必要もない、ということだ。
そこで、ary_inspect()関数では、
という手順により、arena領域の消費を抑えている。
ここで注意すべき点は、最終結果となる配列inspect表現となる文字列は、 mrb_gc_arena_save()の呼び出しよりも前に生成していることである。 こうしないと、必要なオブジェクトがGCで回収されてしまうことになる。
処理のパターンとしては、さまざまな一時オブジェクトを生成した上で、 そのうちの一部だけを参照し続けるというケースも考えられる。 このようなケースでは、ary_inspect()のように既存のオブジェクトに結合するような手は 使えないので、mrb_gc_arena_restore()の呼び出しの後に mrb_gc_protect(mrb, obj)を呼び出して、そのオブジェクトをarenaに再登録する必要がある。 ただし、mrb_gc_protect()は注意して用いないと、これ自身がarena overflow errorの原因になることがあるので 注意するように。
などということを、Twitterだけで説明するのは現実的じゃないよなあ。
あ、そうそう。
このmrubyのAPIには、改善が必要だなと思ってる点があって、 その最大のものは、トップレベルでmrb_funcall()を呼ぶと GC arenaに戻り値が登録されるのでそのうちarena overflow errorになることだ。
戻り値を使わないmrb_funcall()のようなものを用意すればいいんだと思うんだけど。 いい名前が思いつかないんだよなあ。
*1 tDiaryにTwitterの発言を引用するプラグインが欲しいなあ
先日、Webronza というところに寄稿したのだが、有料登録しないと後半が読めなくなっていた。で、交渉して公開許可を頂いたので、ここで全文掲載。
「ちょっと待った!小中学校でのプログラミング教育」
現代社会はもはやコンピュータがなければ成り立ちません。そして、コンピュータは誰かが作ってソフトウェアがなければ、まったく役に立ちません。コンピュータは自発的に仕事をしてくれないどころか、誰か人間がソフトウェアという形でどのように仕事をすれば良いのか教えてやらなければ、なんの働きもできないのです。コンピュータが社会に役に立っているのは、ソフトウェアがあるからです。
どんなに賢いように感じられるコンピュータでも、自らソフトウェアを開発することはできません。コンピュータは単純な計算をものすごく速く行うことができますし、それを積み重ねることで人間を越える能力を備えていますが、その一方で、なにか新しいことを創りだすなどの創造的な活動は苦手です。はっきりいうとまったくダメだと言ってもいいでしょう。当面の間は人間がソフトウェアを作って、コンピュータに仕事を教えてやるしかないのです。
社会におけるコンピュータの重要性は明らかで、そのコンピュータがソフトウェアがなければ役に立たず、そのソフトウェアは人間にしか作れないとなれば、ソフトウェアを開発する人間こそが真に重要だということになります。しかし、現状、誰でもがソフトウェアを開発することができない以上、ソフトウェアを開発する人、プログラマを養成することは急務です。とは言うものの、どのような教育を行えば、優秀なプログラマを育成できるのかについて誰でもが合意できる方法はまだ見つかっていないようです。
最近、注目されているのが若い人たち、特に小学生や中学生へのプログラミング教育です。確かに優秀なプログラマは若い頃からその才能を発揮している人が多いようです。また、プログラミング能力はあまり年齢に関係ないようで、天才と呼ばれる小学生プログラマがいると思えば、70歳をはるかに超えて現役で活躍する方もいらっしゃいます。
そこで、若いプログラマを育てるために、小学校や中学校での情報処理の教育やプログラミング教育に力を入れようという動きもあるようです。しかし、自分自身のプログラマとしての経験から考えると、これにはなかなか困難がつきまとうように思えます。第一の課題は「誰が教えるか」という点です。学校をプログラミング教育の現場にするためには、当然のことながらプログラミングを教える教師が必要です。しかし、現在の小学校・中学校の教員でプログラミングの能力を持つ人はごく少数でしょう。もちろん、教科書通りに教えることができる人は短期間で用意できるかもしれませんが、それでは子供たちにプログラミングに前向きな気持ちを伝えることは困難でしょう。中学生時代にプログラミングをはじめた私自身も含めて、若い頃からプログラミングに「はまった」人たちは、結局、コンピュータを使いこなすのが楽しいからこの道に進んだようなもので、教科書に書いてあるから、あるいは学校の授業だからという理由でプログラミングをはじめた人など見たことがありません。プログラミングを教えるというのであれば、少なくとも教える人はプログラミングの楽しさを自覚している人でなければ成果をあげられないと思いますし、そのような人をそれぞれの学校に配備するのは大変な困難ではないかと思います。
第二の課題は「どのように評価するか」ということです。学校の授業であるということは、なんらかの評価をする必要があるわけですが、これがまた困難です。まず、プログラミングの能力は、創造性をともなうという点である種芸術に似ています。そういう点では美術の授業に似ているのですが、問題はプログラミングの場合、人によっては非常に短期間で上達するため、小学生であってもプロを越える作品を完成させることがたびたびあることです。人によって出来栄えが10倍、100倍あるいは1000倍も違うようなものを学校の成績としてどのように評価したらよいのか途方にくれます。
第三の課題は「なにを教えるか」ですが、この点は最初のふたつの課題に比べればなきに等しいものです。
では、学校教育はプログラマ養成にふさわしくないのであれば、いったいどうすれば未来を担うプログラマを養成することができるでしょうか。
正しい答えは私自身も持っていないのですが、私の知っている優秀なプログラマのほとんどが自学自習でプログラミング能力を身につけている現実を考えると、あるいはプログラマとは、養成されるものではなく、発見されるものなのかもしれません。
ある調査によれば、大学におけるプログラミング教育の受講者の内6割程度はプログラミングの基本的な部分の理解に困難を感じ、それは他の学科の成績に必ずしも相関しなかったのだそうです。だとすると、世の中にはプログラミングに向いた性格の人がいて、成績その他では区別することができないことを意味します。であるならば、少しでもたくさんに人にプログラミングに触れる機会を与え、そしてそれに興味を持てた人、才能の片鱗を見せた人にはより豊かな機会を与えるようなプロジェクトを総合的に設計することで、未来のプログラマを「発掘」できるのかもしれません。そして、そのような才能あふれる人たちに適切な待遇を与えることが、このIT社会の競争力を強める最大の方策なのかもしれません。
一年ぶりの更新か。「年刊Matzにっき」だな。
今年もニューオーリンズで開催されたRubyConf。同じ都市で二度開催は初めて。
で、しょっぱなが私のキーノート。
まあ、あんまり語ることはないので。スライドを見てもらおうか。
こんな感じ。
角谷さんオススメのspeakerdeck.comも使ってみた。
で、最後に「Diversity(多様性)」である。
初日のDave Thomasのキーノートでも3つの重要なこととして、 Diversifyをあげていた。多様性は重要なのである。
とはいえ、多様性はいいことばかりでもない。 Rubyにおける多様性といえば、昨今数々登場している別実装である。
などなど、多くのRuby実装がある。これら以外にも「Rubyっぽい言語」まで含めると 本当にいくつあるのか見当もつかない。
昔はPythonの人たちに「Ruby(とPerl)は複雑すぎて、別実装は登場しそうにない。 Pythonを見てみろ、CPythonとJytonとIronPythonがある」などと言われたものだが、 今や別実装の数ではRubyの方がしのいでいる。Pythonの方もPyPyとか新しいものも登場してるが。
が、一方、このような多様性にはコストがかかる。 まあ、プラットフォームの違うJRubyはおいておくとしても、 CRubyとRubiniusとMacRubyで分散しているリソースを集約すれば、 もっと早く言語(実装)が進歩するような気もしないでもない。
しかし、オープンソースプロジェクトでは結局はそれぞれの参加者がやりたいように関わって モチベーションを維持することの方がはるかに重要だ。
多様性は善で、コストは必要経費であるというのが私の認識である。
さて、多様性は善であるので、さらにそれを豊かにするために 私自ら新たな処理系を送り込もうと思う。
それは RiteVM であり、組み込みなど小規模なデバイス向けをターゲットとした処理系である。
現在のRubyは元々Unixをベースにして開発されたものであり、 UnixやPOSIX APIを提供しないような小さなデバイスや、 アプリケーションへの組み込みなどはあまり重視されてこなかった。
しかし、一方、組み込み分野などではデバイス性能の向上で ソフトウェアの比重が高まりつつあり、 実行速度やリアルタイム性がそれほど要求されない分野で Rubyのような「高級」な言語を使いたいという要求はそれなりにあるようだ。
そこで、以下のような処理系を新規に開発する
このVM開発のプロジェクトコードネームは Rite と称することにする。 RiteVMの開発は、経済産業省 平成22年度「地域イノベーション創出研究開発事業」の一環として行われる。
これにより、たとえば
などにRuby(のサブセット)が使えるようにする。
もちろん、今すぐにそうなるとは思っていないが、 今から準備すれば数年後には現実にできると考えている(ビジネス上の成功は別だけど)。
と、ぶちあげてしまったので、もう後に引けなくなったな。