年末あたりに http://dic.nicovideo.jp/a/メタルマンの人 の動画からz級映画に傾倒していくつか消化してきたが、このクロース・エンカウンターは断トツで厳しい映画だった。
結局のところ、カメラが厳しいのが一番厳しいという感想で、脚本やCG、演技などが多少破綻していても映像がブレてなければ見られるが、カメラが厳しいとそもそも不快感が尋常ではないので内容の吟味すら厳しくなる。
クロース・エンカウンターは序盤からずっとその調子であるため、本当に厳しかった。
眠らせていた linux 機をまともに使用しはじめた。OSX 版と違って atom の起動が早くて良い。
どこかのツイートで「What Every Programmer Should Know Abount Memory」が最高というのを見かけたので、それを読み始めた。 結構長くてまださわりまでしか読めてないけど、確かに面白い。
前々からメモリ周りのちゃんとした理解を深めたいとは思っていたので、ちょうど良かった。 今は 2.2.2 Precharge and Activation の手前まで読んだ。
DRAM アクセスの際には RAS/CAS の信号が必要になり、それぞれがメモリコントローラのクロックの立ち上がりに合わせて動く。 Row を変更する必要がない場合はその分だけ処理する必要がなくなり、連続するメモリを読みだす場合はさらに有用になると。
今見ているやつだと cpu のクロックよりかなり遅くなるし、更に複数サイクル要するので確かに「メモリは遅い」と言いたくなるような。
頭文字だけをとって「wepskam」で通用するっぽい。検索するとヒットする。
最近のおしごとであったので.
とあるモジュールが gen_fsm で実装されており OTP20 では deprecated なため, 将来のことを考えて移行する必要が出てきた.
gen_statem とかは http://erlang.org/doc/design_principles/statem.html にあるような雰囲気で, ある状態に対してイベントが発生するとそれに対するアクションと次の状態が得られるやつのようで.
例として gen_fsm でとある状態遷移のやつを作ったあとに gen_statem に移す場合の例を書いてみる.
まぁ公式の http://erlang.org/doc/man/gen_fsm.html にも migration to statem みたいな感じで移行例みたいなのがあって, それに準じているのだけれど.
モデル化する対象として, 何かよいアイデアが思いつけばよかったのだけれど思いつかないので, 状態遷移を検索キーワードとしてよく出てきたストップウォッチさんを対象にすることに.
https://github.com/brly/blog-coding/blob/master/2017/10-14-gen_statem/sw_fsm.erl
まず, 簡単にモジュールの説明を書くと start_link/0 で起動した後に push_hoge でイベントを起こすようにしていて, push_hoge メソッドが状態遷移図の “Push start” とかに相当している.
gen_fsm ではイベントに対して同期的と非同期的に対応する場合は関数が分かれており, Module:StateName/2 が非同期で Module:StateName/3 が同期的となる. アリティ3 の場合は From が引数に増えている.
さらに状態を問わず発生させたいイベントを処理するためのコールバックとして Module:handle_event/3 と Module:handle_sync_event/4 を用意する必要もある. これはさらに引数として現在の状態を表す StateName が増え, StateName に合わせて処理を変えて ~ のような感じになるはず.
今回はそのような global なイベントとして, 内部で保持している数値を出力させるためのイベントをつくった.
また, くっそ雑な実装として計測中(measuring)状態の時は timeout イベントを発行するようにして, 保持している整数値をインクリメントするようにしている. これはストップウォッチの計測中画面をイメージしたものになっている.
マイグレーションする. https://github.com/brly/blog-coding/blob/master/2017/10-14-gen_statem/sw_statem.erl
gen_statem に行くと, 先ほどまであった Module:StateName/2 やら Module:handle_event/3, Module:handle_sync_event/4 が消えていて, さらによくわからないが callback_mode/0 が増えており, そこで指定するやりようによっては全て “Module:StateName/3 で処理するように.” という感じになっている.
gen_fsm では状態とは関数名(=atom)であるという制限があったが, gen_statem ではパワーアップして状態を任意のtermとして扱えるようになっており, その場合は Module:handle_event/4 で処理するようになっている.
そうでなく, gen_fsm 形式を選ぶ場合は Module:StateName/3 で処理できるようだった.
どちらを選ぶかを表明するのが callback_mode/0 で callback_mode() = state_functions | handle_event_function
のどちらかの atom を返すことによって行える.
今回はとりあえず gen_fsm からの移行なので state_functions にしておく.
Module:StateName/3 しかないのにどうやって非同期イベントに対応するのかというと, 最初の引数で event_type() なるものを取り, これが cast だったり {call, From} だったり info だったり timeout だったりと様々で, これで場合分けして対応する方式になるようで.
また gen_fsm 時にあった handle_sync_event 的なやつはなくなっているので, 各状態で対応する形になるのだが, 今回は面倒くさいので各状態でそのような global なイベントに対応する時はもとの handle_sync_event を残しておいて, それを呼ぶように変更した.
若干面倒くさい点として, reply やら timeout の形式が変わっており, timeout は {next_state...
でやりたい時は末尾に整数値を加えるだけでよいのだが, reply の形式が変わっており, 新しく action()
という形式でやる必要があり
%% sw_fsm.erl
handle_sync_event(show, _From, StateName, Data) ->
Number = maps:get(display, Data, undefined),
Reply = [{state, StateName}, {display, Number}],
{reply, Reply, StateName, Data, 1000};
このように {reply..
から始まる形では使えずに, reply は action として渡す必要があり,
%% sw_statem.erl
handle_sync_event(show, From, StateName, Data) ->
Number = maps:get(display, Data, undefined),
Reply = [{state, StateName}, {display, Number}],
{keep_state, Data, [{reply, From, Reply}, {timeout, 1000, []}]};
こんな感じで, action は action()
の配列を渡せるので reply_action()
と event_timeout() = enter_action()
として渡すように.
面倒くさくなったけど, 機能的にはリッチになってるか.
適当に遊んでいるようす. OTP20 で実行.
マズイ点があったら教えてもらえると.
Docs: https://docs.google.com/document/d/1vrAy9gMpMoS3uaVphB32uVXX4pi-HnNjkMEgyAHX4N4/edit?pli=1#heading=h.vuko0u3txoew
ジェネリクスとその問題の心臓は以下になります. (generic より)
1. (C 方式) 放置. プログラマーを遅くさせる. しかし言語に複雑性を追加しない.
2. (C++ 方式) コンパイル時特殊化もしくはマクロ拡張. コンパイルを遅くする. 大量のコードを生成し, それらは冗長であり, 複製されたコピーを排除する良いリンカを必要とする. 独立した特殊化は効率的かもしれないが, プログラム全体としては命令キャッシュを貧しく使うことになり苦しめられる. template の使用を削除したことで text segment がメガバイトから 10 キロバイトに縮小した単純なライブラリについて聞いたことがあります.
3. (Java 方式) 全てを暗黙的にしまう. 実行を遅くする. C プログラマが書いた実装や C++ コンパイラが生成した実装と比較すると, Java コードはより小さいですが, 暗黙的なボクシングとアンボクシングのために空間計算量や時間計算量の面で効率が下がります. byte のベクトルは 1 byte あたり 1 バイトよりも大きな空間を使用します. ボクシングとアンボクシングを隠そうとすると, 型システムも複雑になる可能性があります. 一方で, 命令キャッシュをより良く使えるかも知れず, バイトのベクトルは別々に書き込むことが出来ます.
ジェネリクスのジレンマとは: プログラマを遅くしたいか, コンパイラを遅くさせバイナリを肥大化させたいか, 実行速度を遅くさせたいか.
一部分だけ雑に訳すとこのような感じ. バイトのベクトル? とかよくわかってないけれど…も, 概ねの雰囲気は分かった気になった.
C++ 方式は最悪種類の数だけ生やす事になるので, 上に書いたようにバイナリの肥大化とかキャッシュが有効に使えなくなったり, みたいな問題があり Java 方式はバイナリの肥大化は招かないものの, いわゆる int とかのプリミティブ型のジェネリックな関数を呼び出した時にボクシングしたりして遅くなりますよねという話.
上のドキュメントに書いてあったけど最近少しばかり話題の Rust は C++ 方式らしい. なるほど.
バイナリの肥大化の問題は書き手の問題になるとは思うけれど, そもそも言語がそれを許さないようにする (自由度が低いようにする) 方が, 言語としては開発者に優しいのでそういう方針ならば, そのままいけばいいと思う.
https://days2011.scala-lang.org/sites/days2011/files/ws3-1-Hundt.pdf
ちょっと昔に golang と多言語を比較する論文を読んだことがあり, 上のリンクはそれで, 内容は大体忘れてしまったけれど たしか書き手を選ぶが C++ が最高の結果であり, golang は実行速度とかバイナリサイズが大きくて色々負けていて, これから改善していきたい, みたいな話だったような気がする. 今は結構人気が出てきたけれどもジェネリクスの追加を C++ 方式のようにバイナリの肥大化を招きかねないようなアプローチですることは無いだろうし, 実行速度が落ちるようなアプローチでも無いだろうなという気はする.
初めてPCを自作した. しかも安定ではなくコストパフォーマンスを取って AMD の Ryzen にした.
総額 20万くらい. 初めてなのでもちろん使いまわせるパーツなどもなく.
とりあえず計算資源のために買ってみたので, OS は Linux (Ubuntu 17.04) にした. なんかよくわかってないけど, kernel 4.10 以降がおすすめだという話から. そもそも LTS じゃないし多分消すだろうし, とりあえず使えればよいという心持ち.
物理的な組み立ては, ケース名で検索したりして出てきた blog などを参考にした.
この例では Q-Connector などの説明があり, 何の知識もないので助かった.
苦戦したのは電源から伸びる CPU 補助電源ケーブル 8pin が足りなくて, 延長ケーブルが必要になった程度. ビッグカメラに行って, 8pin の 12V と書いてあるものを適当に買ったら EPS 12v と書いてあり, マザーボードの説明書には EATX 12v とあり, あれ違うと思ったけど刺してみたら動いた.
マザーボードの HDMI 出力からディスプレイにつないで BIOS 画面を待っていたけど, 一向に出ない.
調べたところ, どうやらあの出力は 対応した APU (?) がついてないと使えないものらしく, GTX につなぐと BIOS の画面が出力された.
Chrome の検索履歴を見る限りでは, ここで数時間溶かした模様.
live usb は手元の Mac Book Pro で Ubuntu 17.04 の iso を落とし, 以下の記事にしたがって作成.
いざ起動させると, なにやら起動しない.
どうやら検索してみると grub2 の画面で e を押して edit モードに入り, kernel の boot パラメータに acpi=off
にすると boot する, というような上記 forum の書き込みがあった. 実際, それで起動した. あとでそれで苦しめられたんだけども.
上の方法で無事 live usb が起動し, gui でインストールを進め, 最後に「インストールメディアを抜いたら enter 押してね」のメッセージの画面が表示されて無事 HDD へのインストールは終わり, に見えたが enter を押しても反応がない.
前にも似た現象(ただし、古い Ubuntu であったが)があり, その際は強制的に終了して再起動したらついたので, 1分くらい待ってから強制的に再起動した.
そして HDD から boot させようとしてみると, grub2 の画面が出ることなく紫一色の画面で止まってしまう.
この時点で考えられることとしては, 先程 live usb を起動させようとした時と同じように boot の引数に同じ acpi=off
を足す必要があったのではないか, ということだった.
そこで, live usb を再び同じようにして起動させ, その時のインストール先であった /dev/sda2
をマウントして, grub2 の設定のなにがしかを変えれば行けるはずと考える.
マウントまでは出来たが, grub2 の設定は最後に sudo update-grub
を叩く必要があった. これはログイン中のファイルシステムの構造に依存してしまう. live usb 上で sudo update-grub
を叩いても, live usb のファイルシステム上で grub2 の設定が行われるだけになってしまう.
実際, これは chroot
をマウント先ですることで解決出来た.
ところで update-grub
は /etc/default/grub
の設定を読み込み, /boot/grub/grub.cfg
を出力ファイルとして吐き出すようなのだが, マウントした先 (HDDインストール先) には /boot/grub/grub.cfg
が生成されていなかった. これはインストールに失敗していた, と考えるのだろうか…
まーよくわからないけど acpi=off
するように /etc/default/grub
をいじって, update-grub
して grub.cfg が生成されていることを確認し, HDD ブートを試みると, ちゃんと起動された.
grub2 の調査・理解は以下のサイトが役に立った.
正直, ここまで来てヘトヘトだった. ここまでで 1日での作業であって, 不慣れな作業が続いたこともあった.
apt から GTX のドライバを入れたりして一息ついていたが, top を叩いて 1 を押して cpu の具合を見てみると 1 つしかない. web に上がっている cpuinfo の情報を見る限りでは, どうやらコア数表示されるもののようだ. もちろん, cpuinfo でも processor :0
しかない.
BIOS の設定をいじくりまわしたが, なにも変わらず. マザーボードかCPU のハズレを引いたんや, という気持ちと「やっぱ安定の Intel にしておけば…」という気持ちに苛まれつつも調査を進めると同じような症状を見る人が見られる.
自分と同じように「 acpi=off
にしてなんとか起動したが 1 コアしか認識されないんだよ」, という書き込みが幾つかの forum で見られた. 大体の forum はそこで終わってしまっている.
1コアしか認識されないことと, acpi=off
に関連が見られ, よーするにこれを切れば行けるのではという気持ちになり試してみると, うまくいった. top で 1 を押すと 16 コア分表示される.
acpi=off
にして起動しなくなるのかと思いきや, どうやらそうではなくなっていたようだ.
あんまり良くわかっていないが, GTX のドライバを当てる前ではそれが必要で, 当てた後は必要なくなっていた.
どうやら acpi=off
がかなり強力なパラメータらしく, 所望の動作(=ドライバを当てる前にうまく boot させる)としては nomodeset
で事足りているようだった.
作業した日はこれでヘトヘトになり, 何か重たいワークロードを動かしてみる気にもならなかった.
あとは現時点でよく書かれているように usb 周りの動作がやばい. これは原因がどこなのかあんまり良くわかってないが. usb 機器の動作が不安定なのはもちろん, usb ハブを繋いでると Windows ですらブートに失敗するなど色々見られる.
個人的にはよく使うので, 早く治って欲しい :pray: