2020年の目標

ゲームをする

2019年は全然ゲームをしていなかった。Twitterとかで無為に時間を溶かすくらいならゲームをしような。というか2018年の振り返りでPrismataとStarCraftをやりたいって書いてたのにどっちもやってないやんけ……。この2作品もそうとして、今の所ハナノパズルとライザのアトリエが積まれている。

太らない

太らない。

毎月なんらかの新しい技術に触れて知見をまとめる

なんとなく知識をリフレッシュしよう〜くらいの気持ちだと具体性がなくて全然進まないので、毎月なんかしら新しいことをやってアウトプットできるようにする。

中国語を勉強する

中国旅行に行って、漢字は読めるんだし中国語が最低でも読み書きできるといいなあと思った。話せるともっといいけど練習方法がよくわからないので今のところは保留しておく。とりあえずDuolingoを始めた。

毎月目標達成度を振り返る

Twitterで何人か定期的に一年の目標達成度を振り返っている人がいて、モチベーションの維持に良さそうだと思った。

2019年振り返り

人生3万日しかない。今年は有意義だったか?

目標だったもの osak.hatenablog.jp

アメリカで太らない

年始は145lbs(65.7kg)だったが、3月に引っ越したついでにFitbitの体重計を買ってダイエットすることにした。といってもゆるふわで、食べる量を減らしたり、炭水化物を意識的に避けたりするくらい。結果、62kgくらいになった。

ボイロ動画を作る

なんもやってない。内輪でちょっと流行ってたのでTwitch実況はした。

個人用途でWebインフラを作って管理する

ほぼ何もやってない……。年末になって、余ってたThinkpad自宅サーバを立てて、Webで見つけた画像やらなんやらをクロールしてくるシステムを作り始めた。EC2とかじゃないのは、Google Photosがポルノ画像を勝手に削除したとか、勝手にアカウントごと凍結したとかいう話があり、いまいちクラウドが信用できないため……。今後は死活監視したり、集めた画像のビューワを作ったりする予定。

旅行に行く(3回以上)

  • 日本(6月・9月) 6月はICFPCのために行って、少し働いたのと友達と遊んだくらい。9月は妹の結婚式に行って、ついでに温泉旅行した。
  • 中国(上海・蘇州・北京)(7月) 6月末にせっかく日本に行くんだからと、そのまま中国旅行をした。
  • ヒューストン(7月) グリーンカードを取るために出生証明書が必要になり、領事館で戸籍謄本を翻訳してもらうついでに観光した。
  • サンアントニオ(12月) グリーンカードの面接ついでに日帰り観光した。
  • ダラス(12月) 車でどっか行きたくなったので、手頃かつ行ってない都市に行ってみた。

なんかのついでに旅行するケースが多かったけど、おおむね良いのでは。飛行機で行くようなところにもっと行きたい。

ICFPCで1位を取る

無理でした。そもそも総合7位とあまり振るわなかった。どう考えても自分の自動化が甘すぎたのが敗因の一つなのでつらい……。今年のICFPCは問題は面白かっただけに残念。

本を読む / 映画を見る / ゲームをする

本:いろいろ読んだ。一番ためになったのはDesigning Data-Intensive Applications。印象に残ったのはまちカドまぞくとScheme手習い。あと色々な漫画が最終回を迎えた。

映画:5月にAvengers: Endgameをいきなり見て、それから興味を持って過去作を1ヶ月くらいかけて見た。面白かった。

ゲーム:Baba is YouとThe WitnessとBraidをやった。あと日本でやり残していたパンデミック:レガシー2を終わらせた。どれも良いゲームだった。

そのほか

グリーンカードを取得した。正直アメリカにずっと住むかはよく分からないけど、さしあたって日本に戻る理由もあまりないし、会社がサポートしてくれるとのことだったので取れるなら取っとこうという感じ。

総評

かなり仕事にリソースを振ってしまった気がする。新しいプロダクトをすごい勢いで作っていくチームで、仕事としては面白いから不満はないんだけど、自分が直接外に公開できるような成果にはならないので長期的にはあまり良くないなあという気がする。2020年はもっと仕事以外で人生を充実させることをしたい。

ダラス旅行

車でどこか遠くに行きたくなったのでダラスに行った。遠いと言っても片道3時間くらいだけど、1人の短期日程でこれより長距離を走るのもつらいものがあるので……。オースティンから手頃な観光地的な所に行こうとすると、3時間の次は6時間コースになってしまう。

 

オースティンとダラスを結ぶI-35 (Interstate 35)というハイウェイの途中にWacoという都市がある。オースティンから1時間くらいのところにあり、オースティン市内を走っていても案内標識に時折出てくる謎の地名。立ち寄って、TripAdviserで見つけたAlpha Omegaというギリシャ料理店で昼食を食べた。

www.tripadvisor.com

Beef PlatterとGreek Coffee。味は普通。ギリシャコーヒーってなんだよと思ったら、どうやらトルココーヒーと同じものらしい?

 

近くにDr. Pepper Museumなる謎の施設があったので行ってみる。Dr. Pepper好きじゃないんだけど。運営母体はKeurig Dr Pepper社とは関係のないNPOとのこと(Dr Pepper社は毎年多額の寄付をしているらしいが)。そもそもDr. Pepperを作ってるのはコカ・コーラだと思っていた……。

中は普通の資料館みたいになっている。7UPの会社も資本関係にあるため、ミュージアムで一緒に展示されていた。

 

2時間かけてダラスへ向かう。ホテルに行く前に、Twitterで教えてもらったKing Spa Dallasというスーパー銭湯みたいな施設に行ってみる。

http://www.kingspa.com/dallas/

(外観写真を撮った気になってたけどなんもなかった)

入館料$30とかなりお高いが、中は普通のスーパー銭湯のような感じ。でもアメリカで普通の風呂に入れる!すごい!ダラスに住んでたら定期的に行きたい。オースティンからわざわざ行くかと言うと難しい。

共用部に館内着を着たまま入るサウナ的な部屋がいくつかあった。ちょっと試してみたけど、ピラミッド・パワーとか遠赤外線とかをフィーチャリングしていて、まあ客寄せなんだろうなという感じだった。そもそもサウナがあまり好きじゃない……むせるので……。

 

ホテル。クレジットカードのポイントが腐るほど貯まっていたので、中心部にあって便の良さそうなFairmont Hotel Dallasを取った。そしたらスイートルームだった。

ホテルのアメニティはフランス語が書いてあった。日本人がとりあえず英語を置いとくのと同じ感じでおしゃれなのか?

 

 夜は同僚に教えてもらった、Tei-anという蕎麦屋へ。ちょっと早めの年越しそばを食べる(という建前)。

www.tripadvisor.com

おまかせコース$100(ドリンク別)。かなり日本の蕎麦屋っぽくてよかった。そばもちゃんとコシがある。味噌汁は鱈かなんかの身と肝が入っていて感動的にうまかった。刺身も悪くはないが、やっぱり日本のほうがいいかな……。日本酒は梵ゴールドとあとなんかもう一つ飲んで、2つ目のやつが1合$25とかするのに日本酒臭くて悲しくなった。これに限らずアメリカは日本酒が高い。悲しい日本酒を引くのはまあ、冒険するのが悪い……。

締めのそばは好きなものを選べるんだけど、なんか日本人だとバレたのかなんも言わずに日本語のメニューが出てきた。英語が下手だったからなのか、携帯いじってて日本語が見えたからなのか。

 

翌日は昼過ぎに起きて適当にダウンタウンを歩き回る。なんか巨大な目玉のオブジェがあった。ビーム撃ってきそう。

 

目玉オブジェの近くのChop House Burgerというところで昼食を食べる。普通のバーガー。

 

西の方にちょっと歩くとJohn F. Kenedy Memorial Plazaがある。全然知らなかったけど、ケネディが暗殺されたのがここダラスらしい。

photos.app.goo.gl

ケネディが撃たれたところに印が付けてある。ここに立って記念写真を撮ってる人もいた。

中東系っぽい人々が反戦運動っぽいことをしていた。

 

次なる目的地に向かう。最近流行りのLime(モバイルアプリで時間貸ししてる電動スクーター)があって移動はとても便利。

なんかかっこいいビルがあったので適当に写真撮ったらいい感じになった。

AWSがビルの壁に全面広告を出していた。

 

Dallas Museum of Artを見る。アメリカの都市圏はどこに行っても美術館があるな?

展示はあんまり刺さるものがなかった。まあ入場無料だしね……。庭に欠けた星が刺さってるのだけ面白かった。

あと夏にヒューストンに行った時に見たThe Visitorsという映像作品がここでも展示されていて、ちょっと感動した。いい作品がちゃんと評価されてると嬉しい。大画面のスクリーンを9枚使う大掛かりな作品なんだけど、映像作品は簡単に本物を展示できるんだなぁ……。レイアウトもヒューストンと違っていて、作品としてレイアウトそのものにはこだわりがなさそうなのも発見で面白かった。

en.wikipedia.org

 

疲れたのでホテルに戻ってダラダラしてから夕食を食べに行く。昼間にイタリアンの店を見てイタリアンの気持ちになっていたので、適当に良さそうなところを探して行った。

www.tripadvisor.com

チキンとバジルのパスタとキャロットケーキ。味はそこそこだけど、店の雰囲気が良かった。うるさい音楽がかかってないし、バーテンダーが丁寧だった。

 

年末だからなのかは知らないが、夜のダウンタウンはあんまり人がいない。でも電飾がいたる所にあって、ちょうどいい華やかさになっていた。

 

寝て、ホテル内のスタバで軽く朝食を食べて、帰路へ向かう。途中でTEN RAMENという、Google Mapを眺めてて見つけた謎のラーメン屋に立ち寄った。

www.tenramen.com

 

日本かぶれのアメリカンという感じのラーメン屋。アメリカにしては珍しく立ち食いしかない。Google Mapでは★4.5だったのでちょっと期待してたんだけど、トンコツラーメンはスーパーの生ラーメンみたいな味だった……。悪くはないけど、$12で食べるほどではない。厚く切った豚バラの炙りが乗っていたり、スープがぬるめだったりするところなど、全体的にアメリカ式ラーメンという感じ。

 

I-35をひたすら南下してオースティンに帰る。

都市部を抜けるとひたすらこういう感じの風景が広がっている。特にきついカーブとかもなく、ほぼ直線みたいな道が延々と3時間続く。日本は森や山が広がっているのでそれなりに刺激はあるけど、テキサスは本当に平地しかなくて飽きてくる……。なんもない平地でネズミ取りしてるっぽいパトカーがいて、その脇を制限速度+20マイルくらいで通り過ぎてしまったけどなんも言われなかった。というか周りの車もそれくらい出している……。もっとヤバい奴を取り締まるためのネズミ取りなんだろうか。

photos.app.goo.gl

Wacoの北で渋滞が起きていたので、給油がてらハイウェイを降りて休憩したところでパノラマ写真を撮った。なんもない。

 

年末という特殊な時期ではあったけど、ダラスは結構いい街だった。オースティンよりすっきりとした感じだし、高層ビルが林立しているとちょっと安心する。King Spaはもっと近ければ定期的に行きたい。このためにダラスに移住するのも考えるレベル。

ダラスの交通はちょっと悪い。オースティンはオースティンで道路のキャパシティ以上に車がひしめいていてヤバいんだけど、ダラスは全体的に古いのか、ボコボコになっている道路とか、停止線が消えていてどこで止まればいいか分からない信号とかがちょくちょくあって困った。

collectdを試す

仕事だとモニタリング周りは既に整備されており、アプリケーション書きとしては社内用のライブラリを叩くだけでメトリクスが勝手に飛んでいってダッシュボードに表示されてしまう。それはそれで楽なんだけど、せっかく自宅サーバを立てたことだし、いい機会なのでモニタリング周りを勉強してみることにした。

いろいろ調べた結果、collectdでメトリクスを集めてCloudWatchに飛ばすのが良さそうという感じになった(このへんの話は後で書く(と思う))。

collectdはWikiがあって大体のことがまとまっているっぽい。 collectd Wiki

インストール

Ubuntuだとapt-get install collectdで入った。

設定

collectdは本体にいろいろプラグインを差し込むことで機能拡張ができるようになっている。たとえばcpuプラグインを読み込むとCPUの使用状況を集めてくるし、networkプラグインを読み込むと集めたデータを定期的にサーバへ送りつけるようになる。便利そうなプラグインはパッケージにバンドルされており、/usr/lib/collectd以下にインストールされている。

Ubuntuの初期設定だとcpuやmemoryといったシステムの基本的なメトリクスを集めるのに加え、RRDという形式でメトリクスを/var/lib/collectd/rrdに保存するようになっている。RRDの扱い方はいまいち分からないが、ドキュメントを流し読みして勘で以下のようなコマンドを叩いたらそれっぽい感じになった。

[2019-12-26 01:02:19-0600][/etc/collectd]
osamu@glados(:|✔)> rrdinfo /var/lib/collectd/rrd/glados/cpu-0/cpu-user.rrd
filename = "/var/lib/collectd/rrd/glados/cpu-0/cpu-user.rrd"
rrd_version = "0003"
step = 10
last_update = 1577343767
header_size = 3496
ds[value].index = 0
ds[value].type = "DERIVE"
ds[value].minimal_heartbeat = 20
ds[value].min = 0.0000000000e+00
ds[value].max = NaN
ds[value].last_ds = "1561932"
ds[value].value = 1.4700000000e+01
ds[value].unknown_sec = 0
rra[0].cf = "AVERAGE"
rra[0].rows = 1200
rra[0].cur_row = 397
rra[0].pdp_per_row = 1
rra[0].xff = 1.0000000000e-01
rra[0].cdp_prep[0].value = NaN
rra[0].cdp_prep[0].unknown_datapoints = 0
rra[1].cf = "MIN"
(snip)

[2019-12-26 01:04:33-0600][/etc/collectd]
osamu@glados(:|✔)> rrdtool graph --start now-3600 --end now /tmp/a.png DEF:a=/var/lib/collectd/rrd/glados/cpu-0/cpu-user.rrd:value:AVERAGE LINE:a#0000FF
481x141

[2019-12-26 01:04:37-0600][/etc/collectd]
osamu@glados(:|✔)> display /tmp/a.png

rrdtool graphの出力結果

なんかCPU使用率っぽいグラフが出ている。RRDのセマンティクスはなんか独特っぽい雰囲気があるが、さしあたりの目標はCloudWatchに送ることなのであんまり深追いはしないでおく。

ネットワーク経由の出力も見ておこう。こんな感じでnetwork pluginをロードする。接続先はデフォルトだとUDPのport 26826になる(参考)。

[2019-12-26 01:07:55-0600][/etc/collectd]
osamu@glados(:|✔)> cat /etc/collectd/collectd.conf.d/network.conf
LoadPlugin "network"

<Plugin "network">
  Server "localhost"
</Plugin>

[2019-12-26 01:08:59-0600][/etc/collectd]
osamu@glados(:|✔)> sudo systemctl restart collectd

[2019-12-26 01:09:04-0600][/etc/collectd]
osamu@glados(:|✔)> netcat -u -l localhost 25826 | hexdump -C
00000000  00 00 00 0b 67 6c 61 64  6f 73 00 00 08 00 0c 17  |....glados......|
00000010  81 17 29 14 85 16 7f 00  09 00 0c 00 00 00 02 80  |..).............|
00000020  00 00 00 00 02 00 09 73  77 61 70 00 00 04 00 09  |.......swap.....|
00000030  73 77 61 70 00 00 05 00  09 75 73 65 64 00 00 06  |swap.....used...|
00000040  00 0f 00 01 01 00 00 00  00 00 00 00 00 00 08 00  |................|
00000050  0c 17 81 17 29 14 8b 52  4d 00 04 00 0c 73 77 61  |....)..RM....swa|
00000060  70 5f 69 6f 00 00 05 00  07 69 6e 00 00 06 00 0f  |p_io.....in.....|

(snip)

確かになんかやっているっぽい。

StatsD

プラグインを書いたら対応するメトリクスを収集してくるという世界観はなかなかUNIXっぽくていいが、実際問題としてはアプリケーション固有のメトリクスも吐きたいし、かといってアプリケーションごとにプラグインを書くのはあまりにもめんどい。そもそもアプリケーションのカスタムメトリクスはpush的であり、CPUやメモリの使用率のように定期的にポーリングしてくるというアーキテクチャはあんまり適合しない……。幸いにして世の中には既にStatsDという標準があり、DataDogもStatsD互換のagentを提供しているほどに市民権を得ているので、これに乗っかるのが普通だと思う。

collectdはなんとStatsD Pluginを提供しており、StatsD形式の入力を受け付けてメトリクスとして吐いてくれるようになる。

[2019-12-26 01:26:52-0600][/etc/collectd]
osamu@glados(:|✔)> cat /etc/collectd/collectd.conf.d/statsd.conf
LoadPlugin "statsd"

<Plugin statsd>
  Host "localhost"
  Port "8125"
  DeleteSets     true
  TimerPercentile 90.0
</Plugin>

[2019-12-26 01:27:00-0600][/etc/collectd]
osamu@glados(:|✔)> sudo systemctl restart collectd

[2019-12-26 01:27:17-0600][/etc/collectd]
osamu@glados(:|✔)> echo "azunyan:1|c" | nc -u -w0 localhost 8125

[2019-12-26 01:28:03-0600][/etc/collectd]
osamu@glados(:|✔)> echo "azunyan:3|c" | nc -u -w0 localhost 8125

[2019-12-26 01:28:15-0600][/etc/collectd]
osamu@glados(:|✔)> echo "niconii:25252|c" | nc -u -w0 localhost 8125

[2019-12-26 01:29:06-0600][/etc/collectd]
osamu@glados(:|✔)> ls /var/lib/collectd/rrd/glados/statsd/
derive-azunyan.rrd  derive-niconii.rrd

[2019-12-26 01:52:57-0600][/etc/collectd]
osamu@glados(:|✔)> rrdtool graph -a CSV --start "20191226 01:27" --end "20191226 01:40" - DEF:a=/var/lib/collectd/rrd/glados/statsd/derive-azunyan.rrd:value:MAX LINE:a#0000FF
"time",""
1577345230,"NaN"
1577345240,"NaN"
1577345250,"NaN"
1577345260,"NaN"
1577345270,"NaN"
1577345280,"NaN"
1577345290,"NaN"
1577345300,"2.1000000000e-01"
1577345310,"0.0000000000e+00"
1577345320,"0.0000000000e+00"
1577345330,"0.0000000000e+00"

(snip)

うーん、なんか値は出ているものの、レートっぽい値が出力されてしまっている。合計値も見たいんだけど……。そもそもderiveってなんだ?

Data source - collectd Wiki

Wikiによると、Deriveは読んで字のごとくDerivativeで、要するに前回記録された値との差分を時間差で割ったものを保持しているらしい。なるほど?じゃあ生の値は保存してないってこと?確かに積分すればだいたい元には戻るけど、計算誤差が出るから嬉しくなさそう……。

さすがに合計値が出せないことはないだろうと思ってソースを読んでみると、どうもconf_counter_sumという変数がセットしてあると、Gaugeとして別に値を出力してくれるらしい(statsd.c:800)。そしてこれはCounterSumというコンフィグでコントロールされていることが分かる(statsd.c:639)。コメントの雰囲気的に、合計値が知りたいならDeriveを積分すりゃいいじゃんって思っていそうで不安だけど……。後のセクションで分かるように、statsd pluginではどうもDeriveの意味がオーバーライドされているっぽい。

[2019-12-26 02:25:19-0600][/etc/collectd]
osamu@glados(:|✔)> echo "azunyan:3|c" | nc -u -w0 localhost 8125
                                                                                
[2019-12-26 02:25:39-0600][/etc/collectd]
osamu@glados(:|✔)> echo "azunyan:4|c" | nc -u -w0 localhost 8125

[2019-12-26 02:26:16-0600][/etc/collectd]
osamu@glados(:|✔)> rrdtool graph -a CSV --start "20191226 02:25" --end "20191226 02:27" - DEF:a=/var/lib/collectd/rrd/glados/statsd/count-azunyan.rrd:value:AVERAGE LINE:a#0000FF
"time",""
1577348710,"0.0000000000e+00"
1577348720,"0.0000000000e+00"
1577348730,"0.0000000000e+00"
1577348740,"6.0000000000e-01"
1577348750,"3.2000000000e+00"
1577348760,"3.2000000000e+00"
1577348770,"0.0000000000e+00"
1577348780,"NaN"
1577348790,"NaN"
1577348800,"NaN"
1577348810,"NaN"
0x0

[2019-12-26 02:26:24-0600][/etc/collectd]
osamu@glados(:|✔)> ls /var/lib/collectd/rrd/glados/statsd/
count-azunyan.rrd  derive-azunyan.rrd  derive-niconii.rrd

[2019-12-26 02:27:07-0600][/etc/collectd]
osamu@glados(:|✔)> cat /etc/collectd/collectd.conf.d/statsd.conf
LoadPlugin "statsd"

<Plugin statsd>
  Host "localhost"
  Port "8125"
  DeleteSets     true
  TimerPercentile 90.0
  CounterSum true
</Plugin>

なんかひどいことになっている。3つの数を足すとちょうど7になるので、時間差で記録したcountが均されているっぽい。しかしstatsd.cを読んでもこういう平均っぽい処理は行われていない……。

collectdのソースコードを読んでみると、plugin_dispatch_valuesという関数を呼ぶことでこのプラグインが読んだ値をcollectdに通知しているっぽい。ドキュメントによるとこの関数は全てのwrite callbackを発火させる作用があるとのことなので(参考)、最終的にはRRD pluginがstatsd pluginの読んだ値を書いているはず。ということは、RRDがなんか変なことをしているだけで、CloudWatchと繋いだら普通に出てくる可能性が高い……のか?

プラグイン機構の実装

プラグイン機構の詳細はWikiに書いてあり、モジュール固有のinit, read, write, shutdownのコールバックを登録するという、よくある感じの構造になっている。

Plugin architecture - collectd Wiki

上でも書いたplugin_dispatch_valuesの実装はdaemon/plugin.cにある。この関数自体は本当に素直に、飛んできた値をwrite queueに詰めることしかしていない。このへんを見ても特に怪しいことはしていないので、やっぱりRRD pluginが変なことしてそう……と思いつつソースツリーを眺めていたら、csv pluginというのを見つけた。飛んできたメトリクスをCSVに書き出すやつのように見える。やってみましょう。

[2019-12-26 03:39:37-0600][/etc/collectd]
osamu@glados(:|✔)> cat collectd.conf.d/csv.conf
LoadPlugin "csv"

[2019-12-26 03:40:15-0600][/etc/collectd]
osamu@glados(:|✔)> echo "azunyan:3|c" | nc -u -w0 localhost 8125

[2019-12-26 03:40:24-0600][/etc/collectd]
osamu@glados(:|✔)> echo "azunyan:4|c" | nc -u -w0 localhost 8125

[2019-12-26 03:40:36-0600][/etc/collectd]
osamu@glados(:|✔)> cat /var/lib/collectd/glados/statsd/count-azunyan-2019-12-26
epoch,value
1577353227.505,3.000000
1577353237.505,4.000000
1577353247.505,0.000000

[2019-12-26 03:40:50-0600][/etc/collectd]
osamu@glados(:|✔)> cat /var/lib/collectd/glados/statsd/derive-azunyan-2019-12-26
epoch,value
1577353227.505,3
1577353237.505,7
1577353247.505,7
1577353257.505,7

ちゃんと記録されてるーーーーーー!!!ただDeriveのセマンティクスがWikiと違って累積和になってるっぽい……というかcountとderive逆じゃない?バグっぽい雰囲気がする。

おわり

クソコード批判とクソコード批判批判はなぜ燃えるのか

Short Answer:批判は人格否定だからです。

Long Answer:

知的活動の成果物とアイデンティティ

文脈 note.mu

要約すると、「自分の書いたコードと自分のアイデンティティは不可分であり、コードを否定されるのはアイデンティティを否定されることに等しい」という話と、「研究への批判は人格に対する批判を意図してはいないので、それらを混同するべきではない」という話であり、一般化すると「知的活動の成果とアイデンティティを同一視するのは妥当であるか」という問題になると思う。話が面倒になるので、本当に人格攻撃がしたくて批判しているケースはここでは考えない(現実の批判は、両者が様々な割合で混ざっているものだと思う)。

この問題に対する答えは、おそらく立場によって異なる。成果物を出す側、すなわちこの文脈で批判を受ける側としては、成果物とアイデンティティは多かれ少なかれ同一視することが普通ではないかと思う。成果物とは自己顕示欲を満たすものであり、それまでに培ってきた思索の質を反映するものであり、人生の少なくない時間を費やした努力の成果である。しかも現代社会では、試験や評定といった形で思考の質を評価することが一般に行われており、この尺度において多くの人に受け入れられることが高い社会的立場、つまりアイデンティティに直結すると言って良い。このような状況で、思考の果てに生み出した成果物を否定されることは、直接的にせよ間接的にせよ、思考の質を否定されていることと同じであり、ひいてはアイデンティティを脅かすものである、という解釈は十分に納得できる。

一方で、批判する側はこの文脈からは容易に離れていられる。既に何らかの成果物がある場合、そこに思いつく限りの具体例やパターンを当てはめてみることにより、理想的な形からの逸脱は比較的容易に発見できる。ことプログラミングや研究に関しては、前提条件に形式的な操作を施すことで論理的に導かれる帰結が重視される傾向にあるため、一つの不整合は成果物の価値を毀損するのに十分な影響がある。そういった不整合を発見した場合であっても、批判側は成果物の表現する論理に形式的な操作を行い、その結果を報告しただけであるから、成果物の背後にアイデンティティが密着しているという事実は用いておらず、議論に用いていないのだから無関係だと主張したくなるのも、批判側の視点としてはあながち的外れではない。もちろん、提示した反例が実は誤っているというケースはありえるし、反例の瑕疵を指摘された場合には彼我が逆転して一転アイデンティティの危機となる可能性もある。しかし、批判は常に後出しになるため、批判側は自信がなければ黙っていることができるから、よっぽどの理由がない場合は安全圏からものを言うことができる。

かくして、批判側は成果物そのものの価値を、それを生み出した人格とは切り離して評価していると考える一方で、受け手は成果物を貫通してアイデンティティを攻撃されているように感じるという構造ができる。批判という行為の性質上、この非対称性を変えるのは難しいから、解決策としては互いの意識を調整するというあたりが落とし所になる。つまり、批判側はそれがどのような種類の批判であっても、本質的には受け手にとって人格攻撃の性質をもつという点を理解し考慮すべきだし、受け手は批判の全てが人格へと向かっている訳ではないことを理解し、意識的に成果物とアイデンティティの距離を置くべきである。どちらの比重が大きくなるかは、知的活動の種類によると思う。例に挙げたプログラミングや研究であれば、瑕疵のない論理を組み上げることが重要であるため、受け手は自分のアイデンティティたる成果物の一部を放棄し、瑕疵を取り除く勇気をもつようにしたほうが有益だろう。逆にアイデンティティの表現そのものが目的である、絵画や音楽といった芸術作品を批判する場合には、表面的な指摘からは想像できないほどに相手の深部を傷つける可能性があることに留意したい。

このバランスを見誤ったとき、批判は燃える。微妙なコードを強く批判すると、相手は傷つく。傷ついた人は批判されたことを批判する。批判側は有利な立場とは言え、それは批判の内容についてであり、批判するという行為そのものについては大抵ノーガードだから、やはり傷つく。互いに殴ったらあとはもう戦争しかない。特にTwitterなんかやってるような人は脳を止めて脊髄反射で会話してるので、見たいものしか見ないし書きたいものしか書かないし行間は読まない。後には焼けただれた荒野だけが残る。

元のNoteでも紹介されていた、ポール・グレアムアイデンティティは控え目に」は、成果物とアイデンティティが分離していることを期待されるケースにおいて、この問題への効果的な対処法になっている。批判がアイデンティティに刺さる痛みが嫌なら、当たり判定を小さくすればいい。でも芸術家はこの手法使えないと思うんだけど、どうしてるんだろう。 blog.livedoor.jp

「クソ」コードという言葉

以上の議論は成果物とアイデンティティの関係に着目していたが、「クソコード批判」という文脈に固有(ただし、他の文脈への転用は容易)の問題として、「クソコード」という言葉の意味にも議論の余地がある。インターネット上や現実でのこの言葉の使われ方から考えると、「クソ」の部分の解釈には相当の幅があるように感じられる。この幅の一端では、「クソ」という言葉は完膚なきまでの否定であり、根本的に発想を変えなければ修正不可能であるような、大きい瑕疵を意味している。他方の端では、「クソ」は単なる二値分類のラベルであり、無謬ではないという程度の意味しかもっていない。このような広い解釈をもつ言葉を、特にコンセンサスもなく使った場合、大混乱をきたすことは火を見るよりも明らかであるし、更に人格否定の性質を多少なりとも内包しているのだから、収拾がつかなくなるのはほとんど当たり前とも言える。

