brly.github.io

09 February 2018 : クロース・エンカウンター 第4種接近遭遇

Movie

年末あたりに http://dic.nicovideo.jp/a/メタルマンの人 の動画からz級映画に傾倒していくつか消化してきたが、このクロース・エンカウンターは断トツで厳しい映画だった。

結局のところ、カメラが厳しいのが一番厳しいという感想で、脚本やCG、演技などが多少破綻していても映像がブレてなければ見られるが、カメラが厳しいとそもそも不快感が尋常ではないので内容の吟味すら厳しくなる。

クロース・エンカウンターは序盤からずっとその調子であるため、本当に厳しかった。

08 February 2018 : 2/8

CS
Memory
Reading

眠らせていた linux 機をまともに使用しはじめた。OSX 版と違って atom の起動が早くて良い。

どこかのツイートで「What Every Programmer Should Know Abount Memory」が最高というのを見かけたので、それを読み始めた。 結構長くてまださわりまでしか読めてないけど、確かに面白い。

前々からメモリ周りのちゃんとした理解を深めたいとは思っていたので、ちょうど良かった。 今は 2.2.2 Precharge and Activation の手前まで読んだ。

DRAM アクセスの際には RAS/CAS の信号が必要になり、それぞれがメモリコントローラのクロックの立ち上がりに合わせて動く。 Row を変更する必要がない場合はその分だけ処理する必要がなくなり、連続するメモリを読みだす場合はさらに有用になると。

今見ているやつだと cpu のクロックよりかなり遅くなるし、更に複数サイクル要するので確かに「メモリは遅い」と言いたくなるような。

頭文字だけをとって「wepskam」で通用するっぽい。検索するとヒットする。

14 October 2017 : gen_fsm から gen_statem への移行

Erlang

最近のおしごとであったので.

とあるモジュールが 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 みたいな感じで移行例みたいなのがあって, それに準じているのだけれど.

モデル

モデル化する対象として, 何かよいアイデアが思いつけばよかったのだけれど思いつかないので, 状態遷移を検索キーワードとしてよく出てきたストップウォッチさんを対象にすることに.

gen_fsm

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 イベントを発行するようにして, 保持している整数値をインクリメントするようにしている. これはストップウォッチの計測中画面をイメージしたものになっている.

gen_statem

マイグレーションする. 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 で実行.

マズイ点があったら教えてもらえると.

23 September 2017 : Go のジェネリクス

Golang

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++ 方式のようにバイナリの肥大化を招きかねないようなアプローチですることは無いだろうし, 実行速度が落ちるようなアプローチでも無いだろうなという気はする.

12 June 2017 : Jisaku

AMD
Ryzen

初めてPCを自作した. しかも安定ではなくコストパフォーマンスを取って AMD の Ryzen にした.

総額 20万くらい. 初めてなのでもちろん使いまわせるパーツなどもなく.

  • CPU: Ryzen7 1700
    • 付属の CPU クーラーを利用
  • GPU: GTX 1080 Ti
  • Memory: CMK16GX4M2A2666C16 (8GB x2)
  • ケース: H440-PLUS-BBL
  • HDD: 3.5 inch 2TB
  • マザーボード: PRIME X370 PRO
  • 電源: Corsair CX650M

とりあえず計算資源のために買ってみたので, 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 の boot が失敗する

live usb は手元の Mac Book Pro で Ubuntu 17.04 の iso を落とし, 以下の記事にしたがって作成.

いざ起動させると, なにやら起動しない.

どうやら検索してみると grub2 の画面で e を押して edit モードに入り, kernel の boot パラメータに acpi=off にすると boot する, というような上記 forum の書き込みがあった. 実際, それで起動した. あとでそれで苦しめられたんだけども.

HDD から boot しない

上の方法で無事 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 の調査・理解は以下のサイトが役に立った.

認識される CPU のコア数が 1

正直, ここまで来てヘトヘトだった. ここまでで 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: