ReadyNasでSFTPしたい

概要

ReadyNASは、だいたいNTT-X Storeのメルマガで嫌というほど見ているNASだと思うが、NAS製品ではちょっと安めで定番である。

機能的にも充実していて使えるNASなのだが、ちょっと弱点がある。 それは、「iSCSI LUNにディスク全体の90%しか割り当てられない」ことだ。 どうも、90%以上ディスクを使わせないという考えかららしいのだが、はっきりいって10%もデッドにされるのは無駄である。

仕方ないので残り10%(3Tくらいはある)をなんとか使いたいのだが、そうなると共有ドライブということになるだろう。 まぁ、iSCSIボリューム以外に共有しない形で存在するディスクがあるとそれはそれで便利でもある。

しかし、その選択肢は、SMB, NFS, AFP, FTP, HTTP…

SFTPがない。 せっかくディスクの暗号化をしているのに、さすがに共有ディスクがネットワークから丸見えではお話にならない。 やはりSFTPがなくてはならないだろう。SFTPが使えればNemoやSSHFSも使えるし。

そこでReadyNASでSFTPを使えるようにする。 普段LinuxやSSHをよく使っている人には目新しさのないお話だ。

具体的手順

SSHDを有効にする

システム → 設定 → SSHで有効にする。 「SSHを有効にする」にチェックを入れて適用するだけで良い。 警告が出るが、続行する。

鍵を作る

ssh-keygen -f ~/.ssh/readynas_rsaという感じでいいだろう。

アカウントを用意する

アカウント → ユーザー → 新しいユーザー でユーザーを作る。 特にNFSを使う気がないのならUIDはそれほど気にする必要はないだろう。

ユーザーを作ったらユーザーを選択し、設定からSSHタブを選択。 「シェルアクセスを許可」にチェックを入れ、「公開キーのインポート」でreadynas_rsa.pubをアップロードする。 このとき、「rsyncのみ」のチェックを外すこと。

ログインして設定

これでログインできるようになった。

コントロールパネルから設定した鍵は~/.ssh/ssh_authorized_keysに入るようになっているが、このファイルは書き込みできず、~/ssh_authorized_keysも有効である。 そこで、もう1ペア鍵を作り、

$ scp -i ~/.ssh/readynas_rsa ~/.ssh/readynas_sftp_rsa.pub user@readynas:.ssh/authorized_keys

のようにコピーするのだが、その前にコピー元のファイルでcommand="internal-sftp"を手前に追加しておくといいだろう。

これでSFTPの準備は完了

~/.ssh/configに記述しておけば簡単にSFTPアクセスができるようになった。 非常に柔軟に扱うことができてとても良い。

なお、ルートディレクトリを除くとどれがドライブディレクトリかはすぐ分かると思うが、/homeもディスクアレイのbtrfsがマウントされている。 ドライブディレクトリ以下にもhomeというディレクトリがあるけれど、これは「共有」にある「ホーム」にあたるもので、別にホームディレクトリに直接ファイルを配置しても問題はなさそうだ。

なにはともあれ、とても使いやすくなってバンザイ。

LinuxでNvidiaもIntelもおかしなことになっているのでRadeonにかえてみた

もういやだ

パワフルなThinkStation P720ワークステーションだが、最近様子がおかしい。 Nvidiaではおなじみの

  • 画面が点滅する
  • X11 APIでスクリーンキャストするとウィンドウが消えてしまう (だからスクリーンショットでもウィンドウが消える)

という問題は直る気配がない…どころか、従来はVblankとFlippingを禁止すれば抑制できたのだが、最近はダメだ。 さらにいえば、Nvidia固有だったこの問題は、最近ではIntelでも発生する、というかINtelのほうがひどくなっている。

そして、追加で

  • Libreofficeが使っているSAL PLUGINとglibの連携が腐ってしまって、ほとんど操作不能なレベルでフリーズする
  • Vivaldiでありとあらゆる操作においてフリーズする

という問題が追加された。 Libreofficeの問題は最初Nvidiaだけだったが、現在はIntelでも発生するし、SAL_USE_VCLPLUGIN=genで防げたのだが、現在はこれも無理である。

そして、4kディスプレイにしてからビデオメモリが足りないのか、ウィンドウを開いていくとウィンドウ領域がグリッド状に表示され、かすかにウィンドウが見える場合もあるがほとんどの場合見えず、操作もできないという状態になる、ということに悩まされた。 作業効率が恐ろしく悪いことになってしまうため、毎日とてもストレスだし、貴重な時間を、1日で1-2時間程度失っている状態だった。

もうこれは耐えられない。

一方、Godavariはこのような問題を発生しない。 「じゃあGodavariでやれよ」と思うかもしれないが、Godavariは全面的にとっても非力である。よく馬鹿にされるくらいには。

それで、現在の出費は痛いし、TSMCは7mmプロセッサを控えている状態だというのはわかっているが、 結局のところRadeon 3000シリーズは噂の域を出ないということも踏まえて、思い切ってRadeon RX580を購入することにした。

なぜRX580か

最大の理由はLinuxにおけるパフォーマンスである。

WindowsにおいてはVega RXもそれなりの性能を示すようだが、Linuxにおいては多くの場合Vega RXとの性能差は誤差の範疇に留まる。 Vega RXでもGTX1060は越えない、というような所ぅたいだ。

にもかかわらずVega RXはずっと高値を維持しており、相当にコストパフォーマンスが悪い。 しかも、Vega RXはNvidiaと比べても相当燃費が悪いようで、「電気をめちゃくちゃ食う」のである。

そもそも私のP720はQuadro P6000のSLIを想定しているためキャパシティとしては耐えられないこともないのだが、家の電気回線はそうではないし、 無駄な電気は使いたくない。

また、Vega RXのパフォーマンスはVega RX 64であっても「Nvidia GTX1070に届かない程度」というのがWindowsにおいても現実であるようで、 ゲームのことを考えて高性能なカードを選ぶ、という気にもなれなかった。

タイミングは悪いなぁ、やたら高いなぁと思いつつも、Linuxにおいてはベストな選択肢となりそうなRX580を選択したということだ。

製品はAMD専門メーカーSapphireのゲーミング向けNITRO+シリーズSA-RX580-8GDN+001V2 VD6801を選択した。 オーバークロック製品で、発熱量は多いが性能は高いようだ。

P720にSapphire NITRO+ RX580は「ギリギリ入る」

製品が届いてびっくりした。 「大きい」

いやもう、比べるのが無意味なぐらい大きい。 「ミドルクラスだし、GTX960と同じくらいだろう」とか思ってサイズのことは一切気にしなかったのだが、「これはやばいかもしれない」と感じるほど大きかった。 ゲーミング向けトップモデルはこれより大きいのか!?

入らないかもしれない、と懸念したが、結局はギリギリながら収まった。 上下とも入るが、下段に関しては電源配線に触れてしまう。 また、サイドパネルに接触するため、サイドパネルがしめづらくなる。

「普通に入る」とは言い難い、「かろうじて入った」である。 だが、入る。

なお、SA-RX580-8GDN+001V2は補助電源が8ピン+6ピンというGTX1080Ti並の要求で、P720の標準の補助電源は2本であるためこれを全部使ってしまい、SLI構成はできない。 汎用の拡張コネクタがあるのだが、こちらは専用品で、なおかつLenovoとして単体の販売はしていないので、Crossfire構成にしたい場合は予めP6000のような「補助電源を2つ使う」ビデオカードを2枚構成にしておく必要がある。

クーリングパイプがまるでインテークマニホールドのようで、さらにSAPPHIREのロゴが青く輝き、大変かっこいい。 まぁ、P720の場合サイドパネルが透明なわけでもないので、全く見えないが。

消費電力は

4114+Quadro P400構成と比較すると、アイドル時で107Wと、約60W増えた。 運用時も140から220Wで、60W増加と見ていいだろう。 ただし、変動しやすくなったため、平均すると80W増加とみたほうがいいかもしれない。

ただし、変動しやすくなったのは、「今までP400で処理しきれなかった分を即座に処理する瞬発力があるため」と見ることができ、効率的には変わっていないかもしれない。

「省電力」という印象はなくなってしまったが、性能を考えればまぁ想定通りといったところ。

問題は解消したか

ほぼyesである。

  • 画面の点滅はなくなった。代わりにログイン時に画面がおかしくなることがあるが、支障は実際にはない
  • スクリーンショット、及びスクリーンキャスティングでウィンドウが消える不具合はなくなった
  • Libreofficeがフリーズする問題は解消された
  • Vivaldiが異様にフリーズするという問題は 軽減された
  • ウィンドウが表示できなくなる問題は解消された

また、私が非常に不満としていた、私お気に入りのフラゲ1が重すぎるという問題が事実上解消された。

この問題自体はつい先日からChromiumがマルチスレッドでFlashを処理してくれるようになり、最大で12.5%(5スレッド)で処理してくれるためにかなり軽減されてはいたのだが、 ビデオ描画のもたつきがなくなったためか、全く支障なくなった。 不思議なものだ。 まぁ、Flashはビデオアクセラレーションの対象ではあるのだが。

ちなみに、このゲームがあまりにも重いのは「4114が非力で、うち特有の問題」と考えていたのだが、先日Thinkpad X1でやってみたところ、とてもプレイできないくらい重かった。 というわけで、むしろ「このハイパフォーマンスマシンですらもこの程度」という状態だったと考えられる。 恐らくはWindowsであれば支障ないのだろう。

Vivaldiの重さはどこから

RX580になってVivaldiが操作のたびに、あるいはフォーカスするたびに止まる (タブ=レンダリングプロセスが止まるのではなく、Vivaldi全体が止まる)という問題は解消された。

ちなみに、Intelはこの問題はより深刻で、Vivaldiを立ち上げたあとメニュー操作をするとウィンドウマネージャごと固まってしまう。

だが、依然としてふたつの問題がある。

ひとつは、右クリックでメニューを開くとかなり長い時間固まるということだ。 これはstraceしてみると

read(17, "!", 2)                        = 1
write(18, "!", 1)                       = 1
openat(AT_FDCWD, "/usr/local/share/fonts/Commercial/FONT-COMP-SET21/Fontstyles2/FontGraphic_Mac/UniversalWalk.otf", O_RDONLY) = 251
fstat(251, {st_mode=S_IFREG|0644, st_size=8832, ...}) = 0
mmap(NULL, 8832, PROT_READ, MAP_PRIVATE, 251, 0) = 0x7fc6fd5f7000
close(251)                              = 0
munmap(0x7fc6fd5f7000, 8832)            = 0
openat(AT_FDCWD, "/usr/local/share/fonts/Commercial/FONT-COMP-SET21/Fontstyles2/FontGraphic_Mac/UniversalWalk.otf", O_RDONLY) = 251
fstat(251, {st_mode=S_IFREG|0644, st_size=8832, ...}) = 0
mmap(NULL, 8832, PROT_READ, MAP_PRIVATE, 251, 0) = 0x7fc6fd5f7000
close(251)                              = 0
munmap(0x7fc6fd5f7000, 8832)            = 0
openat(AT_FDCWD, "/usr/local/share/fonts/Commercial/FONT-COMP-SET21/Fontstyles2/FontGraphic_Mac/UniversalWalk.otf", O_RDONLY) = 251
fstat(251, {st_mode=S_IFREG|0644, st_size=8832, ...}) = 0
mmap(NULL, 8832, PROT_READ, MAP_PRIVATE, 251, 0) = 0x7fc6fd5f7000
close(251)                              = 0
munmap(0x7fc6fd5f7000, 8832)            = 0
openat(AT_FDCWD, "/usr/local/share/fonts/Commercial/FONT-COMP-SET21/Fontstyles2/FontGraphic_Mac/UniversalWalk.otf", O_RDONLY) = 251
fstat(251, {st_mode=S_IFREG|0644, st_size=8832, ...}) = 0
mmap(NULL, 8832, PROT_READ, MAP_PRIVATE, 251, 0) = 0x7fc6fd5f7000
close(251)                              = 0
munmap(0x7fc6fd5f7000, 8832)            = 0
openat(AT_FDCWD, "/usr/local/share/fonts/Commercial/FONT-COMP-SET21/Fontstyles2/FontGraphic_Mac/UniversalWalk.otf", O_RDONLY) = 251
fstat(251, {st_mode=S_IFREG|0644, st_size=8832, ...}) = 0
mmap(NULL, 8832, PROT_READ, MAP_PRIVATE, 251, 0) = 0x7fc6fd5f7000
close(251)                              = 0
munmap(0x7fc6fd5f7000, 8832)            = 0
openat(AT_FDCWD, "/usr/local/share/fonts/Commercial/FONT-COMP-SET21/Fontstyles2/FontGraphic_Mac/UniversalWalk.otf", O_RDONLY) = 251
fstat(251, {st_mode=S_IFREG|0644, st_size=8832, ...}) = 0
mmap(NULL, 8832, PROT_READ, MAP_PRIVATE, 251, 0) = 0x7fc6fd5f7000
close(251)                              = 0
munmap(0x7fc6fd5f7000, 8832)            = 0
openat(AT_FDCWD, "/usr/local/share/fonts/Commercial/FONT-COMP-SET21/Fontstyles2/FontGraphic_Mac/UniversalWalk.otf", O_RDONLY) = 251
fstat(251, {st_mode=S_IFREG|0644, st_size=8832, ...}) = 0
mmap(NULL, 8832, PROT_READ, MAP_PRIVATE, 251, 0) = 0x7fc6fd5f7000
close(251)                              = 0
munmap(0x7fc6fd5f7000, 8832)            = 0
openat(AT_FDCWD, "/usr/local/share/fonts/Commercial/FONT-COMP-SET21/Fontstyles2/FontGraphic_Mac/UniversalWalk.otf", O_RDONLY) = 251
fstat(251, {st_mode=S_IFREG|0644, st_size=8832, ...}) = 0
mmap(NULL, 8832, PROT_READ, MAP_PRIVATE, 251, 0) = 0x7fc6fd5f7000
close(251)                              = 0
madvise(0x3a38dab69000, 57344, MADV_DONTNEED) = 0
madvise(0x3a38dab69000, 57344, MADV_DONTNEED) = 0
madvise(0x3a38d9095000, 8994816, MADV_DONTNEED) = 0
madvise(0x3a38dab69000, 57344, MADV_DONTNEED) = 0
madvise(0x3a38dab69000, 57344, MADV_DONTNEED) = 0
madvise(0x3a38dab69000, 57344, MADV_DONTNEED) = 0
madvise(0x3a38dab8b000, 49152, MADV_DONTNEED) = 0
madvise(0x3a38d9095000, 8994816, MADV_DONTNEED) = 0
access("/usr/local/share/fonts/DeletedManjaro/UmePlus/umeplus-p-gothic.ttf", R_OK) = 0
madvise(0x3a38dab8b000, 385024, MADV_DONTNEED) = 0
madvise(0x3a38dab69000, 57344, MADV_DONTNEED) = 0
madvise(0x3a38dab69000, 57344, MADV_DONTNEED) = 0
madvise(0x3a38dab69000, 57344, MADV_DONTNEED) = 0
madvise(0x3a38dab8b000, 49152, MADV_DONTNEED) = 0
madvise(0x3a38d9095000, 8994816, MADV_DONTNEED) = 0
access("/usr/share/fonts/TTF/DejaVuSans.ttf", R_OK) = 0
madvise(0x3a38dab8b000, 385024, MADV_DONTNEED) = 0
madvise(0x3a38dab69000, 57344, MADV_DONTNEED) = 0
madvise(0x3a38dab69000, 57344, MADV_DONTNEED) = 0
madvise(0x3a38dab69000, 57344, MADV_DONTNEED) = 0
madvise(0x3a38dab8b000, 49152, MADV_DONTNEED) = 0
madvise(0x3a38d9095000, 8994816, MADV_DONTNEED) = 0
access("/usr/local/share/fonts/Monospace/font-bh-ttf-1.0.3/luxisr.ttf", R_OK) = 0
madvise(0x3a38dab8b000, 385024, MADV_DONTNEED) = 0
madvise(0x3a38dab69000, 57344, MADV_DONTNEED) = 0
madvise(0x3a38dab69000, 57344, MADV_DONTNEED) = 0
madvise(0x3a38dab69000, 57344, MADV_DONTNEED) = 0
madvise(0x3a38dab8b000, 49152, MADV_DONTNEED) = 0
madvise(0x3a38d9095000, 8994816, MADV_DONTNEED) = 0
access("/usr/share/fonts/gsfonts/NimbusSans-Regular.otf", R_OK) = 0
madvise(0x3a38dab8b000, 385024, MADV_DONTNEED) = 0
madvise(0x3a38dab69000, 57344, MADV_DONTNEED) = 0
madvise(0x3a38dab69000, 57344, MADV_DONTNEED) = 0
madvise(0x3a38dab69000, 57344, MADV_DONTNEED) = 0
madvise(0x3a38dab8b000, 49152, MADV_DONTNEED) = 0
madvise(0x3a38d9095000, 8994816, MADV_DONTNEED) = 0
access("/usr/share/fonts/TTF/MuktiNarrow.ttf", R_OK) = 0
madvise(0x3a38dab8b000, 385024, MADV_DONTNEED) = 0
madvise(0x3a38dab69000, 57344, MADV_DONTNEED) = 0
madvise(0x3a38dab69000, 57344, MADV_DONTNEED) = 0
madvise(0x3a38dab69000, 57344, MADV_DONTNEED) = 0
madvise(0x3a38dab8b000, 49152, MADV_DONTNEED) = 0
madvise(0x3a38d9095000, 8994816, MADV_DONTNEED) = 0
access("/usr/share/fonts/TTF/malayalam.ttf", R_OK) = 0
madvise(0x3a38dab8b000, 385024, MADV_DONTNEED) = 0
madvise(0x3a38dab69000, 57344, MADV_DONTNEED) = 0
madvise(0x3a38dab69000, 57344, MADV_DONTNEED) = 0
madvise(0x3a38dab69000, 57344, MADV_DONTNEED) = 0
madvise(0x3a38dab8b000, 49152, MADV_DONTNEED) = 0
madvise(0x3a38d9095000, 8994816, MADV_DONTNEED) = 0
access("/usr/share/fonts/TTF/Sampige.ttf", R_OK) = 0
madvise(0x3a38dab8b000, 385024, MADV_DONTNEED) = 0
madvise(0x3a38dab69000, 57344, MADV_DONTNEED) = 0
madvise(0x3a38dab69000, 57344, MADV_DONTNEED) = 0
madvise(0x3a38dab69000, 57344, MADV_DONTNEED) = 0
madvise(0x3a38dab8b000, 49152, MADV_DONTNEED) = 0
madvise(0x3a38d9095000, 8994816, MADV_DONTNEED) = 0
access("/usr/share/fonts/TTF/padmaa-Medium-0.5.ttf", R_OK) = 0
madvise(0x3a38dab8b000, 385024, MADV_DONTNEED) = 0
madvise(0x3a38dab69000, 57344, MADV_DONTNEED) = 0
madvise(0x3a38dab69000, 57344, MADV_DONTNEED) = 0
madvise(0x3a38dab69000, 57344, MADV_DONTNEED) = 0
madvise(0x3a38dab8b000, 49152, MADV_DONTNEED) = 0
madvise(0x3a38d9095000, 8994816, MADV_DONTNEED) = 0
access("/usr/share/fonts/TTF/VL-Gothic-Regular.ttf", R_OK) = 0
madvise(0x3a38dab8b000, 385024, MADV_DONTNEED) = 0
madvise(0x3a38dab69000, 57344, MADV_DONTNEED) = 0
madvise(0x3a38dab69000, 57344, MADV_DONTNEED) = 0
madvise(0x3a38dab69000, 57344, MADV_DONTNEED) = 0
madvise(0x3a38dab8b000, 49152, MADV_DONTNEED) = 0
madvise(0x3a38d9095000, 8994816, MADV_DONTNEED) = 0
access("/usr/share/fonts/OTF/ipag.ttf", R_OK) = 0
madvise(0x3a38dab8b000, 385024, MADV_DONTNEED) = 0
madvise(0x3a38dab69000, 57344, MADV_DONTNEED) = 0
madvise(0x3a38dab69000, 57344, MADV_DONTNEED) = 0
madvise(0x3a38dab69000, 57344, MADV_DONTNEED) = 0
madvise(0x3a38dab8b000, 49152, MADV_DONTNEED) = 0
madvise(0x3a38d9095000, 8994816, MADV_DONTNEED) = 0
access("/usr/share/fonts/TTF/TSCu_Paranar.ttf", R_OK) = 0
madvise(0x3a38dab8b000, 385024, MADV_DONTNEED) = 0
madvise(0x3a38dab69000, 57344, MADV_DONTNEED) = 0
madvise(0x3a38dab69000, 57344, MADV_DONTNEED) = 0
madvise(0x3a38dab69000, 57344, MADV_DONTNEED) = 0
madvise(0x3a38dab8b000, 49152, MADV_DONTNEED) = 0
madvise(0x3a38d9095000, 8994816, MADV_DONTNEED) = 0
access("/usr/share/fonts/TTF/NanumGothic.ttf", R_OK) = 0
madvise(0x3a38dab8b000, 385024, MADV_DONTNEED) = 0
madvise(0x3a38dab69000, 57344, MADV_DONTNEED) = 0
madvise(0x3a38dab69000, 57344, MADV_DONTNEED) = 0
madvise(0x3a38dab69000, 57344, MADV_DONTNEED) = 0
madvise(0x3a38dab8b000, 49152, MADV_DONTNEED) = 0
madvise(0x3a38d9095000, 8994816, MADV_DONTNEED) = 0
access("/usr/local/share/fonts/Korean/UnDotumBold.ttf", R_OK) = 0
madvise(0x3a38dab8b000, 385024, MADV_DONTNEED) = 0
madvise(0x3a38dab69000, 57344, MADV_DONTNEED) = 0
madvise(0x3a38dab69000, 57344, MADV_DONTNEED) = 0
madvise(0x3a38dab69000, 57344, MADV_DONTNEED) = 0
madvise(0x3a38dab8b000, 49152, MADV_DONTNEED) = 0
madvise(0x3a38d9095000, 8994816, MADV_DONTNEED) = 0
access("/usr/local/share/fonts/Commercial/Unknown/CODE2000.ttf", R_OK) = 0
madvise(0x3a38dab8b000, 385024, MADV_DONTNEED) = 0
madvise(0x3a38dab69000, 57344, MADV_DONTNEED) = 0
madvise(0x3a38dab69000, 57344, MADV_DONTNEED) = 0
madvise(0x3a38dab69000, 57344, MADV_DONTNEED) = 0
madvise(0x3a38dab8b000, 49152, MADV_DONTNEED) = 0
madvise(0x3a38d9095000, 8994816, MADV_DONTNEED) = 0
access("/usr/local/share/fonts/Morisawa/\343\203\222\343\203\251\343\202\256\343\203\216\344\270\270\343\202\264 Pr6N W4.otf", R_OK) = 0
madvise(0x3a38dab8b000, 385024, MADV_DONTNEED) = 0
madvise(0x3a38dab69000, 57344, MADV_DONTNEED) = 0
madvise(0x3a38dab69000, 57344, MADV_DONTNEED) = 0
madvise(0x3a38dab69000, 57344, MADV_DONTNEED) = 0
madvise(0x3a38dab8b000, 49152, MADV_DONTNEED) = 0
madvise(0x3a38d9095000, 8994816, MADV_DONTNEED) = 0
access("/usr/local/share/fonts/Morisawa/\343\203\222\343\203\251\343\202\256\343\203\216\344\270\270\343\202\264 Upr W4.otf", R_OK) = 0
madvise(0x3a38dab8b000, 385024, MADV_DONTNEED) = 0
madvise(0x3a38dab69000, 57344, MADV_DONTNEED) = 0
madvise(0x3a38dab69000, 57344, MADV_DONTNEED) = 0
madvise(0x3a38dab69000, 57344, MADV_DONTNEED) = 0
madvise(0x3a38dab8b000, 49152, MADV_DONTNEED) = 0
madvise(0x3a38d9095000, 8994816, MADV_DONTNEED) = 0
access("/usr/share/fonts/TTF/Mathematica6.ttf", R_OK) = 0
madvise(0x3a38dab8b000, 385024, MADV_DONTNEED) = 0
madvise(0x3a38dab69000, 57344, MADV_DONTNEED) = 0
madvise(0x3a38dab69000, 57344, MADV_DONTNEED) = 0
madvise(0x3a38dab69000, 57344, MADV_DONTNEED) = 0
madvise(0x3a38dab8b000, 49152, MADV_DONTNEED) = 0
madvise(0x3a38d9095000, 8994816, MADV_DONTNEED) = 0
access("/usr/share/fonts/TTF/Mathematica7.ttf", R_OK) = 0
madvise(0x3a38dab8b000, 385024, MADV_DONTNEED) = 0
madvise(0x3a38dab69000, 57344, MADV_DONTNEED) = 0
madvise(0x3a38dab69000, 57344, MADV_DONTNEED) = 0
madvise(0x3a38dab69000, 57344, MADV_DONTNEED) = 0
madvise(0x3a38dab8b000, 49152, MADV_DONTNEED) = 0
madvise(0x3a38d9095000, 8994816, MADV_DONTNEED) = 0
access("/usr/share/fonts/TTF/AndikaRegular.ttf", R_OK) = 0
madvise(0x3a38dab8b000, 385024, MADV_DONTNEED) = 0
madvise(0x3a38dab69000, 57344, MADV_DONTNEED) = 0
madvise(0x3a38dab69000, 57344, MADV_DONTNEED) = 0
madvise(0x3a38dab69000, 57344, MADV_DONTNEED) = 0
madvise(0x3a38dab8b000, 49152, MADV_DONTNEED) = 0
madvise(0x3a38d9095000, 8994816, MADV_DONTNEED) = 0
access("/usr/local/share/fonts/Japanese/Tsuyuzora-bunko/XZBB.ttf", R_OK) = 0
madvise(0x3a38dab8b000, 385024, MADV_DONTNEED) = 0
madvise(0x3a38dab69000, 57344, MADV_DONTNEED) = 0
madvise(0x3a38dab69000, 57344, MADV_DONTNEED) = 0
madvise(0x3a38dab69000, 57344, MADV_DONTNEED) = 0
madvise(0x3a38dab8b000, 49152, MADV_DONTNEED) = 0
madvise(0x3a38d9095000, 8994816, MADV_DONTNEED) = 0
access("/usr/local/share/fonts/Japanese/WadaLab/wlcmaru2004aribu4430/wlcmaru2004aribu.ttf", R_OK) = 0
madvise(0x3a38dab8b000, 385024, MADV_DONTNEED) = 0
madvise(0x3a38dab69000, 57344, MADV_DONTNEED) = 0
madvise(0x3a38dab69000, 57344, MADV_DONTNEED) = 0
madvise(0x3a38dab69000, 57344, MADV_DONTNEED) = 0
madvise(0x3a38dab8b000, 49152, MADV_DONTNEED) = 0
madvise(0x3a38d9095000, 8994816, MADV_DONTNEED) = 0
access("/usr/share/fonts/OTF/FiraSans-Regular.otf", R_OK) = 0
madvise(0x3a38dab8b000, 385024, MADV_DONTNEED) = 0
madvise(0x3a38dab69000, 57344, MADV_DONTNEED) = 0
madvise(0x3a38dab69000, 57344, MADV_DONTNEED) = 0
madvise(0x3a38dab69000, 57344, MADV_DONTNEED) = 0
madvise(0x3a38dab8b000, 49152, MADV_DONTNEED) = 0
madvise(0x3a38d9095000, 8994816, MADV_DONTNEED) = 0
access("/usr/local/share/fonts/Japanese/nukamiso/nukamiso_yamiyootf_beta07.otf", R_OK) = 0
madvise(0x3a38dab8b000, 385024, MADV_DONTNEED) = 0
madvise(0x3a38dab69000, 57344, MADV_DONTNEED) = 0
madvise(0x3a38dab69000, 57344, MADV_DONTNEED) = 0
madvise(0x3a38dab69000, 57344, MADV_DONTNEED) = 0
madvise(0x3a38dab8b000, 49152, MADV_DONTNEED) = 0
madvise(0x3a38d9095000, 8994816, MADV_DONTNEED) = 0
access("/usr/share/fonts/OTF/Vollkorn-Regular.otf", R_OK) = 0
madvise(0x3a38dab8b000, 385024, MADV_DONTNEED) = 0
madvise(0x3a38dab69000, 57344, MADV_DONTNEED) = 0
madvise(0x3a38dab69000, 57344, MADV_DONTNEED) = 0
madvise(0x3a38dab69000, 57344, MADV_DONTNEED) = 0
madvise(0x3a38dab8b000, 49152, MADV_DONTNEED) = 0
madvise(0x3a38d9095000, 8994816, MADV_DONTNEED) = 0
access("/usr/share/fonts/TTF/Ricty-Regular.ttf", R_OK) = 0
madvise(0x3a38dab8b000, 385024, MADV_DONTNEED) = 0
madvise(0x3a38dab69000, 57344, MADV_DONTNEED) = 0
madvise(0x3a38dab69000, 57344, MADV_DONTNEED) = 0
madvise(0x3a38dab69000, 57344, MADV_DONTNEED) = 0
madvise(0x3a38dab8b000, 49152, MADV_DONTNEED) = 0
madvise(0x3a38d9095000, 8994816, MADV_DONTNEED) = 0
access("/usr/local/share/fonts/Commercial/Motoya/NFbc1kp.ttc", R_OK) = 0
madvise(0x3a38dab8b000, 385024, MADV_DONTNEED) = 0
madvise(0x3a38dab69000, 57344, MADV_DONTNEED) = 0
madvise(0x3a38dab69000, 57344, MADV_DONTNEED) = 0
madvise(0x3a38dab69000, 57344, MADV_DONTNEED) = 0
madvise(0x3a38dab8b000, 49152, MADV_DONTNEED) = 0
madvise(0x3a38d9095000, 8994816, MADV_DONTNEED) = 0
access("/usr/local/share/fonts/Japanese/Motoya/MTLc3m.ttf", R_OK) = 0
madvise(0x3a38dab8b000, 385024, MADV_DONTNEED) = 0
madvise(0x3a38dab69000, 57344, MADV_DONTNEED) = 0
madvise(0x3a38dab69000, 57344, MADV_DONTNEED) = 0
madvise(0x3a38dab69000, 57344, MADV_DONTNEED) = 0
madvise(0x3a38dab8b000, 49152, MADV_DONTNEED) = 0
madvise(0x3a38d9095000, 8994816, MADV_DONTNEED) = 0
access("/usr/local/share/fonts/Japanese/tfont/TGothic-GT01.ttc", R_OK) = 0
madvise(0x3a38dab8b000, 385024, MADV_DONTNEED) = 0
madvise(0x3a38dab69000, 57344, MADV_DONTNEED) = 0
madvise(0x3a38dab69000, 57344, MADV_DONTNEED) = 0
madvise(0x3a38dab69000, 57344, MADV_DONTNEED) = 0
madvise(0x3a38dab8b000, 49152, MADV_DONTNEED) = 0
madvise(0x3a38d9095000, 8994816, MADV_DONTNEED) = 0
access("/usr/local/share/fonts/Japanese/UchidaAkira/OradanoGSRR.ttf", R_OK) = 0
madvise(0x3a38dab8b000, 385024, MADV_DONTNEED) = 0

という感じで、フォントを片っ端から読んでいて、ものすごく時間がかかっている感じだ。 ちなみに、これはChromiumだと起動時に発生するため、フォント数が増えるとChromiumは起動が遅くなるのだが、Vivaldiではコンテキストメニューの表示がすさまじく遅くなる。 また、この問題はフォームをクリックしたときに発生するため、非常にストレスになっている。

もうひとつはページをロードしたときにロードの途中で異様に時間がかかる。 これはstraceを見ても止まるわけではないのだが、特徴的な要素は見つからず、コンソールで見るとただ単に飛んでいる。 これはChromiumでも発生するため、恐らくBlinkの問題だ。

Vivladi固有の問題のほうは時間があるときに報告するつもりだ。

現状では「Firefoxを使う」というのが解決策になりそうだ。 Chromiumは最近とにかく重くなっている。CPUの使い方がえぐいし、異様に処理に時間がかかるようになった。 フリーズしている、と感じるような処理時間で、原因がよくわからないケースが多い。 これがバージョン71から発生していることは認識しているのだが、そもそも「ウェブレンダリングを難しくしすぎている」というのが問題で、 「なんでもかんでもウェブでやろうとするのやめてくれないかなぁ」と心底思う。

だいたいこの流れや、レンダリングエンジンの複雑さがGoogleの胸先三寸になってしまっていることにはとても気持ち悪さを感じている。

Radeon RX580はどうだったか

なお、RTX2000シリーズは通常利用での暴走や出火などが問題視されていることからあまり完成度は高くないと見られており、 AMDは14nmプロセスでNvidiaより圧倒的に性能が低く、それでありながらライバルと位置づけるNvidia製品よりもずっと高い、という状況である。 しかもTSMCは7nmプロセスの新しいカードを用意しており、Radeon VIIという新製品は「RTX2080相当の性能で、価格的にも同等。Vega RX 64比では性能は1.8倍」とのたまっている。 TSMC、及びAMDは7nmで攻勢をかけるつもりのようで、とにかく時期が悪いのは確かだ。

だから、今買うならGTX1080かRTX2070あたりが妥当な判断で、RX580が4万円程度はするという点を考えると普通の(Windowsの、ゲーマーの)ユーザーには「迷わず買う人以外は買わないほうがいい」レベルの製品だろうとは思う。

Linuxにおける性能は、おおよそWindowsにおけるGeForceとの関係と同等。 つまり、NvidiaがWindowsと比べて半分程度の性能しか出せていない以上、AMDのドライバもWindows版と比べて半分程度、と考えれば良いのではないだろうか。 以前はもっともっと差が開いていたのでだいぶ健闘しているといって良さそうだ。現在はNvidiaのプロプライエタリドライバとAMDのオープンソースドライバ(AMDGPU)は同等の出来ということになる。 性能面では、だが

ゲーミング部分で言うと、ドライバの出来と、ゲームにおける最適化の結果として圧倒的にGeForce寄りであり、Radeonはその力を発揮できないという状態が続いている。 だから、ゲーム用途では何も考えずにGeForceでいいというレベルだし、多分それはRadeon VIIが出ても変わらない。

一方、Linuxのワークステーションユースで不満が出るという気はしなかった。 vdpauinfoでは

display: :0   screen: 0
API version: 1
Information string: G3DVL VDPAU Driver Shared Library version 1.0

Video surface:

name   width height types
-------------------------------------------
420    16384 16384  NV12 YV12 
422    16384 16384  UYVY YUYV 
444    16384 16384  Y8U8V8A8 V8U8Y8A8 

Decoder capabilities:

name                        level macbs width height
----------------------------------------------------
MPEG1                          --- not supported ---
MPEG2_SIMPLE                    3 65536  4096  4096
MPEG2_MAIN                      3 65536  4096  4096
H264_BASELINE                  52 65536  4096  4096
H264_MAIN                      52 65536  4096  4096
H264_HIGH                      52 65536  4096  4096
VC1_SIMPLE                      1 65536  4096  4096
VC1_MAIN                        2 65536  4096  4096
VC1_ADVANCED                    4 65536  4096  4096
MPEG4_PART2_SP                  3 65536  4096  4096
MPEG4_PART2_ASP                 5 65536  4096  4096
DIVX4_QMOBILE                  --- not supported ---
DIVX4_MOBILE                   --- not supported ---
DIVX4_HOME_THEATER             --- not supported ---
DIVX4_HD_1080P                 --- not supported ---
DIVX5_QMOBILE                  --- not supported ---
DIVX5_MOBILE                   --- not supported ---
DIVX5_HOME_THEATER             --- not supported ---
DIVX5_HD_1080P                 --- not supported ---
H264_CONSTRAINED_BASELINE       0 65536  4096  4096
H264_EXTENDED                  --- not supported ---
H264_PROGRESSIVE_HIGH          --- not supported ---
H264_CONSTRAINED_HIGH          --- not supported ---
H264_HIGH_444_PREDICTIVE       --- not supported ---
HEVC_MAIN                      186 65536  4096  4096
HEVC_MAIN_10                   186 65536  4096  4096
HEVC_MAIN_STILL                --- not supported ---
HEVC_MAIN_12                   --- not supported ---
HEVC_MAIN_444                  --- not supported ---

Output surface:

name              width height nat types
----------------------------------------------------
B8G8R8A8         16384 16384    y  NV12 YV12 UYVY YUYV Y8U8V8A8 V8U8Y8A8 A8I8 I8A8 
R8G8B8A8         16384 16384    y  NV12 YV12 UYVY YUYV Y8U8V8A8 V8U8Y8A8 A8I8 I8A8 
R10G10B10A2      16384 16384    y  NV12 YV12 UYVY YUYV Y8U8V8A8 V8U8Y8A8 A8I8 I8A8 
B10G10R10A2      16384 16384    y  NV12 YV12 UYVY YUYV Y8U8V8A8 V8U8Y8A8 A8I8 I8A8 

Bitmap surface:

name              width height
------------------------------
B8G8R8A8         16384 16384
R8G8B8A8         16384 16384
R10G10B10A2      16384 16384
B10G10R10A2      16384 16384
A8               16384 16384

Video mixer:

feature name                    sup
------------------------------------
DEINTERLACE_TEMPORAL             y
DEINTERLACE_TEMPORAL_SPATIAL     -
INVERSE_TELECINE                 -
NOISE_REDUCTION                  y
SHARPNESS                        y
LUMA_KEY                         y
HIGH QUALITY SCALING - L1        y
HIGH QUALITY SCALING - L2        -
HIGH QUALITY SCALING - L3        -
HIGH QUALITY SCALING - L4        -
HIGH QUALITY SCALING - L5        -
HIGH QUALITY SCALING - L6        -
HIGH QUALITY SCALING - L7        -
HIGH QUALITY SCALING - L8        -
HIGH QUALITY SCALING - L9        -

parameter name                  sup      min      max
-----------------------------------------------------
VIDEO_SURFACE_WIDTH              y        48     4096
VIDEO_SURFACE_HEIGHT             y        48     4096
CHROMA_TYPE                      y  
LAYERS                           y         0        4

attribute name                  sup      min      max
-----------------------------------------------------
BACKGROUND_COLOR                 y  
CSC_MATRIX                       y  
NOISE_REDUCTION_LEVEL            y      0.00     1.00
SHARPNESS_LEVEL                  y     -1.00     1.00
LUMA_KEY_MIN_LUMA                y  
LUMA_KEY_MAX_LUMA                y  

また、vainfoは

vainfo: VA-API version: 1.3 (libva 2.3.0)
vainfo: Driver version: Mesa Gallium driver 18.3.1 for Radeon RX 580 Series (POLARIS10, DRM 3.27.0, 4.20.1-1-MANJARO, LLVM 7.0.0)
vainfo: Supported profile and entrypoints
      VAProfileMPEG2Simple            : VAEntrypointVLD
      VAProfileMPEG2Main              : VAEntrypointVLD
      VAProfileVC1Simple              : VAEntrypointVLD
      VAProfileVC1Main                : VAEntrypointVLD
      VAProfileVC1Advanced            : VAEntrypointVLD
      VAProfileH264ConstrainedBaseline: VAEntrypointVLD
      VAProfileH264ConstrainedBaseline: VAEntrypointEncSlice
      VAProfileH264Main               : VAEntrypointVLD
      VAProfileH264Main               : VAEntrypointEncSlice
      VAProfileH264High               : VAEntrypointVLD
      VAProfileH264High               : VAEntrypointEncSlice
      VAProfileHEVCMain               : VAEntrypointVLD
      VAProfileHEVCMain               : VAEntrypointEncSlice
      VAProfileHEVCMain10             : VAEntrypointVLD
      VAProfileJPEGBaseline           : VAEntrypointVLD
      VAProfileNone                   : VAEntrypointVideoProc

と当初対応しておらず、重大な弱点であった「H.265の非対応」は解消されているようだ。 (ただし、VP8/9には対応していない)

実際に試してみたところ、hevc_vaapiでのエンコードは116fpsと、P400でのNVENCには劣るもののかなりの優秀さを見せた。これだけ速ければ十分に実用的だろう。 画質がNVENCだと実用には厳しいところがあったのだが、VCEの場合いくらか画質がいいためより実用しやすい。 それでも、利用できる場合は「普通は画質を少しでも良く保存したい」ために「速さ優先」とはなりづらく限定的だが。

さらにNvidiaと違い、Waylandのコンポジターがちゃんと動作するというのも大きなメリットだろう。

消費電力的にも、あまり電気を使わないプロセッサを使っているからという事情が前提になってしまってはいるものの、使いづらくなるほどではない。 気持ちとしては「もっと消費電力が少なかったらなぁ」というのと、「2万円台前半だったらなぁ」という気持ちはとてもしてしまうのだが、そうした点を除けば「入れておけばとりあえず安心できるカード」であると言えるだろう。 「とりあえず入れる」には巨大すぎて入れるケースを随分選ぶが。

DVI-Dがひとつ、HDMIとDPがふたつずつで計5出力というところも、使いやすく不満になりにくい。 このビデオカードは大きさ的にも電力的にもそう来やすく選べるものではないが、Linuxでのグラフィックにまつわる不満を感じているのなら、RX580と言わずとも(例えば560でも)Radeonにする価値はありそうだ。

将来的には

「ゲームをする事情」と「デスクトップユースとして普通のアプリを動かす事情」は似ているのだが、「計算力を求める事情」と「Linuxにおいて快適な仕様」とは大きな違いが出てきている。

だから、贅沢を言えば2種類の仕様を用意できると最善で、贅沢を言えば、 「Ryzen + GeForce (主にWindows及びゲーム向け)」と「Ryzen Threadripper + Radeon (Linux及び計算向け)」の2台で編成できたらなぁ、と思う。 Zen2でこれを構成するのはなかなかドリーミーだ。

とはいえ、現実的に1台に絞るとしたら、「Ryzen + Radeon」という構成で「現実的な計算力とLinuxで扱いやすいグラフィック」というのを取るだろう。

現状、P720はどうなのかというと、やはり4114に求めたものと諦めていたものというのが大きい。

「メニーコア」という性能は、それを発揮する場面というのは本当に少ない。 実際、私の普段の運用では4114ですら持て余し気味である。 ところが、普段遣いではあらゆるアプリで処理性能が足りずストレスフルである。 「普通の使い方でモバイルi3程度の快適性しか求められない」というのはわかっていたが、それでもこのマシンのクラスを考えるととても残念な感じがする。

対して、実際にメニーコアを求めるケースでは4114では全然足りないのだが、それでも処理性能はi7とは段違いであった。 「4114はフルコアを活用できたとしてもi7に劣る」と考えていたが、実際は第8世代Core i7よりもずっと高速に動作した。 シングルコア性能も諦めていたほどに低くはなく、「さすがにそれなりに性能はある」と感じることができる。 そもそも、20コア40スレッドであるから、「20スレッドスケールテスト」が手元でできるというのもかなりのメリットだった。 だから、ちゃんと求めたものは実現できているし、価格を考えれば完全に目的を達成できるほどではないというのはわかっていた。

だが、結局のところ現状P720の最大の魅力は「省電力」である。 48Wから90W程度でありながら非常に優れた処理性能を発揮する、というのはかなり魅力的だ。

だが、やっぱりコンピュータは「性能が足りるか否か」であり、エクスペリエンスが良いかと聞かれるとYESとは答えがたい。 部分的にボトルネックになるブラウジングが全体の作業の足を引っ張るという状況も痛い。

「エクスペリエンスは諦めて処理量を増やしたい」というのとは矛盾しているのだが、やはりエクスペリエンスを優先したい気持ちが湧いてくる。 今回RX580にしたことで飛躍的にエクスペリエンスが向上したことも大きな理由になっている。 というより、RX580にしたことでP720がとりあえず不満ないくらいの動作をしてくれるようになった。 だから、バランス的には今の状態は悪くないのかもしれない。

Erina/Surtrに関する研究が一区切りついて、あまりデータプロセッシングすることがなくなったというのも大きい。 圧倒的に重い処理であったことから、これのためにメニーコアを求めたし、それによって成果も挙げたのだが、これがなくなると相当頻繁に回されるタスクを考えても実際8コアもあればあまり支障はなく、16コアのRyzenが用意されるのであれば不満はなさそうに思える。

ただ、ゲームをすることに関していえばGeForce優位は揺るぎそうにもないし、かといってLinuxでは現状NvidiaもIntelもなかなかの惨状ということを考えればRadeon以外選択肢はないという状態であり、 これが2台構成を求める理由ともなっているが、1台を選ぶのであればLinuxエクスペリエンスを優先することになるだろう。

ただ、Zen2の世代ではまだP720は現役で、しかも「非常に高機能で並列処理能力は高いが、通常の使い方ではそれほど性能は高くなく、消費電力はやや少なめで、筐体はとても大きい」マシンをメイン以外で使うというのは非常に難しい。 今Godavariがそうであるように、「主にWindows環境(音楽制作)、必要なときは計算資源または分離された帯域としてのLinux」みたいな使い方になるだろうか。 実際、音楽制作では機材がIntelプロセッサを想定したドライバでAMDドライバだといまいちうまく動かないケースもあり、現在のP720のような構成は非常に好ましい。 多くのVSTプラグインを使う場合も4114はかなり良い働きをしてくれる。 それでも、なんとも微妙な話で、Zen2世代でメイン機の差し替えを行うかは悩ましいところではある。

やはり「それぞれの用途で最適化した理想の2台」なんて夢見がちな話をするに留めるとして、多分本当に多くの人にとって有益なのは現時点で性能的にやや微妙な立ち位置にあるRyzen Gが不満のない性能まで引き上げられること、 あるいはRX 580相当のRadeonがもっと安く、省電力でコンパクトなものとして出てくることだろう。それは、次の世代で実現するかもしれないし、Zen3の世代まで待たねばならないかもしれない。

一方、X11はいよいよ限界を迎えている感もあるが、一方でWaylandがよくなっているわけでもない。 IntelもNvidiaも不具合が出る状態が続けばかなり苦しいことになるだろう。


  1. 具体的にはFlower Knight Girlである。 なお、 18禁バージョンもあるため 検索される際にはくれぐれも留意願いたい。

AIとディープラーニングと現実

本当に流行ってる

最近、本当にAIとディープラーニングが流行っている。 割となんでもかんでもAIだディープラーニングだと言っている気がする。

両者をイコールでつないでいる人も多いと思うのだけど、別にそんなことはない。

AIというのは昔からあるものだし、ディープラーニングはどちらかと言うと以前流行っていた「ビッグデータ」という文脈にあって、これらがクロスオーバーする関係にあるのは事実だけれども、 ディープラーニング相当の処理というのはデータサイエンス的にAIを使わなくても意味を発生するものだし、別にAIはディープラーニングを用いなくても成り立つ。

おもしろいのは、この手のテクノロジーの流行り言葉というのは実態がないものになりがちで、「クラウド」なんかも、なんどもかんでもクラウドと呼んで何がクラウドなのか全くわからない状態になった。 AIに関してはやはり同様の状況なのだけど、ディープラーニングに関しては実際にディープラーニングしてることがほとんどだし、ディープラーニングが流行っているおかげでAIもディープラーニング型AIで実態があることが多い。

ディープラーニングAIの「使えなさ」

だが、実のところ「ディープラーニングAI」そのものは全然むずかしくない。 なにせ、「データ」と「ソフトウェア」があればよろしいわけで、今現在両方とも調達はあまり難しくないからだ。

だが、それが実用的かというとものすごく疑問がある。

ぶっちゃけ、「データを一定の基準で分析した」という事実だけでは意味があるものにはならないし、精度も担保されない。 というか、ここが地獄の一丁目だ。だって、「分析して、それに基づいて判断できる」という事実は発生するのだが、「それが正しいか」「精度はどの程度か」をちゃんと測定できるケースは極めて少ない。 結果、「誰にも有益さがわからないソフトウェア」のいっちょ上がりである。

コンピュータのプログラムというのは比較的明確な結論を出せることが多い。 特定条件での速度は計測で出せるし、目的を達成できるかどうかも明確な二値になることが多い。 ところが、AIの場合それはできないし、比較的シンプルな構造のAIは「設計に沿った動作をする」ことは最低限抑えているものなのだけど、ディープラーニングAIに至っては「データを解析して、それに基づいて振る舞う」ことが目的化してしまっているのでものすごくいい加減なものが出来上がるリスクは非常に高い。

そもそも、「明確な目的があるときにディープラーニングAIは適していない」と私は思っている。 なぜなら、ディープラーニングという手法が、根本的に「統計的に言えば多分こうなんじゃないかなぁ」というものなので、明確な目的があってそこに誘導するということにかけては最悪に近い手法だというのが私の意見だ。

実際、厳しい制約条件を与えれば評価軸が明確になるので間違えにくくなる。 例えばボードゲームのAIとしてディープラーニングという手法が有効であることは私はずっと以前から認めている。 ただし、もっと計算力にものを言わせる手法のほうが近道だとも言い続けているけど。オセロなんか既に完全解を出すことは現実的に可能なのだから。

ディープラーニングも有効な場合は本当に有効(もう、それは圧倒的に)なんだけれども、ブロックチェーンと同じで「最適解になるケースが極めてきわどい」タイプのテクノロジーだと思う。

しかし、ちゃんと測定する方法がない、つまり「その判断や認識が間違いであった場合に明確に自分に責任が降り掛かって痛い目にあう」という事実がないのが致命的で、この問題は精神医学とかでも同様なんだけれど、 「間違ってない」「こうなんだ」と言い張れるがために正しい方向に進んでいかない。

これはもう、

  • 複合語検索のときに関連するページ(探している情報がどうかに関係なく、入力した検索ワードに関する情報が言及されているページ)がどんどん遠くなるGoogle
  • 私が不快に感じてブロックしているタイプのアカウントばかりを執拗にプロモーションするTwitter
  • エロ画像を挙げて「LINEで友達になってね♡」とやっているタイプのスパムアカウント (しかも私は何度も違反報告している) を執拗に推薦するFacebook
  • 親切心で書いているレビューを抹消したり保留したり取り消しまくるAmazon
  • まるで欲しいものを当ててこないJRの自販機

などなど、「考え方が間違っているから突き詰めていくとおかしなことになる」ということは明らかなのだけど、 誰もそれを認めようとしない状態だ。

まぁ、間違っているなんて認めたくないものだもんね。 みんな現実から目をそらしたいんだよ。

Erinaとディープラーニングとデータサイエンティスト

Erinaの開発時は、そもそもErinaの存在自体「データ分析の正しさの程度を可視化するためのツール」であることからしても、 前節での問題を認識していて、それを回避するために自分に厳しくしていたことがわかる。

そう、Erinaはそもそもそれ自体を目的にしていたわけではなく、「データ分析の正しさを証明する、また間違った考えをしていればそれを私に突きつける」ために生まれている。 言ってみればデータ分析におけるデバッグツールである。

でも、実際のところErinaには相当な調整が入っている。 つまり、データ分析によって「正しさ」が確定できないところを「必ずこの場合はこの経路を通らなければならない」という制約を与えているのである。 調整値自体、すさまじく膨大なのだが、これがないとErinaに人間らしさを感じることができない。どれほど巧みに応答しても不自然なのだ。

結局のところ、特定の目的への到達のためだけではなく、曖昧さが許されるケースでもディープラーニングというのは結構低いほどほどのクオリティにとどまってしまう。 データを増やしても、分析を深めてもクオリティ向上とイコールではないのがディープラーニングの難しさだ。

というか、そもそもそこまでの次元を追い求めない限り実用的なレベルで望ましいAIにはならないのが現状だというのが私の意見で、 ディープラーニングで精度を出すというのはものすごく茨の道であるように思う。 コストもかかるし、そもそもそれはディープラーニングでやるべきことなのだろうか。

そして、データ分析というのは「決まりきったやり方に沿ってやれば結果が出る」とかいうものでは全然なくて、ずーーーーっとデータと真剣に向き合ってくることでなんとなくわかってくるもので、 完全に研究者の世界であるし、その分野を十年とか二十年とか関心をもってデータをとって向き合うということをしていないとデータをどうみればいいのかというのはなかなか見えてこない。 これはどんな研究分野でも同じだろうと思う。そんな数年程度で研究成果が出るのなら、世の大学生はもっと毎年革新的な論文を発表してくれているはずだ。

今までデータ研究なんかやっていなかった人がいきなりデータサイエンティストになったりするケースも多いし、やたら若い人がデータサイエンティストだったりするんだけど、 それは結果が出るのは望み薄だなぁ、と思う。稀にはそんなものを覆せるような才能ある人もいるだろうけども。

私だってそもそもErinaにつながるデータ分析の着手までに観察は7年やっているわけで、Erinaが形になるまでにはそこからさらに18年かかっているのだからデータ分析ってそういうものでしょ、という気がする。

異論はあるだろうけれども、少なくとも「不快感がなく、人間がやるよりも精度が出ているディープラーニングAI」という実例がほとんど私の体験にないというのが、今世にあるディープラーニングAIの現実ではないかという気がしてしまうのだが。

実用的AIとは

すっごく馬鹿にされるけど、ちゃんとチューニングすればテーブルAIって大変実用的。 電話の音声ガイダンスですらそれなりに実用的になるのだから、そこからユーザビリティを高めるようにすれば結構使えるようになる。

スコアリングAIは、スコアのつけ方のチューニングが結構大変で、条件管理も大変になるのでものすごく労力がかかる。 ディープラーニングは「そういう人力でやるロジックづくりを自前でやって楽をしよう」という考えなんだろうと思うし、そこはとても正しいと思うのだけれど、今のところそううまくはいっていないのではないか。

また、「有機的補佐を行うAI」としてはディープラーニングAIって割と有効だと思う。 例えばJRの自販機は飲みたいもの、好ましいものを推薦することはほとんどないのだけど(私に限らず、私の周囲でもほとんどない)、だからといって困るわけではない。 そういう付加的要素に使うぶんには外したところでダメージは小さいのでアリだと思うのだ。 だが、Googleのように本質的機能に取り込んだり、Facebookのようにユーザビリティに致命傷を与えたりするのはダメだろう。

あと、推薦する場合は推薦がユーザーにとって本当に望ましかったかを検証することは必須なのだけど、 結構「誤クリックを誘うことで推薦した結果に対するリアクションをさせる」という「ディープラーニングの正しさをかさ増しする」という行為をすごくよく見かけるので、「そのAIに価値がなくて邪魔になっていることを認識してるんじゃないの?」と思ってしまったりもする。

本当に本質をAIに担わせたいのであれば現在の取り組み程度ではダメで、もっと現実と向き合って積み重ねていかないといけない。 実際、私はその末にErinaを生み出しているわけで、それは説得力ある材料と受け取ってもらえるのではないだろうか。

だからAIはどちらかといえば補助的な用途に留めるべきで、補助的な用途はユーザーを補佐することに限り、ユーザーを阻害することをAIに委ねるべきではないと思う。 目的を達成するためであれば条件が明確に限定されたAIを用意すべきで、その場合でも対応しきれない場合はスムーズに確実な方法にパスできるようにするほうが問題は複雑化せず実用的だろう。

メールの通知の新戦略

今まで重要なメールはYMobileのメールアドレスに転送する、という戦略をとっていたのだけれど、 今まで知らなかったのだが、Virtualで(Virtualのみか?)拡張アドレスを含むアドレスのメールを受け取り、外部ホスト(@を含むメールアドレス)に対して転送すると拡張アドレスを含んだまま転送してしまうため、転送先でエラーになる。