自分はかなり軽い意味で「クソコード」という言葉を使う方だが、主な理由としては、瑕疵のあるコードを表現する語呂のいい言葉が他に存在しないという点が大きい。また、書いたコードとアイデンティティの分離度合いが大きく、少しでもまずいコードは書き直すことに抵抗がなかったり、そもそも自分の思いついたコードが最善であると絶対の自信をもって確信していることが少なく、改善の余地があるのにこの場で思いつけないのは自身の不徳のなすところであるという意識があるため、ほとんどのコードは多かれ少なかれ妥協を含む、完全ではないものだと思っている。加えて、プログラミングコンテスト等を通じて、点数や順位という比べやすい形で負けを意識することが普通だったため、「クソ」のような単語がもつネガティブなニュアンスについても、通常の人が感じるものより弱いのではないかと思う。

こういった意識で「クソコード」という言葉を使う人間が、「クソ」を重く捉える人と話せば、一瞬で齟齬が生じる。そして傷ついた人が経験を共有すると、軽く捉える人は「クソ」の部分を無視して、批判そのものを批判しているように解釈する。すると重く捉える人は反論を繰り返し……という負の連鎖が発生しているのではないかと思う。一般論として、Twitterのような文字数が制限されている上に脊髄反射で文章を書くようなプラットフォームでは、言葉の解釈が食い違っていることに気づくこと自体難しいし、気づいたとしても認識を擦り合わせるのは大変な労力を要する。でもTwitterでそんなめんどくさいことしたくないじゃん?かくしてTwitterは荒れ、批判は批判を呼び、炎はどんどん燃え盛り、人の精神を焼き切っていく。

十数行の変更で改善できる程度の、小さい瑕疵のあるコードをうまく言い表す言葉ってないんですかね。

結局どうすればいいの

  • 批判は人格攻撃なので、相手の精神状態をちゃんと見て調節したほうがいい。
  • 批判されても人格と切り離せるようにしておくとよい。人格攻撃したくてしてそうな奴からは得るものがないので逃げたほうがいい。
  • 「クソコード」よりカジュアルな言葉がほしい。
  • Twitterはクソ。

Spider-Man: Far From Home

Spider-Man: Far From Home

Tony Stark亡き後のヒーローとは何なのか?という話。TonyはPeterに入れ込んでいたみたいだけど、Tonyの技術者・経営者としての成功のような、自信を支えるバックグラウンドを持っていないPeterに押し付けるのは酷だよなあと思う。そのPeterも正体バレ+Beck殺しの濡れ衣を着せられてしまい、追い詰められていきそうで不穏な感じ。かつてのTony StarkもStark Industriesの兵器の流出問題で同じような立場に立たされていたけど、Tonyを支えた才能はPeterにはない。しかしPeterには支えてくれる仲間がいる……。これシンフォギアXVでは??やはりアイアンマンはシンフォギア(確信)

ホログラムを使って現実を上書きする戦闘もよかった。ニンジャスレイヤーのメンタリスト戦を彷彿とさせる、何を信じればいいのかどんどん分からなくなっていくトランス感が気持ちいい。こういうメタ系の演出は一般受けしないので普通の映画ではやんない印象があるけど、そもそも一見さんを容赦なく切り捨てているMCUだからできるのかなと思った。

ICFPC2019感想&反省

ICFPC2019が終わってもう一週間も経ってしまった。忘れないうちに感想と反省を書いておく。

リポジトリは生きてるサーバのパスワード等が入っているのでまだ公開しません。後処理をする前に中国旅行に来てしまったため……。

あ、チームは1kg cheeseです。Register直前までチームメンバーが好き勝手なチーム名を提案していてまとまらなかったが、yuustiがドミノ・ピザの1キロチーズピザを頼もうとか言い出したため、なし崩し的にこのチーム名になった。

コンテストの感想

問題文中でLambda LifterとPanterに言及しており、しかも3回アップデートが来るというそれらの回を踏襲したような形式から、また微妙な回かと思って戦々恐々としていたが、フタを開けてみればかなり良い問題だった。問題自体は適度な大きさの探索でとっつきやすく、プログラムを提出する必要がないのは安心感があるし、タイブレークとしてリアルタイム性のあるマイニング問題が出たのも新鮮味があった。

一方で、前半に要素を詰め込みすぎな感じはした。特に24時間ノンストップでLightningを終わらせて疲れ切った直後に、4時間でマイニング対応してねというのはなかなか辛い。体力を消耗して余裕がなくなる前に対処できるよう、最初からマイニングのようなリアルタイム性を要するシステムが来ると分かっていると良かった。

