OSC2019東京・春での展示の予告

  • (by K, 2019.02.09)

(0)

  • ダウンロードと説明は、(4)から始まります。・・・だからめんどくさい前置きはすっ飛ばして、(4)に進むのがお勧めです。

(1) 厳密ではないけどわかりやすい概要

  • 現在予定通りプログラミング言語を作っています(開発ペースはいまいちですが)。今のところ名前はEssen2019-Aとなっています。1月に自分で決めた「機械語を使わなくてよくなる言語」をもちろん目指していますが、それは少し後回しにすることにして、今はもっと基盤になる部分から書いています。
  • Essenでは、(シリーズで一貫して)言語処理系内で疑似マルチタスクを実現することが理想とされています。これは プログラムA を実行中に、その変数の中身を別の プログラムB が覗き見してもよい、みたいなことをどうしてもやりたくて、そのために必要なことなのです。
  • 別のプログラムから参照するだけではなく、プログラム実行中にコマンドプロンプトから print prog1.i; とかやれば、実行中のprog1のグローバル変数のiが見えちゃうとか、そういうのを想定しています。これでプログラムの実行中に不信な挙動を示したとき、プログラムを止めずに変数の値を確認できるので、デバッグがはかどるのです(デバッガがなくても)。もちろん実行中のプログラムを一時停止したり、また再開したりもできるので、ちょっと怪しいからよく調べたいときは、一時停止にしてじっくり調べることもできます。
  • 一方、この考えを発展させて、プログラムを一時停止させて、それをファイルに保存し、後日ファイルからロードして再開することもできます。これもデバッグには超便利です。だって危ないところに差し掛かりそうになったら直前に保存して、そこから何度でも再開できるからです。でもこれを実現しようとすると、次回起動時に変数などのオブジェクトを同じアドレスに展開できる保証が無くなるので、生ポインタはそのままでは使えなくなってしまいます(まあ要するに内部で疑似セグメンテーションをやります)。
  • でも生ポインタが使えないとなると、今度は大半の処理で速度がダウンします。EssenはJITコンパイラで速度を上げるわけですが、でもこの制約のせいでC言語にボロ負けするようになると、この言語を使う気が失せてしまうかもしれません。・・・ということでプログラム内では数ミリ秒の間は生ポインタを自由に使えることにしました。プログラムは定期的に「ここで生ポインタを全部破棄して、再取得したほうがいいですか?」と言語処理系に問い合わせます。そのタイミングで処理系はプログラムを一時停止するかどうかを決めることにしたので、ほとんど性能低下することなく、私の望みどおりの動作ができるようになりました。
  • ただし、数ミリ秒ごとにこの問い合わせ処理を入れるのはそれなりに面倒ですし、ポインタ計算も(特に再取得が)いろいろめんどくさいです。ということで、それは言語側で隠ぺいして、「ソースコードは自然に書いているのに、でもこれらのメリットを享受できる」という、そんな言語を目指しています。・・・なんかもう微妙にOSっぽいです(笑)。
  • それで、これのデモと説明をOSCでやりたいと思っています。

(2) より正確な記述

  • Essen2019-Aにおけるアプリケーションは、関数の集合として記述します。関数にはそれぞれ番号がついています。関数番号は0から始まります。関数番号0がアプリの実行に際して、一番最初に呼ばれます。
  • 関数は必ずオブジェクトの生ポインタを受け取ります。このオブジェクトは、システムとやり取りするためのハンドルなどが入っています。そのほか、アプリが必要なデータもすべてここに格納します。だからここを書き換えながら実行が進むことになります。
  • この関数は数ミリ秒以内に終了してシステムにreturnしなければいけません。そしてreturn時に整数を1つ返すことになっており、それが次に呼んでほしい関数番号になります。・・・これを繰り返すことで、アプリの処理は進んでいきます。関数が-1を返したらアプリは終了です。
  • 関数は割込みなどによって処理が妨げられることはありません。つまり関数の実行中はすべてのリソースに対してロックをかけていると思っていいです。だからほとんど排他処理に気を付けることなく、疑似マルチスレッドプログラミングができます。関数の中では生ポインタも自由に使えますが、そのポインタをオブジェクト内の変数などを使って次の関数へそのまま渡すことはできません(原則としては)。疑似セグメンテーションを利用して抽象的な方法で記録しておき、次の関数内でまた生ポインタに戻して使うことになります。
  • なお関数は、任意の時点から今の時点まで、生ポインタを再計算しなければいけないようなイベントがあったかどうかを知ることはできます。これを使えば再計算を最小限にして高速化することができます。これのためであればオブジェクト内に生ポインタを保存しておくことは許されます。
  • このシステムにおいての肝は、関数が数ミリ秒以内に確実にreturnしてくれることです。ここが守られないと、システムは容易にハングアップしてしまいます。これを守らないアプリはEssen2019-Aにおいて「バグのあるアプリ」であり、直さなければいけないものです。システムは各関数の実行時間の最大値を常にモニターしており、長かったものをいつでも調べることができます。こうしてユーザはダメなアプリを早期に発見することができます。
  • プログラムをこのような関数の集合で記述するというスタイルは一般的なものとはいいがたく、だからこれを隠ぺいするために(もしくは支援するために)言語処理系があり、JITコンパイルによって実行するというわけです。
  • [Q] なぜ関数の番号なんですか?素直に次に実行する関数ポインタを返せばいいのに。
    • [A] 関数はアプリが終了する前に書き換えられるかもしれません(そういうことをEssen2019-Aは許すのです)。そしたら関数のアドレスは変わるかもしれません(JITコンパイルを部分的にやり直すので)。もしくはオブジェクトをセーブして後日再開して、だから関数のアドレスは変わっているかもしれません。そういうことがありうるので、関数のポインタはあてにならないのです。ということで関数番号を返します。

(3) アプリ移植の難易度

  • アプリ移植の困難さはもちろん自作言語によってカバーする予定ですが、とりあえずC言語でも書けるので、C言語だけでどこまでできるか試しにやってみました。
    win32版Essen2019-A版
    mandel45行89行
    cube回転290行331行
    invader189行251行
  • まあそれなりに手間はかかりました。どれも1時間程度の時間がかかりました。
  • でもEssen2019-A上で動くと感動のものなので、全然苦にはなりませんでした!
  • ということで、この程度の手間だったら、別に自作言語無しでも許容範囲かも、とちょっと思いました(笑)。

(4) ・・・で、何ができるのか?

  • (ここの情報は 内部バージョン 0.00n のものです。)

(4-1)

  • まずe19a000.exe (24.5KB)をWindows上で実行すると、白いコンソールが出てきます。
    Essen2019-A console
    
    >_
  • このEssen2019-Aはアプリケーションを7個内蔵しています(内蔵しているのは、アプリの形式を制定したりローダを書いたりしていたらOSCに間に合わないと思ったからです)。
  • その一覧をappコマンドで見てみます。
    >app
    #0 counter0  // 10億までの32bitカウンタ(画面表示なし)
    #1 counter1  // 1000億までの64bitカウンタ(画面表示なし)
    #2 mandel    // マンデルブロ集合画像の描画プログラム
    #3 cube      // 立方体をくるくる回すプログラム
    #4 counter2  // 1000億までの64bitカウンタ、途中経過を画面に出力
    #5 invader   // 簡単なインベーダゲーム
    #6 monitor   // 変数をモニタするプログラム
  • 画面表示がないのはつまらないように思うので、2番のmandelを実行してみようと思います。runコマンドを使います。
    >run 2
  • これで直ちに実行されて、かっこいい画像が数秒で描画されます(「秒数」はマシンパワーによります)。

http://k.osask.jp/files/e19a000n_fig1.png

  • ちなみにアプリは番号で書かなければいけないので、「run mandel」はできません。それは今はEssenを手抜きして作っているからです。ごめんなさい。

(4-2)

  • さてここまでで、appコマンドとrunコマンドを使ってみましたが、他にどんなコマンドがあるのでしょうか。helpコマンドで確認してみましょう。
    >help
    help     - put this msg.
    app      - apps list.
    ps       - process list.
    wrt      - worst response time.
    run #app - start app.
    st #app  - start app and stop.
    stop #ps - stop process.
    cont #ps - continue process.
    kill #ps - kill process.
    pri      - priority list.
    pri #ps #pri - set priority.
    mem #ps  - memory report.
    memall   - total memory report.
    cls      - clear screen.
    col #c #b - console color.
    set #ps var #val - test & debug.
    exit     - exit Essen2019-A.
  • おお、なんかいっぱいある!(自画自賛)
  • psコマンドがあるのなら、まずはそれをやってみましょうか。
    >ps
    #0 console           run:000001/000001 a
    #1 mandel            run:004167/004168 s
  • これの意味するところは、現在2つのプロセスが実行中で、consoleは1ミリ秒のCPU時間&実行時間、mandelは4.167秒のCPU時間と4.168秒の実行時間だということです。
  • 末尾のaは実行中、sはスリープ中を表しています。