そこで、後々の脱YMobileも視野に、Discordで通知するようにした。 もっとも、Discrodはあまり通知で起きてくれないのだけど。

以前、[Discordへの通知方法は記事にしたし]、既にメールから起動するwebhookでの通知というのは結構使っている。 Extract Mailtextは新開発のメールフィルタのコンポーネントで、メールを可読形式にするのはちょっと難しいのだが、ここで作っていた簡単なフィルタが有効に働いてくれた。 テキストフィルタで処理できるように作ったものだけに応用が効いた。

基本的にはこれでいいのだが、注意点が

  • Virtualからはコマンドが起動できない
  • Aliases(local)から起動されたコマンドには$PATHが入っていない
  • Aliasesから起動されるコマンドはnobodyで実行される

Virtualでコマンドが実行できない問題は、Aliasesのほうに専用の(外部には絶対わからないような)名前を作って、Virtualでローカルユーザー(Aliases上のエントリ)にまわして、さらにコマンドに回すのが良い。 拡張アドレスをつけてローカルユーザーに回すようにすれば、Aliasesで直接記述された拡張アドレスを含む場合の専用引数と、含まない場合、もしくは対応していない場合のデフォルトの引数を使うことができて便利。

「ZshからRubyにしたら速くなる」 その理由とテクニック

現在取り組んでいるプロジェクトで、パフォーマンスチューニングの一環として当初Zshで書かれていたスクリプトをRubyで書き直すことによって、60倍程度の高速化を実現した。 もちろん、単純に書き換えただけではなく、可能な限りfork/execをしないようにしたり、コストがかかる処理を最小にするなどの工夫を伴って手に入れた結果だが、「ZshでしていたことをRubyに書き換えた」だけでも相当な効果があった。

このパフォーマンスチューニングは単にプログラムを書くだけの人には生まれにくい発想である。 Unix、そしてLinuxのシステムや、プログラミング言語処理系に関する知識がないと考えられない要素が多いのだ。

そこで、この話を解説する。

「ZshよりRubyが速い」そのわけ

根本的な話として、Zshはそもそも遅い処理系だ。 「Zshが遅い」という話はZshのメーリングリストでもちらほら話をされる。 別にBashと比べて遅いということではないのだが(Bashもまた非常に遅い処理系だからだ)、状況によっては速度が問題になる程度に遅い。

Rubyも相当に遅い処理系であると言われていたし、実際かなり遅かったのは事実だ。 それでもZshに比べれば随分早かったのだが。

だが、それ以降、Rubyは高速化に取り組み続けている。対して、Zshはあまり高速化には取り組んでいない。だから、差が開いている。

しかし、理由がそれだけというわけではない。

Zshは純粋なインタープリタである。対して、Rubyはスクリプト言語ではあるがバイトコードインタプリタ型である。 この違いは、syntax errorが起きるタイミングが、Rubyがスクリプトを実行しようとしたタイミングであるのに対し、Zshはその行に到達したときであることからもわかる。

インタープリタ型であれコンパイラ型であれ、ソースコードを機械語に変換しなければならない、という点は変わらない。 その違いは方法とタイミングである。

インタープリタ型言語の場合、「1行ずつ(1コマンドずつ)変換する」のである。 その変換方法はもちろん処理系によって異なるのだが、Zshの場合、complex commandでも複数の文をまとめて変換することはしないし、ループによって繰り返される場合でも一度変換したものを使いまわしたりはしない。

対してRubyは、最初にコード全体を構文木に変換する。 RUby 1.8までは構文木インタープリタによってこれを実行していたが、Ruby 1.9以降はこれをさらにバイトコードに変換し、バイトコードインタープリタ(VM)によって実行するようになった。 バイトコードはRuby専用の機械語のようなもので、VMによって非常に小さなコストで実行できる。 Ruby 2.6からはJITコンパイラも追加され、部分的にCコードを生成し、これをネイティブコンパイラ(例えばgcc)によってバイナリコードに変換する(こともできる)。

これで1行だけのようなコードだとあまり差は出ないし、Zshでは1行だけどRubyでは何十行という可能性もあるので、このようなケースではRuby有利というわけではなくなる。 だが、ループで何度も同じコードを実行するような場合には非常に大きな差になってくる。 今回の場合、テスト段階で500回のループであったことから、大きな差になったということである。 だからループ回数が増えると倍率的にも速度差はさらに開く。

fork/execとコンパイルにかかる時間

Unix関連に少し知識がある人であれば、「forkはコストが重く遅い」というのを聞いたことがあると思う。

だが、この認識にはちょっと注意が必要だ。 というのも、C言語の速度から見た時に「forkする時間があればどれだけ実行できるか」という点を考えるとsystemで外部コマンドを呼び出すとそこだけ局所的に時間がかかる、という状況が発生する。

だが、実際にはfork(2)しても1000分の数秒にすぎない。 どちらかといえばそれよりもexec(2)のほうが重いのだが、それでもせいぜい100分の1秒程度だ。 だから、C言語で書いている場合ですらそれなりに長くなる場合はむしろ実行コストを省略できてコマンドを呼び出すほうが速かったりする。

昔のUnixではfork(2)はもっともっと遅かった。 現在のLinuxにおいてfork(2)が速くなったのはコピーオンライト形式であることの恩恵が大きい。 古典的なUnixではfork(2)は呼び出した時点でプロセスのメモリをコピーしていた。直後にexec(2)する場合はコピーしたメモリの内容は全く使わないのでかなりの無駄だ。

ところが、現在のLinuxにおいてはfork(2)によってメモリはコピーされない。共有されるのである。 そしてforkされたプロセスが共有されているメモリに対して書き込みを行った時に別に領域を確保してそれを変更する仕組みだ。

結果的にfork自体は一瞬に近くなっている。

そして、もうひとつ重要なのが「コンパイル時間」だ。 Rubyは起動時に対象スクリプトの変換を行う。 だが、この変換コストは速くなるに従って増加している。以前は構文木に変換するだけだったのが、1.9からはさらにバイトコードに変換する時間が必要になったし、2.6でJITを使うとさらにCコードを生成してそれをコンパイルする時間まで必要になっている。 つまり、Rubyはだんだん「実行は速くなっているが、実行に着手するまでは時間がかかるようになっている」のである。

これは、例えばechoであれば

% time /bin/echo > /dev/null
/bin/echo > /dev/null  0.00s user 0.00s system 79% cpu 0.001 total

ということになるのだが、Rubyだと空っぽに近くても

% time ruby -e 'nil'         
ruby -e 'nil'  0.04s user 0.02s system 59% cpu 0.089 total

結構時間がかかる。 つまり、一瞬で実行が終わるRubyスクリプトを何度も何度も繰り返して呼び出すと、トータルではかなり時間がかかるわけだ。 もともとのスクリプトは本体はRubyで、呼び出しがZshだったので、20並列で各500回、Rubyによるコンパイルがかかっていた。だから、かなりの時間がかかっていたのだ。

だが、「Linuxのforkはメモリが共有され、ほとんど一瞬で終わる」という点を利用すると改善の余地がある。 それは、実行可能なRubyスクリプトをライブラリ化する、という方法だ。

ZshからRubyを呼び出す場合、どうしてもRubyを呼び出すたびにRubyによるコンパイルをかけざるをえない。 当初は10000回コンパイルされていたのだが、500回のループをZshではなくRubyで行うようにすれば20回で済むようになる。だが、それでも20回のコンパイルが必要だ。

しかし、呼び出すスクリプト自体をRubyに変えてしまえば、実行しようとするスクリプトをライブラリとしてロードするという方法がとれるようになる。 ライブラリとしてロードすると、そのコンパイルは呼び出し元スクリプトをロードしたときに行われる。 もちろん、呼び出しの目的は呼び出すだけであり、直接そのライブラリの機能を使うわけではない。だが、この状態からforkすると、「コンパイル済みコードがメモリ上にあるRubyプロセス」が出来上がる。

この時点でスクリプトを実行する方法は「メソッドを呼び出す」(あるいは、その機能を果たすオブジェクトを作ってメソッドを呼び出す)だけである。 繰り返し呼び出すループを書くのも、単にRubyのループを書いて、そこで繰り返しメソッドを呼び出すなりオブジェクトを作るなりすれば良い。 呼び出し元スクリプト側では並列分だけforkしたあと、Process.waitallでもしていればいいわけだ。

これはZshに対して、「Rubyスクリプトのコンパイルが1度だけでいい」「execする必要がない」というメリットをもたらしている。 どちらも結構コストの高い処理であるから、繰り返し実行する場合は非常に大きなコストになり速度を低下させる。「処理自体は軽いのだが果てしなくループする」タイプのスクリプトに対してこの方法は本当に効く。 なぜならば、そのようなスクリプトに対してはコストの高い呼び出しをしているとコストのほとんどは呼び出しで占められ、実行コストは小さいためにスクリプト自体を高速化しようとがんばったところでほとんど無意味だし、逆に呼び出しコストを軽くすると劇的に速くなるからだ。

systemd-nspawnでManjaro LinuxからArch Linuxする

systemd nspawn

なんかSystemdを毛嫌いする人が多いのだけど、結構使いやすくて便利な機能が多いので、私は好きだ。Xサーバーみたいだと思う。

systemd-nspawnはcrgoupsを利用したLinuxコンテナである。 LXCよりもずっとシンプルな実装で、かつ簡単に使うことができる。 全然知られていないけど。