役割分担

今年はy3eadgbeが一身上の都合で抜けて、新しくkenkooooが入った。役割は以前とほぼ同じで、AI班がkawatea, yuusti, mkut、インフラがosa_k, amylase, kenkooooという分担になった。

  • ○役割分担、人数の割当て共に適切だった。
  • ✘インフラ班は人数が多かったものの、互いに互いのコードをちゃんと把握しておらず、ownerが寝ているときにトラブルが発生すると結構どうしようもなかった。特に今回はリアルタイム性が重要だったため、即時トラブル対処ができないと点数に直結してしまうので厳しかった。
    • 全員バラバラの言語かつモジュールでシステムを作っていたのが遠因となりシステムが把握しづらくなっていたので、全部入りの1モジュール構成にすれば見通しが良かったかもしれない。
    • MySQLにアクセスする部分はPythonを使っていたが、誰もPythonMySQLドライバの挙動をよく分かっておらず、異常に大量のコネクションを張ろうとして、サーバ側のコネクション制限に引っかかりまくって辛かった。
  • ✘インフラがいつ壊れるか戦々恐々としていると結構脳に負荷がかかるので、暇な時にAIを実装してみるといった遊びができなくて悲しい。
    • 自分は左手法で隙間を舐めていく動きがいいんじゃないかとずっと主張していたので実装したかったけど、余裕がなくて結局諦めてしまった。
    • 壊れにくく、かつ他人にメンテを投げやすい設計ができると負荷が軽減できるのでは?

解答ファイルの管理

前回まではソルバの出力をwrapperがポータルサイトREST APIを叩いてアップロードする形式だったが、今回は所定のディレクトリに置かれたファイルをIndexerが定期的に確認する方式にした。

  • ○前回までと同様、Flask製のポータルサイトMySQL Connection Error等で落ちまくっていたため、HTTP経由で生成した解答を登録する方式だとほぼ間違いなく死んでいた。
  • ○インフラが整わないうちに生成された解答をとりあえずコピーしておき、後で採点するというムーブができた。
  • ✘ファイルはコピーされたがIndexerにまだ検出されていない状態の解答が見えないため、AI班に待ちが発生してしまった
  • ✘解答のVerifyもIndexerにやらせてしまったため、バッチ実行の後などで解答が大量に生成された後はIndexerが遅くなった。
    • 新しいファイルを拾ってキューに入れるプロセスと、実際に採点と登録をするプロセスを分ければマルチプロセスできそう。
    • ソルバを実行するためのwrapperに採点まで組み込む構成も考えたが、verifierがおかしいことが後でわかった時のbackfillや、複数のマシンをセットアップした時にいちいちHeadless Chromeをセットアップしないといけなくて面倒くさいなどの、非本質的な部分でrejectしてしまった。ただ、AI作成者が自分のマシンでwrapperを走らせていたことも考えると、一概にダメとは言えなそう。

ソルバの管理

前回と同様、Gitリポジトリのmasterで指定されたパスにあるコードを make && ./run.sh する素朴な実装にした。

  • ○ソルバの更新がすぐに反映できた。
  • ○サーバ上で実行する限りにおいて、必ず最新の補助スクリプトを使うことを強制できた(2017年はブランチで分けたが、古い補助スクリプトが使われて問題を起こすケースがあった)。
  • ✘ソルバにバグが混入すると、その後もずっとバグり続けるのが厳しい。オフライン実行型のタスクなら普通は大きな問題にはならないが、今回はLambda Coinのマイニングというオンラインに近いタスクがあったため、バグったソルバのせいで解答に失敗することがあった。最終的に10万LMC程度損していると思われる。
    • ソルバをパッケージ化してfreezeし、可能な範囲で正しさが保証されているソルバを動かせるようにするべきだった(ディレクトリを切ることで擬似的にfreezeしてもらっていたが、間違ってバグっているコードで上書きする事故があった)。

マイニング

Lightning終了時点まで完全にオフラインの問題だと思っていたところに突然ぶっこまれたので、あまり複雑なシステムを実装する気力がなく、最終的にはその時点で有力だったソルバと、kawateaが作ったマップ生成プログラムを直列に実行してsubmitするスクリプトを書いた。

  • ○初回のマイニングが開始した時点ではとりあえず動いた。
  • ✘ずっと動かしていたら、問題サイズがある程度大きくなったところでソルバの実行に長時間かかるようになり、数回解答に失敗した。
    • 最終的に、マイニングのソルバも通常のシステムとマージして、強いCPUを使いつつ複数のソルバを並列で回せるようにした。もっと早くやっておくべきだった。

リポジトリ構成

単一のGitリポジトリにみんなでコミットする構成。特にブランチ分けやレビューなどしなかったので、無限にマージコンフリクトが起きて面倒だった。git pushのときに勝手にfetchしてrebaseするオプションとかありそうだけど……。