(4-3)

  • ここで、mandelのウィンドウのXボタンを押してウィンドウを閉じると、
    > !!
    #1 process terminated. run=004167/004168
  • と表示されます。つまりアプリケーションが終了すると、コンソールで教えてくれるわけです。
  • これを便利だと思うか、うっとうしいと思うかは、まあ人それぞれかもしれません(苦笑)。川合は高速化が大好きで、いつも実行時間が気になるので、このレポートは助かります。

(4-4)

  • 数秒程度で実行が終わってしまうと他の機能の説明がやりにくいので、今度は3番のcubeを実行してみます。
    >run 3
  • こんな感じでくるくる回ります。

http://k.osask.jp/files/e19a000n_fig2.png

  • psコマンドを使えば負荷もわかります。
    >ps
    #0 console           run:000001/000259 a
    #1 cube              run:000557/000557 w
  • 余談ですがcubeの負荷が小さくて私は嬉しいです。wはタイマーをwaitしているという意味です。
  • さて、回っているキューブの変数の状態をリアルタイムで確認してみましょう。
    >run 6 1
  • これはmonitorプログラムに引数1を渡して起動したということです。この1はアプリ番号ではなくプロセス番号です。・・・すると別のウィンドウが出てきて、
    #1 cube
    theta_x = 3.560419
    theta_y = 5.340681
    theta_z = 0.837708
  • こんな感じの表示がでて、値が刻々と変わっていきます。
  • この3つの角度で、立方体の方向を決めているわけです。
  • プログラムではほかの変数ももちろん使っていますが、それらはテンポラリな変数なので、プロセスの変数として登録されておらず、だから出てきません。すみません。
  • stop 1 ってやれば一時停止できますし、 cont 1 すればまた回り始めます。
  • 当然ですが一時停止中はCPU時間も実行時間も全く増加しなくなります。
  • ここでこのcubeを終了させてみます。さっきと同じようにウィンドウのXボタンでも閉じられますが、コンソールで kill 1 しても終了できます。
  • モニターしているプロセスが終了してなくなってしまうと、モニターは対象プロセスをロストしたと表示して、それ以降表示は更新されなくなります。これは同じプロセス番号で同じアプリを起動しなおしても変わりません。それが別のものであると認識できているからです。

(4-5)

  • それでは次はインベーダゲームをやってみます。まずコンソール以外のすべてのプロセスをkillしてから、
    >run 5
  • これで直ちに実行されておなじみのゲームが始まります。もちろんキー入力できて普通に遊べます。

http://k.osask.jp/files/e19a000n_fig3.png

  • psコマンドで負荷を確認することもできます。stopもcontもできます。
  • さらにモニターアプリを使うことで、変数の中身を見ることができます(実は、モニターアプリの画面を小さくしてしまったので、いくつのかのプロセス変数は表示が省略されています)。
    #1 invader
    fx = 0018    ix = 0006    iy = 0002
    point = 0001 lx = 0000    ly = 0000
    mwait0 = 0020  score = 00000000
  • これも表示はリアルタイムに更新されていきます。ゲームとの対応が分かりやすく楽しいです。
  • うわー、ぼんやりしていたらインベーダが下のほうまで下りてきてしまいました!
  • ・・・でも大丈夫。それなら変数iyを1にしちゃえばいいのです。これでインベーダは最上段に戻ります。これをやりたいときは、コンソールで、
    >set 1 iy 1
  • と入力すればいいのです。プロセス1の変数iyを1にしてくれます。
  • え、インベーダが速くなってきてだんだんつらくなってきた? それなら変数mwait0を変更しちゃいましょう。
  • 推奨値は 20 で、それがゲーム開始直後の値ですが、これはつまりインベーダが1歩だけ歩く間に、人間側には20回のターンがあることを意味します。でもまあそれじゃあつまらないので、200にしてみましょう。
    >set 1 mwait0 200
  • うおー、これはすごい。まるで時間が止まったかのようにインベーダが超ゆっくりです。ボコボコにやっつけられます(笑)。
  • とまあゲームバランスは壊したい放題なのですが、そもそも単に点数が欲しいだけなら、変数scoreに1万でも10万でも好きな値を代入すれば済むことではあります(笑)。
    >set 1 score 999999