とりあえずArchする

Manjaroにもarch-install-scriptsがあるためpacstrapできたりする。 なので、初回インストール前には

# pacman -S arch-install-scripts

とやっておく。

そしたらコンテナを作る。 こちらは新規コンテナを作るときにやる作業。

# pacstrap -i -c ~/Container base base-devel

Arch Linuxをインストールしたことのある人ならわかると思うのだけど、 最低限baseがないと機能しない。そして、AURを利用する場合はbase-develも含んでおかないといけない。

あとはログインする。 これは、起動時に実行するもの。

# systemd-nspawn -b -D ~/Container

これでコンテナを起動することができる。終了する場合、コンテナ上で電源を切る。 終了は少し時間がかかる。

# poweroff

LXCと違って自動的にブリッジインターフェイスもセットアップされ、起動しただけでネットワークがちゃんと機能する状態になる。 LXCと比べても随分と話が早い。

ManjaroだとArchを構築するのは簡単だし、もちろんArchやAnterogosでも簡単だけど、そうでないディストリビューションがどうやってコンテナ上にOSをインストールするか、については各々調べて欲しい。 私はArchが使えれば満足なので、コンテナ上で他のディストリビューションを使おうとは思わないし、現状、Manjaro以外をホストにしようとも思わないからだ。

おまけ。この環境からPowerlevel9kするために

ちょっとつまずいたのでおまけ。 標準ロケールがen_US.utf8になっているため、Powerlevel9kのPowerline記号が出ない。

そこで、/etc/locale.genを編集してja_JP.UTF-8 UTF-8をアンコメントし、

# locale-gen

そして、/etc/locale.confを編集してLANGja_JP.UTF-8にしてから

# localectl set-locale LANG=ja_JP.UTF-8

これでちゃんとPowerlevel9kのアイコンが出るようになる。

ループ回数の多い処理でのログ出力

概要

現状、1処理が0.0012秒程度で終わるシステムがある。1回の処理は非常に短いが、これが呼ばれる回数は1処理あたり数百万回にも及ぶ。 速度がそれなりに求められる案件だ。

しかし、その中で何度かログファイルに書き出したいタイミングというのがある。それぞれのログファイルに書き出すタイミングは呼び出しの中では1度だけだ。 だが、「処理時間が短くて」「回数が多い」のに共有のログファイルに書くというのはなかなか鬼門だ。 ファイルに書いている時間に待ち合わせがあると、速度に対してものすごくロスが発生する。

まずは普通に

普通に共有されているログファイルをロックする方針でやってみる。

ログファイルにアクセスできるのは1プロセスだけなので、同時にログに書こうとすると待たされることになる。 同時に書くとファイルが壊れてしまうので、すごく妥当な方法なのだが…

% time ruby test-logging1.rb
ruby test-logging1.rb  20.24s user 676.50s system 1102% cpu 1:03.18 total

1分3秒ということでかなり長い。

ソケットで投げてみる

UNIXドメインソケットにして投げつけてみる。 サーバー側が受け取った後の処理はクライアント側の処理時間には影響しないので、サーバーは投げ捨てる。

クライアントは直接ファイルに書くのではなくUNIXドメインソケットを使う。

待ち合わせは全く発生しないわけではない(ソケットを受付てからスレッドに移るまでの間はブロックされる)が、速くなるはずだと思うのだが。

% time ruby test-logging2.rb
ruby test-logging2.rb  25.51s user 33.05s system 62% cpu 1:33.02 total

1分33秒。却って遅くなってしまった。 やはりソケットに接続するコストが高いようだ。

ただ、並列数としては今回の場合最大24で、繰り返しの間というのは別にソケットをつなぎ直す必要はない設計になっている。 そのため、ループの外側に接続を置くことができる。

1行入れ替えただけだが、効果てきめん。

% time ruby test-logging2.rb
ruby test-logging2.rb  2.91s user 2.63s system 1488% cpu 0.373 total

今度は0.3秒で終わった。 Linuxの場合ソケットへの書き込みはカーネルがバッファするため、この程度の量であればメモリへの書き込みだけで終了する。そのため、かなり高速である。

ちなみに、ファイルの場合共有されているファイルをみんなが開いていたらぐちゃぐちゃになってしまうのでこの方法は使えない。 また、親子関係にある別プロセスについてはファイルディスクリプタ自体は共有されているため、fd番号さえわかっていれば共有できるのだが、 その場合は同じ経路から出ていくことになり、ファイルを共有した場合と同じ問題に直面するため、バグになってしまう。

PIDを使う

私の新しいアイディアは、「ログファイルにPIDを含める」という方法だ。

プロセス自体がマルチスレッドになっていなければ、その瞬間には同一PIDのプロセスはそのプロセスしかいないため、そもそもロックする必要がない。 ファイルのロックはあくまで「同時に」書かれるから必要になるのであって、どれだけ速くても順次であれば何も問題はないのだ。

ファイル名にPIDを含めているため、同時に走っている別のプロセスが同じファイルにアクセスすることはない。 もちろん、これは並列化の方法にプロセスを選択している場合だけだが。

% time ruby test-logging3.rb
ruby test-logging3.rb  12.26s user 22.37s system 1778% cpu 1.948 total

なかなか速い。だが、ソケットとはだいぶ差があるようだ。 しかし考えてみればこれもプロセスをオープンしてからであればなにもループのたびに開き直す必要はない。ソケットと同じ話だ。 そこで、ソケットと同じく順序を入れ替えてみる。

ループのたびにファイルを開くのではなく、予めファイルを開いてループで書いていくようにした。

% time ruby test-logging3.rb
ruby test-logging3.rb  2.52s user 0.13s system 1356% cpu 0.196 total

ファイルとソケットのオープンコストの差か、ソケットよりも速くなった。 ちなみに、ソケットを一回だけ開く方法よりも速い。

「forkでの並列処理に追記のログファイルはファイル名にPIDで」、このテクニック、私はどこかで見た記憶はないのだが、かなり有用だと思う。お勧めだ。

素晴らしきRinda

Rindaって

Java生まれのTuppleSpaceのRuby実装である。

Javaの実装はLindaというので、RubyだからRindaということらしい。

Rindaは基本的に分散Ruby(dRuby)のデモンストレーションのようなライブラリのようだが、今(2.5.2)に至るまでずっと標準添付され続けている。

dRubyすらあまり使ったことのある人がいない中、dRubyはPStoreやYAMLStoreなどと同様、 「知っている人は少ないが使ったことのある人はほとんどいない」標準ライブラリになっている。

TuppleSpaceって

TuppleSpaceは共有して置いておけるプールである。

「ホワイトボードシステム」と呼ばれることが多いのでホワイトボードになぞらえるが、このホワイトボードにマグネットシートを貼ったり取ったりできる。 ボードから取ってくるマグネットシートは種類による選別が可能だ。

一般的なソケットと違い、違う種類のやり取りをひとつの経路に簡単に混ぜることができる。

TCPサーバーとして起動できるので、ネットワーク分散も可能だ。

Rindaの概要

Rindaに対して置くことができるのは配列またはハッシュなのだが、「使い勝手はハッシュだが普通は配列」というのが私が感じている空気である。

例えば次のようなオブジェクトを配置する。

このメソッドはTuppleSpaceに接続し、この値を入力する間はブロックするが、受け取り手がいようがいまいが、すぐに終わる。 とにかく一旦TuppleSpaceに置かれるのだ。

そして、別のプロセスが次のようにして受け取る。

nilはワイルドカードになるので、valの値がなんだったかに関係なく受け取られる。 ただし、さらに他のプロセスが

のようにしていたら、この値は受け取らない。[0]に明に指定された値と異なるからだ。

全体リソースの中での曖昧さ

サーバーでは無限にコネクションが維持できるわけではないし、無限にリソースがあるわけでもない。 そのため、処理できるものからどんどん処理していきたい、と考えるのだが、Rindaはこれによって「要請」と「結果」をプールしておくことができる。

例えば、次の例では検索クエリを受け取って、検索を行った結果をTuppleSpaceに置く。 クエリがないときはブロックする。

検索を要請する側がidを発行しておくことで、一意に応答することができる。 この処理を行うプロセスの数によって「並列で検索できるワーカーの数」を制御することができる。 余っているワーカーがあったとしてもそれはRinda::TuppleSpaceProxy#takeによってブロックされるだけであり、「同時最大並列数」だけプロセスを起動しておけば良い。

さらにこれを取っていくワーカーは別のホストでも構わないのだ。

TuppleSpaceによって簡単に分散処理、並列処理が可能なのだが、 これは「入れた順番には出てこない」。処理が大量にたまってしまった場合でも順番を守って処理してほしい場合には適さない。

だが、「手が空いているならば仕事を持ってきて処理する」という形式はなんとも人間的で、かつコンピュータ的にも結構優れたモデルだと思うのだ。 Rindaが有効に機能するように設計することにより、並列処理に伴う難しさをかなり軽減することができる。 そもそも並列処理は厳密さを求めるにはあまり向いていないので、このようなふわっとしたレイヤーをはさむと結構いい感じに動作する。

in action

今回は「ワーカーが処理するデータはそのワーカー単独が処理するディレクトリに分割し、ワーカーは処理が終了したらRinda経由でディレクトリパスを取得する」という方法をとった。

以前はHTMLチャットサーバーの実装で、要求に対して即座にリターンさせるためにRindaを使っていたこともある。

あまり知られておらず、使われる機会もないが、使ってみると結構面白いのではなかろうか。