(4-6)

  • ここでいったん話をまとめます。
  • このようにEssenではアプリに対して変数を監視したり、値を勝手に上書きしたりがいつでも自由にできます。
  • そんなことができて何がうれしいんだと思うかもしれません。今回はもともとちゃんとしているゲームに対していたずらをしたので、この意義がよくわからないかもしれませんが、もしゲームを作成中で、mwait0の値がいくつくらいならゲームバランスが良くなるか調整中だとしてみましょう。Essen以外の言語の場合、ソースコードをいじってパラメータを変更し、再コンパイルして再実行してみて、「うーんもうちょっと増やしたほうがいいかな」みたいなことを繰り返すのが普通でしょう。でもEssenならそんなことしなくても、つまりソースを全くいじらなくても、そして再コンパイルしなくても、いやそれどころかアプリを再実行しなくても、変数は好きなだけ変更して試せるのです。
  • 変数の監視も役に立ちます。気軽に確認できるので、おかしな挙動の前兆を早く見つけられるでしょう。デバッグがはかどります。「xが奇数の時にyがちゃんと期待通りの値になっているだろうか?」みたいなことを調べたければ、stopやcontでアプリを一時停止させてじっくり見ることだってできるのです。デバッガのようなブレークポイントは仕込めませんが、プログラムに直接stop命令を書けば、人間がコンソールでcontするまで止まらせることができます。プログラムに書く場合は、stopの前にif文を置くこともできるので、特定の条件の時のみ止まるようにもできるはずです。・・・そして一度止まってしまえば、変数の値をじっくり観察するなんてことは、わけもないことです。
  • 開発中のプログラムに対して、なんでもいちいちきちんと確認することはとても重要です。たいていはなんとなくうまく動いていれば、「まあたぶんこれでいいのだろう」ということにして先に進んでしまいます。しかしこれでバグを見逃して、後になってプログラムが大きくなってからだと、バグを見つけるのは本当に大変になります。
  • じゃあどうすれば確認するでしょうか。それは確認するためのコストを減らすことです。テストコードを書かずとも気軽に確認できればいいんです。それで特に違和感なく何事もなければそれでよし、ちょっとでも引っかかることがあればテストコードを書いてでも解明すればよし、そういうことなのです。きっかけが大事なのです。・・・そういうデバッグ哲学に基づいてEssenは作られています。
  • Essenにはmemコマンドがあり、いつでもメモリ消費状況を確認できます。気が向いたらちょろっとmemコマンドを実行してみて、期待通りかどうか確かめてみればいいでしょう。これでメモリリークに早く気付けるようになるはずです。またmemコマンドはユーザが明示的に確保したメモリしか表示されないので、システムが管理するメモリも含めて確認したいときは、memallコマンドも使います。アプリを実行して終了した後で、メモリが元通りになっているかどうかはmemallで簡単に確認できるのです。

(4-7)

  • Essenにはpriコマンドもあります。
  • これは例えばcount2アプリを同時に2つ起動してみると分かりやすいです。
  • 同時に起動するには run 4 4 ってやるといいです。6番のモニターアプリ以外は、原則としてアプリは引数を取らず、そのために run で複数の数字を書くと、同時に起動してくれるのです。
  • ちなみに stop や cont も同時に複数のプロセス番号を列挙することができて、その場合は一斉に止めたり一斉に再開したりできます。速度競争などをやりたい場合は、なかなか便利な仕様です。
  • さて本題に戻って pri コマンドですが、プロセスがいくつか起動しているときに引数なしの pri コマンドを実行すると、それぞれのプロセスの優先度が表示されます。デフォルトは4です。これよりも小さくすることも一応できるのですが、システム的には3以下を設定することはあまり想定してないので、基本的には最小値は4だと思っておくほうがいいでしょう。
    >run 4 4
    
    >ps
    #0 console          run:000001/001853 a
    #1 counter2         run:002266/004520 a
    #2 counter2         run:002254/004520 a
    
    >pri
    #0 console           pri:04    a
    #1 counter2          pri:04    a
    #2 counter2          pri:04    a
  • priコマンドに引数をつけると、指定したプロセスの優先度を動的に変更することができます。count2で、4と40とで競争させれば、だいたい10倍くらいの実行速度比になります。
    >pri 2 40
    
    >pri
    #0 console           pri:04    a
    #1 counter2          pri:04    a
    #2 counter2          pri:40    a
  • つまり負荷100%の適当なプログラムと、priコマンドがあれば、デバッグ中のプログラムを減速させて観察することができるようになります。速すぎてよくわからないなんてことはよくあることです。それはぜいたくな悩みですが、でもわからないものはわからないので、単純に減速させればいいんです。
  • 本当はダミーのプログラムなんか使わないで、CPUのクロックを下げられればいいのですが(そのほうが間違いなく低消費電力だろうし)、そんなに気軽には狙った速さに減速できないので、やむなくこうするわけです。
  • psコマンドでも、実時間は同じ1分なのに、CPU時間は6秒くらいしかなくて、ああ、1/10に減速できているなーと簡単に確認できるようになっています。
    >st 4 4      // 起動はするけどただちにstopするコマンド
    
    >pri 2 40
    
    >cont 1 2
    
    >ps
    #0 console          run:000095/003975 a
    #1 counter2         run:001152/012166 a
    #2 counter2         run:010994/012166 a

(4-8)

  • ここまででEssenがデバッグのためにどれだけ多くの配慮をしているかを紹介してきました。
  • でもこれらは少し危ない要素もあります。たとえばモニターアプリはほかのアプリの変数を自由に読んでいるわけです。コンソールアプリもほかのアプリの変数の値を強制的に書き換えているのです(しかも実行中に)。アプリ同士の独立性というか、不干渉・不可侵の原則はどうなってしまったのでしょうか。
  • ええ、そんなものはこの際どうでもよいというのが川合の見解です。その原則を守っていたら、不自由なデバッグはなかなか解決しないのです。不干渉にしたいのなら、そういう機能を「言語が」持てばいいのです。それはポインタにconst属性をつければ読み込み専用になるという理屈と似たようなものです。そのポインタ自身は別に何も変わっていません。だからアセンブラを使ったり、無理やりキャストしたりすれば、const属性付きのポインタしかなくても、書き込みアクセスはできてしまいます。でもそうであっても基本的には読み込み専用は守られて、目的は達せられます。
  • そもそもこういう不干渉の原則はいったい何のためにあるのでしょうか。それはバグを早く発見するためであり、またバグや悪意あるプログラムからほかのプログラムを守るためのものです。
  • 今回、プログラム開発者のことだけを考えるので、いったん悪意あるプログラムについては考えないことにしましょう。そうだとすれば、バグから守るために不干渉の原則があるのです。
  • しかしそもそも既存の環境にあっても、たいていの場合はプログラムにバグはなく、それゆえプログラムは一般保護例が起こすこともなく、無事に動いています。だからもしバグがないのであれば、不干渉の原則なんかあってもなくても同じことなのです。
  • そしてそんな原則をなくすことでバグを減らせるのであれば、それはとてもいいことなのではないでしょうか。
  • ということで、将来的には「信用できないEssenアプリをEssen上でどうやって実行したらいいのか」という問題はありますが、ひとまずそれは気にせず、今の路線で行けるところまで行ってみたいと思っています。
    • まあEssen上で一つだけアプリを動かせばいいだけなのかもしれないです。
    • Essenは同時に何個も起動できますし、Essen同士は干渉しないので。

(4-9)

  • 最後に言い訳めいたことも書きます。
  • まずこのプログラムは現在Windows専用になってしまっています。グラフィック系をどう移植したらいいかわからないからです。
  • またソースコードを見るときっと「いやこれはさすがにないわー」みたいなところがいくつかあると思います。でもこれもとにかく突貫で(睡眠時間を激しく削って)何とか作ったものなので、どうかそこは気にしないでください。このEssen2019-Aの目的は、私がどういうことを実現したいと思っているのかを実演するためのもので、かつ、それが実現可能であるということを示すためのものなのです。中ではズルみたいなこともやっていますが、それはすべて手抜きのせいであって、そうすべきだと思っているわけじゃないのです。
  • あと1週間あれば、念願のタスクセーブとタスクロードをつけられたんだけどなあ・・・。
    • つまり実行中のプロセスの状態をファイルに保存して、後日再開する機能のこと。
    • ぎりぎりで時間が足りないことが本当に悔やまれる・・・。

こめんと欄

  • Xlibを使う形に書き直せば、Linuxやmacにも対応できそうです。OSCが終わったらやってみたいです。 -- K 2019-02-20 (水) 00:33:42
  • お、OSCまでにセーブとロードを実装できそうな方法を思いついたかも?! -- K 2019-02-21 (木) 00:31:19
  • 展示会場で配布したパンフレット(というかチラシ程度のものですが)は、45枚配布できたようです。みなさん興味をもって手に取ってもらえて、しかもたくさん質問してくれてどうもありがとうございました。 -- K 2019-02-24 (日) 22:04:07

コメントお名前NameLink

リロード   新規 編集 差分 添付   トップ 一覧 検索 最終更新 バックアップ   ヘルプ   最終更新のRSS
Last-modified: 2019-02-24 (日) 22:04:07 (234d)