2015-04-28

超会議2015の感想

超会議2015が開催された。

ほぼすべてのドワンゴ社員は、何らかの形で超会議の運営スタッフとなる。筆者はゲームエリアでアナログゲームのインスト要員をしたかったが、どういうわけかまるなげ広場に割り当てられた。

さて、事前の社員説明会が月曜日の朝(ドワンゴ時間)に行われた。説明会のスケジュールが社内のスケジュール管理システムに追加されたのは、おそらく金曜日の昼(ドワンゴ時間)以降である。月曜日は朝過ぎに出社して、ボードゲームなどをしていたので、説明会に参加するのを忘れてしまった。さて、同じ場所に割り当てられた表に顔の出ている他のドワンゴ社員であるま○ら○きも、同じく忘れたらしい。二人で議論した結果、この状況で説明会に参加するのは不可能であるという結論に達した。

さて、説明会に参加した人の話では、まるなげ広場に割り当てられたスタッフは、馬車をやるらしい。

馬車というのは、野尻抱介なる人物が毎年行っている超会議の企画だ。その内容は、荷車に人を載せて、人力で引っ張りながら、会場である幕張メッセのホール内を一周するというものだ。と、概略だけ説明すると、なんともチープな企画のように読めるが、その文脈が問題だ。

なにしろ、超会議は来訪者が相当に多い。去年の超会議では、通路に人があふれ、目的の場所まで進むにも人をかき分けて行かなければならない有り様だった。そこに荷車を通すのだから大変だ。荷車の前後左右に人を配置して、荷車が通れる空間を確保しながら進まなければならない。

さて、超会議の前日にリハーサルで幕張メッセに行ってみると、馬車が用意されていた。予想に反してなかなか興味深い装置が取り付けられている。ブレーキはもちろんのこと、何とパワーステアリングがついているではないか。また、車輪のボールベアリングの性能のおかげか、とても軽い力で引っ張ることができる。荷車には乗り降りするための階段がついているが、今回は強化されているらしい。聞くところによれば、前回の超会議で、力士が乗ろうとしたところ、その重さで階段が壊れたとのことだ。去年の雪辱を晴らすべく、今回の階段は力士が乗っても問題ないようにしたとのことだ。

他には、スピーカーがついていて、効果音を鳴らしたり、マイクが使える。電光掲示板がついていて、文字列を流すことができる。取り外しできる椅子がついている。

さて、リハーサルで練習がてら、他のブースを回ってみた。アニメブースでは、ごちうさブースとゴルゴ13ブースが特に興味深かった。

ごちうさブースでは、空気で膨らますクッションのコースに乗り、水平に張られるロープを体に装着して、「あぁ^~心がぴょんぴょんするんじゃぁ^~」と叫びながら、前方の抱きまくらをホールドするアトラクションが設置されていた。見事抱きまくらをホールドできたものには、何と抱きまくらがそのままもらえるという。

実際に体験してみたが、かなり難易度が高かった。ロープには伸縮性がなく、空気でふくらませる土台の伸縮性のみを使って目的の一まで到達しなければならない。戦略として、勢いよく跳躍する方法と、土台を掴んでにじり寄る方法とがありそうだ。両方試してみたが、どちらもうまくいかない。土台は掴みどころがないが、ボルダリングで握力が鍛えられたためか、ピンチ力でつまむことはできた。しかし体を引っ張れるほどの力が出せない。

あとから成功した者に話を聞くと、姿勢を低くして跳躍すればよいらしい。成功者はアメフト経験があり、かなり大柄な体格をしていたというのも大きい。

ゴルゴ13ブースは、斜めに張られたロープを滑車で滑空しながら標的を赤外線銃で狙い撃つもののようだ。リハーサルで運営スタッフしかいないのにだいぶ待ち時間があった。また、ハーネスを装着するのは、明らかにクライミング経験のない素人スタッフで、下にマットやネットが張られておらず、やや不安を覚えた。また、実際のアトラクション自体は、並んでまで体験するほど面白くも感じられなかった。もちろん、これはリハーサルでありわずかなにわか運営スタッフだけで練習している段階だからそう感じるのであって、本番は違うのかもしれないが、本番環境では体験していないのでわからない。

アナログゲームのブースを見たが、卓の距離があまりにも密接しすぎている気がした。超会議の参加人数から考えれば、周りが人で埋まって悲惨なことになるのではないか。それに、他のブースと距離が近いのも気になる。

さて、リハーサル後は、海浜幕張駅の近くのイオンモールの中にある、PEKIPEKIというクライミングジムに行った。なかなかおもしろいジムだった。日本でここにしかないクライミングマシーンが置いてある。ベルトコンベヤーを縦にしたような機械にガバホールドが大量に取り付けてある。さながらハムスターの回し車のように、延々と登り続ける機械だ。また、壁の角度も稼働中に変更可能なのだ。実際に使ってみたが、これはトップロープで長い課題を登っていく感覚に近いようだ。PEKIPEKIに行くクライマーは、むしろ短くて難しい課題を好むので、このマシンはほとんど使われていないという。それにしてもどこでもクライミングができる面白い機械だ。自宅や会社にあるといいのだが、お値段が5,600万ぐらいするとのことだ。そしてアメリカから輸入しなければならず、またこの手の機械は、定期的に保守点検しなければならないだろうから、保守契約も結ぶ必要があるだろう。そんなに金があれば、普通に壁を設置させたほうがまだ安そうだ。

さて、当日だ。何しろ会場が幕張メッセだ。運営スタッフの中には自腹で近場に宿を取ったものもいるが、私は家から向かった。幸い、今回はとあるシェアハウスに泊まったため、野方から行くよりも一時間ほど通勤時間を節約できた。

さて、東京駅から京葉線で海浜幕張まで向かったが、まだ朝6時だというのに混んでいる。これが皆超会議に向かうのかと思いきや、乗客の半分ほどは、舞浜駅で降りていくではないか。これが、某黒いネズミの土地だ。毎週末ごとにこれなのだろうから、その集客力たるや呆れ返るばかりだ。

一日あたり、ホールを20周以上歩いたので、○さ○っきは足の痛みを訴えていた。しかし、休むと言いながら、女コスプレイヤーが乗るとカメラを抱えて場所の周りを歩きつつ写真を撮っている。

「いや、足は痛いんだけど、写真はとらなきゃなんないし」

見上げた根性だ。

初日の終わりに、乗合馬車の運営スタッフから打ち上げに誘われたが、極めて非常識的なことに、ニコチン中毒者が同席してタバコを常用すると聞いたので、断った。一体何を考えているのだろうか。失礼という言葉には限度というものがある。その場で罵らなかっただけ、私も丸くなったものだ。

さて、一日目の帰りに、新卒の同僚から飲みに行かないかと声をかけられた。行きたいところではあったが、やはりニコチン中毒者が同席してタバコ吸引をするという。これも断った。一体この世の中はどうなっているのだ。

二日目も終わり、やはりPEKIPEKIに行って壁を登ってから帰った。

ドワンゴ広告

この記事はドワンゴ勤務中に書かれなかった。振替休日と有給でしばらく休みだ。

ドワンゴは本物のC++プログラマーを募集しています。

採用情報|株式会社ドワンゴ

CC BY-ND 4.0: Creative Commons — Attribution-NoDerivatives 4.0 International — CC BY-ND 4.0

ビッグデータツールチェインのセキュリティはビッグリスク、あるいは、誰もHadoopをスクラッチからビルドする方法を知らない件について

The sad state of sysadmin in the age of containers

コンテナー時代のシステム管理者の惨状

システム管理は惨劇に見舞われている。現状は悲惨だ。

筆者は昔気質のシステム管理者に不満はない。システムの稼働を維持し、アップデートし、アップグレードする方法を知っている者達だ。

この憤りは、コンテナーと構築済みVMと、それらがもたらす、「信頼」や「アップグレード」の欠如による悲惨な惨劇に対するものだ。

例えば、Hadoopを見てみろ。誰もHadoopをスクラッチからビルドする方法を知っているようには見えないぞ。依存性とバージョンとビルドツールが悲惨なほどに絡まりあっている。

この手のイケてるツールの中で、古典的なmakeコマンドでビルドできるものは存在しない。すべてのツールが独自の互換性も移植性もない、「今朝俺が考えた方法」でビルドされている。

そして、誰もスクラッチからコンパイルできる者がいないので、皆、コンパイル済みのバイナリをどっか適当なWebサイトから落としている。たいていは、何らの認証や署名もなしにだ。

NSAとマルウェア作成者にとっては天国だろうよ。もはやセキュリティホールを突く必要などないのだ。単にappとかVMとかDockerのイメージを作成して、悪意あるバイナリを読みこませればいいのだ。

DebianのHadoopのWikiページが典型的な例だ。要するに、Debianの連中は、2010年には、Debian上でHadoopをソースからビルドして便利なパッケージを提供するのを諦めたってわけだ。

Apache Bigtopをビルドするには、まずpuppet3なるものをインストールしなければならない。こいつはインターネット上から不可思議な謎のデータをダウンロードする。そして、sudo puppetしてNSAのバックドアを有効にしようとする(たとえば、こいつは時代遅れのビルド済みJDKをダウンロードしてインストールする。なぜならば、利用者は自力でJavaのインストールができないほどのマヌケだと考えているからだ)。そして、gradleのビルドが200行もの役に立たないバックトレースを吐き出さないかどうか祈るのだ。

私はふざけているのではない。こいつは以下のようなコマンドを実行しようとしやがる。

/bin/bash -c "wget http://www.scala-lang.org/files/archive/scala-2.10.3.deb ; dpkg -x ./scala-2.10.3.deb /"

訳注:暗号化されていない、いくらでも途中の経路で差し替え可能な脆弱なHTTPプロトコルを使いコンパイル済みのバイナリをダウンロードして、単に展開する。

これが、パッケージを正しくインストールすらしていないことに注意されたい。単にルートディレクトリに展開するだけなのだ。このダウンロードは署名を一切確認していないし、SSL証明書すら確認していない。(ソース:bigtop/scala.pp at master · apache/bigtop

たとえ、ビルドが正しく動いたにせよだ、まだMavenが未署名のバイナリコードをインターネット上からダウンロードして、そいつをビルドに使いやがる。

クリーンでモジュール化されたアーキテクチャにしないどころではない。最近はどこでもそびえ立つクソのような相互依存性のデッドロックに陥る。前回筆者が調べたところでは、Hadoop classpathはすでにjarファイル100を超えていた。筆者はHBaseGiraphFlumeCrunchPigHiveMahoutSolrSparkElasticsearch(あるいは似たようなApacheのカオスなもの)を使わずに、今では150個ぐらいにはなっている方に賭けてもよい。

スタックとは、「俺は自分が何やってるか全然わかんねー」という意味の新しい言葉だ。

Maven, ivy, sbtは、システムに未署名のバイナリをインターネットからダウンロードし、自分のコンピューターで実行させるためのツールだ。

そしてコンテナーが、この惨状をさらに悲惨にする。

お前ら、コンテナー内でセキュリティアップデートしたことがあるか?

要するに、Dockerの手法とは、未署名のバイナリをダウンロードして、実行して、かつまたそれが会社のネットワークに何らのバックドアをも仕込まないことを祈るためのツールなのだ。

90年代のWindowsのシェアウェア時代に逆戻りしたようだぞ。

さて、Ask toolbarを含むDockerイメージが最初に公開されるのはいつだろうな。Dockerイメージ経由で広がるインターネットワームが最初に流行るのはいつだろうな。

かつて、Linuxディストリビューションは安全なオペレーティングシステムを提供しようとしてた。署名されたパッケージと、Web上の信頼とでだ。バイナリ一致の再現性のあるビルドを試みているところまである。

だが、全てが急にWindows化されてしまいやがった。セキュリティを気にせず、アプリケーションを次のバージョンにアップグレードすることも考えずに、ダウンロードして実行する「アプリ」なるものが氾濫している。「手間と時間の節約」という名目で。

追記:筆者は、これがDocker以前にも行われていたと指摘を受けた。「Dockerとは新しい'curl | sudo bash'である」と。その通りだ。しかし、もはや「データセンター」で信頼できないソフトウェアをダウンロードして実行するのが主流になってしまった。これはまずいことだ。極めてまずい。かつて、システム管理者はセキュリティホールを防ぐことに力を注いでいた。今では、自らdevopsと称し、ネットワーク上に先を争って進出している。

Debianですら2010年にHadoopのソースからビルドしてパッケージ化をあきらめているとは悲惨だ。

続き

Your big data toolchain is a big security risk!

ビッグデータはセキュリテイにとってビッグリスク

この記事は、先の記事、コンテナー時代のシステム管理者の惨状を受けての追記である。この記事を編集している間に、HackerNews, Reddit, Twitterで取り上げられ、多くのコメントやメールが筆者のもとに届いた。驚くべきことに、多くのコメントは、私の意見を支持するものであった。筆者はもっと多くの、「お前は俺のお気に入りのツールが嫌いだからそういうことを言っているのだ」という文句が寄せられるものだと思っていたのだ。しかし、多くの人が筆者と同じ懸念を抱いているようだ。驚いた。

訳注: The sad state of sysadmin in the age of containers | Hacker News, Your big data toolchain is a big security risk | Hacker News

さて、以下が新しい文句記事だ。ビッグデータを別の側面から切り込んでいる。

最近、みんな「ビッグデータ」とやらをやっているようだ。いや、少なくとも、上司にビッグデータをやっていると見せかけているようだ。大抵の場合、そんなにビッグなデータはない。データ解析を以前より行うようになったというだけの話で、そういうことに「ビッグデータ」の名札を貼り付けて宣伝して、上司から承認をこぎつけようとしている。そんなところだろうか。

「ビッグデータ」とは技術用語ではない。ビジネス用語だ。以前ならば使わなかったデータを解析することによって、ビジネス上の何らかの価値を得ようとすることだ。この視点から考えると、そのようなプロジェクトの多くは、確かに「ビッグデータ」と言える。というのも、「データによる利益の創出」プロジェクトだからだ。容量(Volume)とかその他のVを期待する人たちには不満かもしれないが、まあ、この言葉の使われ方はこんなところだ。

しかし、データ容量と複雑度により新たなオモチャツールを必要とする場合においても、重大な問題が見過ごされているようだ。システムとデータのセキュリティである。

現在使われている、「ビッグ データ テクノロジー スタック」なるものは、セキュア以外の点についてはうまくやっている。まあ独自のHadoop(独自のHadoopディストリビューション)ケルベロス認証で共用型サーバーを売りつけて儲けようとする会社もあるにはあるが。

セキュリティの問題は「スタック」の奥底にまである。これは、その環境にも影響されている。流行のツールを追いかける種類の人間を取り巻く環境だ。多くのプロジェクトでは、システム管理者も務めるLinux開発者というものがもはやいなくなり、かわりに、Apple信者が占めている。この種類の人間は半年後に技術が時代遅れになるような世界に生きているので、プロダクトをそれ以上延命させる必要はないのだ。この種類の人間は開発環境を頻繁に再インストールすることを好む。なぜならば、毎回、何かが変わるからだ。この種類の人間はまた、コンピューターが壊れたら新しい型番を買い直す世界に生きている。(これはビッグデータプロジェクトではうまく行かないことに注意されたい。半年おきにスクラッチから出発するだと・・・)。さて、Macユーザーは長年様々な脅威から影響を受けずにいられたが(そして気にしていなかった。GoToFailとかrootpipe exploitを修正するのに失敗したとか)、Macというオペレーティングシステムはとてもセキュアではない。セキュリティを気にかけないユーザーと組み合わせると、爆発しそうだ。

この種類の開発者は、スタートアップ向けのWebサイトのプロトタイプを短時間で立ち上げたり、毎日新機能をローリングリリースしてユーザーにベータテストさせたりして、ドットコム2.0バブルを維持するのには優れている。また、主要なプロダクトのターゲットとする客層もこの種類のユーザーだ。この種類の人間は半年前のことはさっさと忘れ去り、次の技術プロダクトを心待ちにしていて、発売直後に購入する者達である。

この態度は、スタックという、パッケージがビルドされ、アップグレード(安全なアップグレード)される際に、根本的に問題になる。誰も整合性とか再現性に気を払わないのだ。

このブログにコメントしたある者によると、これらのツールはすべて、「二十歳そこそこのガキによって書かれたようだ」。おそらく正しい。もし、マサカリかついだ経験豊富なシステム管理者が近くに板ならば、ここまでひどくなることはないだろう。どうやってシステムを構築し、10年間保守し、セキュアかつ自動的にデプロイするかという経験がある者は、puppetハックとwgetとunzipと未署名のバイナリコードなど決して信頼しない。

このことを聞きたがる人はあまり多くいないだろうが、

お前のHadoopシステムは多数のみ署名のバイナリコードが至るところで使われている。ダウンロード、アップロード、再ダウンロードを際限なく繰り返した結果だ。その.jarが皆が考えるものと同じである保証はどこにもないのだぞ。

Hadoopは依存性の塊だ。そのほとんどが、セキュリティ目的でまともに調査されたことがない。そもそも、調査されたコードからビルドされたバイナリであるかどうかのチェックができるようすらなっていないのだ。

もしかすると隠し機能が潜んでいて、"yourcompany.com"のようなホスト名のシステムを待っていて、コマンドを監視し、会社の重要なデータを盗むかもしれないぞ。そういう風に組まれたシステムは、どうせファイヤーウォールもこの手の対策のために十分に強固に設定されていないだろう。ソフトウェアはどこかと勝手と通信していても、いわゆるDevOps達には気づきっこないのだ(そもそもこの種類の人間は気にしないのだ。)

最近の「ビッグデータスタック」の精神は、90年代のWindowsシェアウェア時代と同じものだ。インターネットのどこかから適当なバイナリをダウンロードして、セキュリティのためにチェックもせず(Hadoopクラスターにアンチマルウェアかけたって話を聞いたことがあるか?)、どこにでもインストールし散らかすのだ。

なお悪いことに、インストールしたものと、インストール方法とを、管理していない。なぜならば、ツールは毎年変わるからだ。しかし、もし開発者が去ったらどうするのだ。もう二度と実行できる状態に戻せないぞ。

一度実行して忘れ去る(fire and forget)

筆者は、この先5年以内に、多くの会社で数多くのセキュリティ上の問題が起こると予想する。これは産業スパイ天国だ。多くの企業は隠し通すだろうが、一部はマスコミに流出して、この適当なコンポーネントをひっつけはっつけするヒプスター的手法は非難されるだろう。今、巨大な「Hadoopバブル」が膨れ上がっていて、いずれははじけるだろう。

信頼できる状態にするには、ビッグデータツールチャインは以下をマモならければならない。

  • 濃縮

    作業に使うツールが多すぎる。多すぎるツールを管理するツールも多すぎる上に、フロントエンドのフロントエンドすら多すぎる。

  • 軽量化

    あらゆるプロジェクトが、あまりにも大量の他のプロジェクトに依存しているが、その殆どは、極めて些細で特殊な使い方しかされていない。依存性を解消すべきだ。

  • モジュール化

    依存性を解消できないが、個々の機能は小さいのであれば、それはユーザーが必要なときにだけインストールすればよいオプショナルな拡張モジュールとするべきだ。

  • ビルド可能性

    MavenとかlvyとかSBTでバックグラウンドで自動的に何かをダウンロードしなくても、誰でもスクラッチからビルドできるようにするべきだ。オフラインで、クリーンなビルドディレクトリで、ビルドを検証して、ドキュメント化せよ。バグ修正が正しく適用されているかどうかを確かめられるためにも、すべてはどんなシステム管理者でも再現性のある方法でビルドできるようにすべきだ。

  • 配布

    CDNからのバイナリのダウンロードを唯一の配布方法にすべきではない。既存の信頼できるLinuxディストリビューションなどの、別の方法での配布も推奨すべきだ。

  • 互換性の維持

    成功するビッグデータのプロジェクトは、一度だけやって終わりというものではない。いずれ、商業化され、何年も動かす必要が出てくる。環境を新しい、より巨大なクラスターに移行する必要が絶対に生ずる。以降に際してデータを失うことはあってはならない。

  • 署名

    コードには署名が必要だ。議論の余地なし。

  • 検証

    ダウンロードには、ダウンロードされたファイルがアップロードされたものと一致するかという検証が必要だ。

  • 統合

    Linuxシステムがサーバー用途にとても優れているのは、万能のソフトウェアの統合管理があるからだ。システムをアップデートしたならばアップデートされる。アップデートには用途に応じたチャンネルがある。例えば、保守的な"stable/LTS"チャンネル、基本的なQAを受けた最新版が入手できるチャンネル、QAをするための最新版が入手できるチャンネル、システムで使うほぼすべてのソフトウェアはこれによって管理される. カーネルに対するセキュリティフィクスであろうと、Webサーバー、ライブラリ、追加機能、拡張モジュール、スクリプト言語などなど。修正を引っ張ってきてすぐにアップデートする。

さて、読者の中には、HortonworksやClouderaやBigtopなどは、すでにパッケージを提供していると反論するものもいるだろう。それは・・・クソだ。連中が「パッケージ」と称するものは、あらゆる品質の標準を下回るものだ。技術的に、ヴァルトブルクは車である。しかし、現在の安全基準を満たすものではない。

たとえば、連中がサポートしているのはUbuntu 12.04だけである。3年も前の古代のUbuntuが、奴らがサポートしている最新版なのだ。それだけではない。この手のパッケージはだいたい同じだ。Clouderaは管理を「コミュニティ」に丸投げした(要するに、自分たちでやることを諦めたのだ。そして、誰かが尻拭いをしてくれることを期待している)。HortonworksとHDP(あとたぶんPivotal HD)も、同じように丸投げした労力を使っている。連中がやっていることといえば、追加のドキュメントを提供したり、最小の労力でBigtopを使ってパッケージをビルドする方法を教育して利しているだけだ。例えば、bigtopの"spark" .debパッケージは空っぽだ。パッケージに.jarSを入れ忘れているのだ。パッケージングがクソであるという例をこれ以上挙げる必要があるだろうか。bigtopのパッケージはすべて、たったひとつのスクリプトのためだけに、独自のgroovyに依存している。そのたった一つの問題のスクリプトをすでに必須な別の言語で書きなおしたり、ディストリビューションが提供するgroovy対応に書きなおしたりせずに、奴らはbigtop-groovyというパッケージを新たに作ったわけだ。

HortonworksとIBMは「オープン データ プラットフォーム」とやらを発表したのを読んだが、筆者には全く関心がなかった。筆者の知る限り、連中は既存のツールに新しい名前をつけただけのことだ。そういうわけで、ClouderaとMapRがこのブランド改名運動に参加しなかったのは、全然驚きでもない。Hadoopはほぼ似通っているのに、なんでそんな名前が必要なんだ?

さて、なぜこれが問題になるのか。もし、何かが動かなかった時、現時点ではどうしようもなくなるのだ。例えば、Hadoopにバグがあってデータを処理するのに失敗したとする。するともうお手上げだ。それ以上データは処理できない。完全に受け身だ。誰が直すのだ? 巷の「ディストリビューション」と称するものはすべて、同じ、クソみたいな出所から始まっている。この世に問題を把握して修正し、ツールチェインを完全にビルドできる人間は、数十人もいないだろう。明らかに、Hadoop会社はどこもかしこも、Ubuntu 2012.04以降をサポートできていない。これで連中が自分で売っているものを理解していると言えるのだろうか。疑わしいものだ。巷のフリーランスの連中は、皆Hadoopをダウンロードして使う方法は知っている。しかし、果たしてツールチェインの中の業務上深刻なバグを修正して、もう一度実行することができるだろうか。これはLinuxディストリビューションに比べて極めて悲惨だ。Linuxにはビルドデーモンがある。すべてのソフトウェアがコンパイル可能かどうかを検証するサーバーだ。典型的なLinuxのパッケージをスクラッチからビルドするには、大抵、二行のよく知られた有名なコマンドを打つだけですむ。経験ある開発者は誰でもマニュアルを読んで、パッケージを修正できる。別のコンパイラーでディストリビューション全体をコンパイルしようと試みている者達さえいる。これによって将来発生するかもしれない互換性の問題を早期に発見できる。

つまり、連中が販売している「Hadoopディストリビューション」なるものは、奴ら自らコンパイルしたものではないのだ。大半はインターネットのどこからか落としてきた未署名で未暗号化で未検証の.jarファイルだ。奴らはどうやってリビルドすればいいのか分かってないし、誰がコンパイルしたのかも分かってないし、どうやってビルドすればいいのかも分かってない。奴らがわかっているのは、最後のレイヤーだけだ。連中はHadoop .jarをコンパイルする方法を知っている。しかし、それをするには、自動的に大量のバイナリをどこからか自動的に落としてくるときたものだ。ツールは自動的なバイナリのダウンロードを警告しない。連中はそれをHadoopディストリビューションに含めている。

現状では、筆者は業務上のデータをHadoopに食わせることを推奨しない。

おそらく、データをHDFSにコピーして遊ぶのは大丈夫だろう。クラスターと開発機を強固なファイヤーウォールで隔離すればの話だ。だが、すyべてを失って最初からやり直しになる覚悟はしておけ。まだ、実用化には程遠い上に、更に不要なゴミを積み重ねているので、更に実用化は遠い。

ツールチェインの未熟についてもう一つ例をあげよう。

scala-lang.orgのscalaパッケージは、UbuntuとDebianに存在する古いscalaパッケージからクリーンにアップグレードできない(どうも、ディストリビューションは、クソみたいな鶏と卵問題を抱えるビルドプロセスのせいで、新しいScalaをコンパイルするのを諦めたようだ。scalaとsbtをブートストラップするのはかなりやっつけハックになる)。上流のパッケージすら、簡単に修正できない。なぜならば、標準のパッケージツールでビルドされておらず、自動的で摩訶不思議で重要な機能を欠くsbtヘルパー(特に、Replaces:フィールドや、cleaner:にすらアクセスできないからだ。パッケージを適切にコンポーネントに分割するための機能)を使っているからだ。どうやら、UbuntuやDebianのパッケージングの経験が一切ないどこかの馬の骨が書いたらしく、すでに実績あるツールを使わず、自前でやっつけハックを使って自動化を試みる間違ったラッパーになっている。

筆者は、いわゆる「ビッグデータ」プロジェクトなるものの大半は、悲惨な失敗を喫することになるだろうと思う。その原因は管理過剰か管理過少か、データやツールやプロジェクト管理の経験不足だろうが、もちろん、当然のことながら、誰も失敗を認めようとはしないだろう。プロジェクトはすべて、政治的なプロジェクトなのだから、絶対に成功しなければならないのだ。たとえ商業化に結びつかず、一ドルも利益を生み出さなかったとしても。

思うに、機械学習をしている連中というのは、システム管理者でもプログラマーでもないのだ。彼らは目的の計算をするためにコンピューターを使っているだけの利用者である。たしかに、コードは書くものの、プログラマーではないし、自称もしない。

2015-04-25

超会議2015 開幕前

超会議2015がやってきた。

前回は超チューニング祭りというハッカソンに割り当てられたが、今回は馬車に割り当てられた。あの尻Pこと野尻抱介の企画で毎年やっているそうだ。

ちなみにこの馬車だが、パワーステアリングがついている。また、ボールベアリングのおかげで、人が乗っても軽々とひくことができる。スピーカーが搭載されていて騒音を発する。かなり世紀末感のある馬車だ。

また、ペッパーも乗るらしい、

ドワンゴ広告

この記事はドワンゴ勤務中に書かれた。超会議は勤務である。

ドワンゴは超C++プログラマーを募集しています。

採用情報|株式会社ドワンゴ

CC BY-ND 4.0: Creative Commons — Attribution-NoDerivatives 4.0 International — CC BY-ND 4.0

2015-04-20

C++勉強会: 歌舞伎座.tech#8「C++初心者会」の開催案内

歌舞伎座.tech#8「C++初心者会」 - connpass

歌舞伎座タワーにあるドワンゴのセミナールームを使い、C++勉強会として、歌舞伎座.tech#8を開催する。

前回はC++の濃い人間を集めすぎて、単なる発表会になってしまったという半生から、今回は、初心者に積極的に発表する勉強会を目的としていて、外部からの飛び入りの参加者を募集している。

勉強会は、発表してこそ勉強になる。そこで今回は、初心者に積極的に発表する場を提供したいと思った。今回の勉強会で発表の経験を得てもらいたい。

20分枠で話すのは難しいと思うかもしれないが、初心者は発表の時間配分の目安が分からず、発表が伸びがちであることを考慮して、余裕を持った時間設定をした。20分間も話さなければならないというわけではない。

当初、初心者枠とガチ枠を設けて、普段発表をしない初心者に積極的に発表をさせようと目論んだが、あまりにも初心者詐称が多いので、ガチ枠を改めてクソザコ枠とした。C++に僅かにでも関係していれば発表できる。

発表者以外からは、参加費千円を徴収して、ピザとビールを注文し、夕方から雑談できる時間も設ける予定だ。勉強会は多種多様な人が集まって、雑談ができるというところにも価値がある。これは通常、勉強会のあとの懇親会という形で行われるが、今回はその場で無造作にピザとビールを設置してみることにした。

ドワンゴ広告

この記事はドワンゴ勤務中に書かれた。

勤務中に勉強会の企画をして、勤務先の施設を使い、勉強会がたぶん勤務扱いになる不思議な仕事をしている。

ドワンゴは本物のC++プログラマーを募集しています。

採用情報|株式会社ドワンゴ

CC BY-ND 4.0: Creative Commons — Attribution-NoDerivatives 4.0 International — CC BY-ND 4.0

2015-04-19

散財

作画、檜山大輔、原作、村田真哉のマンガ、魔女に与える鉄槌が、アマゾンのウィッシュリスト経由で送られてきた。ネット上で切り抜きを見て興味深そうだと思って登録しておいたものだ。実際に読んでみたが、残念ながらファンタジー要素が強かった。

さて、今日は久しぶりに何の用事もない休日なので、高円寺までぶらりと歩いていった。業務スーパーで雑穀米の素を買い求めたい。また、久しぶりに、何か散財したいものだ。散財とはいえ、本当に何の訳にも立たないものを買う趣味はない。何か便利なものがあれば買いたいものだ。

さて、高円寺を歩いていると、洋服屋の軒先にジャージが売られているのに気がついた。そういえば、今持っているジャージはだいぶ傷んでいる上に、一枚しかない。もう一枚買ってもよさそうだ。ジャージ上下で1900円+消費税であった。

さて、高円寺の業務スーパーにたどり着いて、目的の雑穀米の素と、その他数日中に食べる食材を買い込んだ。

さて、帰宅しつつブラブラと歩いていると、リカーオフが目に着いた。ここは、酒の未開封の中古品を販売している店だ。立ち寄ってみたが、あいにくと、面白そうな酒は置いていなかった。ただし、赤いショットグラスが200円と手頃な値段で綺麗だったので、購入した。

なおも歩くと、リサイクルショップが見えてきた。覗いてみたところ、ホームベーカリー用の機器が複数置いてあった。一番安いものはなんと1800円。とても興味深かったが、買うのはやめておいた。

その後、古本屋に入った。野中英次の課長バカ一代が全巻セットで売られていたので、購入した。私は野中英次のギャグセンスが好きなのだ。また、F. Scott FitzgeraldのThe Great Gatsbyと、Richard DawkinsのThe God Delusionを購入した。

なおも別の古本屋に入り、手塚修とか水木しげるなどの昔のマンガがたくさん収録されている漫画集があったので、購入した。

久しぶりに結構な金を使った気がする。

XKCD 1513: コード品質

xkcd: Code Quality

「僕は独学だからさ、コードはちょっと汚いかもしれないけど」
「とにかく見せてみなよ。大丈夫だって」

「うわ・・・」
「ガキが家の写真一枚だけを参考にして斧一本で建てた家みたい」

「顧問弁護士がExcel数式しか辞書がないケータイのスペル訂正機能使ってサラダのレシピを書いたらこんな感じかしら」

「IKEAで口論してるカップルの書き起こしを土台にエラー無しでコンパイルが通るまで適当に書き換えたらこうなるのかしら」
「分かったよ。コード記法の参考書読むから」

The Old New Thing: なぜ環境変数にはTMPとTEMPがあるのか。どちらが正しいのか

Why are there both TMP and TEMP environment variables, and which one is right? - The Old New Thing - Site Home - MSDN Blogs

久しぶりにRaymond Chenの記事を翻訳する。

環境変数を覗いてみると、一時ファイルを置いておく場所を指定する目的の変数が2つあることに気がつくだろう。TMPとTEMPだ。なぜ2つもあるのか。もし値が異なる場合、どちらが正しいのか。

話は1973年にさかのぼる。この当時の一般的なマイクロコンピューター用のオペレーティングシステムはCP/Mだった。CP/Mオペレーティングシステムには環境変数がなかった。環境変数の話を始めるにしては不思議な時代だが、実は大事な話なのだ。環境変数が存在しなかったのだから、TMPやTEMPなどあるはずがない。当時、プログラムに一時ファイルを置いておく場所を指定したければ、プログラムごとに独自の方法で行う必要があった。たとえば、実行ファイルの特定の1バイトをパッチして一時オブジェクトを置いておくドライブレターを変更するなど。

(筆者の記憶する限り、当時のほとんどのCP/Mプログラムはパッチによって設定していた。少なくとも、筆者はそのように設定していた。筆者はWordStarマニュアルに、どのバイトをパッチすればどのように動作するのかという記述があったことを覚えている。また、プリンター用に独自の動作を設定したい場合などに、利用者が独自のサブルーチンを書くことができる数十バイトのパッチスペースが設けられていた。筆者は、「プリンターは次の文字を受け取る用意があるか?」関数を書くのに使った。これでバックグラウンドでの印刷がスムーズになった。)

1981年に話は飛ぶ。8086プロセッサーとMS-DOSオペレーティングシステムがやってきた。8086プロセッサーの設計と、MS-DOSおペーレーティングシステムの設計は、CP/Mの影響を受けている。実際、8080プロセッサー用に書かれたCP/Mプログラムは、機械的に8086プロセッサー用のMS-DOSプログラムに変換できることが設計目標だった。もちろん、変換器は自己改変コードとか、命令の途中にジャンプするとか、コードをデータとして扱うなどの変なトリックを使わないことを前提にしているが、健全なコードならば、変換器はプログラムを変換できたのだ。

(8080プロセッサー用に書かれたコードを8086プロセッサー向けに機械変換できるという目標は、8086命令セットに存在するいくつかの不思議な制約を説明できる。たとえば、8080のHとLレジスターは、8086のBHとBLレジスターにマップされている。8080では、計算されたアドレスにアクセスできるレジスターはHLだけであった。これにより、なぜ8086の4つの基本的なレジスター、AX, BX, CX, DXのうち、メモリにアクセスできるのはBXだけなのかが説明できる)

MS-DOSがCP/Mとの互換性以上に追加した機能として、環境変数がある。既存のCP/Mのプログラムは環境変数を使っていなかったので、初期のMS-DOSプログラムも使うことはなかった。なぜならば、最初のMS-DOS用のプログラムというのは、すべてCP/Mから移植されていたからだ。もちろん、TEMPやTMPといった環境変数を設定することはできるが、誰も注意を払うものなどいない。

やがて、プログラムは最初からMS-DOS用に書かれるようになり、環境変数は設定データを格納するために使えることに皆気がついた。混沌とした市場から、2つの環境変数が、一時オブジェクトを格納する場所を記述するのに使われるようになった。TEMPとTMPである。

MS-DOS 2.0はあるプログラムの出力をパイプして、別のプログラムの入力とする機能を追加した。MS-DOSはシングルタスクのオペレーティングシステムのため、この機能は、最初のプログラムの出力を一時ファイルにリダイレクトして、実行が終了するまで走らせ、しかる後に第二のプログラムを実行しつつ、入力は一時ファイルからリダイレクトするという方法でシミュレートされた。さて唐突に、MS-DOSは一時オブジェクトを作成する場所を必要とするようになった。何らかの理由で、MS-DOSの作者はTEMP環境変数を一時ファイルが作成される場所を指定する方法として使うことにした。

ところで、COMMAND.comがTEMPを使うことにしたという決定は、他のプログラムがTEMPとTMPのどちらを使うかという選択には影響しない。作者の気分次第で選ばれるものだ。多くのプログラムは両方を確認するという折衷的な戦略を取った。そして、どちらの環境変数を先に確認するかというのも、やはり作者の気分次第で選ばれた。たとえば、昔のDISKCOPYとEDITプログラムは、TMPの前にTEMPを先に確認した。

Windowsも似たような道をたどったが、何らかの理由で、GetTempFileName関数の作者は、TEMPより先にTMPを見にいった。

結果として、特定のプログラムの一時ファイルに使われるディレクトリは、プログラムの種類次第となった。WindowsプログラムはGetTempFileName関数を使って一時ファイルを作成することが多く、その場合、TMPを優先する結果となった。

環境変数の設定ダイアログをみると、まだ2つの変数、TMPとTEMPが生き残っていて、読者の興味をひきつけている。ギーク版のアディダスVSプーマのようなものだ。

2015-04-17

超会議2015

ニコニコ超会議2015が25, 26日に開催される。

今回のドワンゴの企画ブースにおけるアナログゲームは、

  • おばけキャッチ
  • ワードバスケット
  • ワンナイト人狼
  • ごきぶりポーカー
  • エセ芸術家ニューヨークへ行く
  • 6ニムト
  • 麻雀
  • 花札
  • ポーカー

他にも、囲碁将棋のブースがある。

今回は闘会議と違い、重たいゲームがほとんどない。

ちなみに、超会議2015では、私はゲームエリアには割り当てられていない。ゲームエリアでボドゲのインストをしたかったので希望を出したのだが、なぜか丸投げ広場に割り当てられた。今回の丸投げ広場は、幕張メッセの9-11ホールなので、いつもの場所とは離れている。その分、去年より面積が広く確保できているようだ。

私は何をするのかというと、おそらく馬車の先導役なのではないかと思われる。ドワンゴ社員の中でも変わり者が割り当てられたと見える。私のほかに割り当てられたもう一人のドワンゴ社員である○○らっきも、やはり変わり者だ。

ドワンゴ広告

この記事はドワンゴ勤務中に書かれた。

ちなみに、馬車の先導役に割り当てられた私とまさ○○きは、予定されていた社員用の説明会を揃って出席し忘れた。金曜日の夜頃に追加された月曜日の昼からの説明会スケジュールなど把握するのはいかにも無理だ。

ドワンゴは本物のC++プログラマーを募集しています。

採用情報|株式会社ドワンゴ

CC BY-ND 4.0: Creative Commons — Attribution-NoDerivatives 4.0 International — CC BY-ND 4.0

2015-04-16

GCC 5.0でのx86におけるPICの改善と、いかに32bit PICコードがクソであるかというお話

New optimizations for X86 in upcoming GCC 5.0: PIC in 32 bit mode. | Intel® Developer Zone

GCC 5.0では、x86(32bit)におけるPIC(Position Independent Code)が改善された。これまではEBXレジスターがGOT(Global Offset Table)のために予約されていたのだが、これを使わなくなった。この結果、貴重なレジスターがひとつ多く使えることになった。

32bit x86におけるPICのダメっぷりは、以下の記事に詳しい。

EWONTFIX - 32-bit x86 Position Independent Code - It's that bad

32-bit x86 Position Independent Codeは実にクソだ。

まず、簡単なCの関数がposition-independent-codeでどうコンパイルされるのかを見てみよう。(つまり、shared library用に-fPICが使われるということだ)

void bar(void);

void foo(void)
{
    bar();
}

さて、GCCはどうコンパイルするのかというと、

foo:
    pushl   %ebx
    subl    $24, %esp
    call    __x86.get_pc_thunk.bx
    addl    $_GLOBAL_OFFSET_TABLE_, %ebx
    movl    32(%esp), %eax
    movl    %eax, (%esp)
    call    bar@PLT
    addl    $24, %esp
    popl    %ebx
    ret
__x86.get_pc_thunk.bx:
    movl    (%esp), %ebx
    ret

ありゃ、なぜこうなるんだ。

もちろん、ここで期待すべきは以下のようなコードだ。

foo:
    jmp bar

PICではないコード生成ならばこうなる。あるいは、barがみえているPICでもこうなる。この理想的なコードは、PICでは得ることができない。なぜならば、呼び出される側(bar)の相対アドレスは、リンク時に固定されていないからだ。別のshared libraryの中かもしれないし、メインプログラムの中かもしれない。

position-independentコードとGOT/PLTのことをご存知の読者は、なぜ以下のようにできないのか疑問に思うかもしれない。

foo:
    jmp bar@PLT

ここでは、symbol@PLTというアセンブリの記法で、アセンブラーに特別な再配置を指定している。これはリンカーがcall命令における相対アドレスを"procedure linkage table"(PLT) thunkしたコードを生成する。このthunkはshared libraryの固定された場所に配置され(つまり、ライブラリがどのアドレスにロードされようと、呼び出し側から固定された相対アドレスとなる)、関数の実際のアドレスをロードしてジャンプする処理をする。 %

これが、問題の発端だ。

実際の関数barにジャンプするために、PLT thunkはグローバルデータにアクセスする必要がある。つまり、ジャンプ先に使う"global offset table"(GOT)へのポインターにアクセスする必要がある。PLT thunkコードは、以下のようなものだ。

bar@PLT:
    jmp *bar@GOT(%ebx)
    push $0
    jmp <PLT slot -1>

ふたつ目と3つめの命令はlazy binding(後述)に関係するもので、ひとつ目がここで問題にするところだ。32-bit x86は現在の命令ポインターからの相対的なメモリーのロード/ストアをする方法が提供されていないため、SysV ABIがその方法を提供している。コードがPICとしてPLT thunkを呼び出すとき、GOTへのポインターを%ebxに隠し引数として渡さなければならない。

さて、なぜそれが4つ前のコードになってしまうのか。

ABI上、呼びだされた側で破壊していい(call-clobbered)レジスターは%eax, %ecx, %edxだけなのだ。隠しGOT引数用のレジスターである%ebxは呼びだされた側で破壊できない(call-saved)。つまり、fooが%ebxを改変した場合、保存してリストアさせる責任を負う。そのため、極めて悲惨な非効率的状況に陥る。

  1. fooは%ebxをbar@PLTへの引数としてロードしなければならない
  2. fooは呼び出し元に戻る前に、%ebxがcall-savedなレジスターのため、%ebxをスタックに保存して復帰させなければならない。
  3. barの呼び出しは末尾呼び出しにならない。なぜならば、fooはbarから戻った後に処理を行わなければならないから。%ebxをリストアさせなければならない

そのため、例2のようなそびえ立つものとなる。

一体どうやったらこの問題を解決できるのか。

まず挙げられる解決法は、隠し引数を渡すレジスターを変えることだ。しかし、これはコンパイラーとリンカーのABI規約を変えずに行えないので、選択肢からは外れる。

bar@PLTへの隠し引数の要件を無くすというのはどうだろうか。これも、ABI変更を伴うが、非互換ではない。とはいえ、現実的ではない。PLT thunkがレジスターを破壊せずにGOTアドレスをロードするのは簡単ではなく、破壊してもいいたった3つのレジスターは、すべてデフォルトではないがサポートしなければならない"regparam"呼び出し規約のために使われている。%ebxを使うという選択は意図的なものだ。引数私に使われている可能性のあるレジスターを壊すのを避けるためだ。

では、どんな手が残されているのか。

PLT thunkをなくしてしまうというのはどうだろうか。例4のようなコードを生成することを目指すのではなく、以下のようなコードを目指すのはどうか。

foo:
    call    __x86.get_pc_thunk.cx
    jmp *bar@GOT+_GLOBAL_OFFSET_TABLE_(%ecx)
__x86.get_pc_thunk.cx:
    movl    (%esp), %ecx
    ret

これはfooの肥大化のかなりを削れるし、PLT thunkのための追加のキャッシュラインの必要な命令もひとつ減らせる。なかなかよさそうだ。

なんで最初からこうなっていなかったのか。

残念ながら、こうなっていなかったのには理由がある。その理由はよろしくない。

PLTが存在するそもそもの理由は、メインプログラムが固定アドレスにロードされて(PIE以前の時代を考えてみよ)、position-independent codeを使わずにshared libraryの関数を呼び出すためのものだ。メインプログラムのPLTは、%ebxに隠しGOT引数を必要としない種類のものだ。なぜならば、固定アドレスであるため、自分のGOTには絶対アドレスを使えるのだ。ただし、メインプログラムはPLTを必要とする。なぜならば、レガシー(非PIC)なオブジェクトファイル、呼び出す関数が実行時に任意の場所にロードできることを知らないコードに対応するためのものだ。(そのようなオブジェクトファイルから、PLT thunkを生成して適切に結びつけられた、ダイナミックリンクされたプログラムを生成するのは、リンカーの仕事だ)

position-independent codeはPLTを必要としない。例6で例示したように、GOT自信から目的のアドレスをロードして、間接的なcall/jumpを行える。position-independentna tなshared libraryコードにおけるPLTの利用は、PLTの別の利点を活用するためのものだ。すなわちlazy bindingだ。

lazy bindingの基本

lazy bindingが使われるとき、ダイナミックリンカーは呼び出される側のシンボル名の検索をロード時まで遅延させる。名前検索は関数が最初に呼ばれる時まで遅延される。理論上、これは実行時のオーバーヘッドとして、やや複雑な機構と最初に関数を呼び出した時の遅延を犠牲に、起動時間を節約するものである。

少なくとも、数十年前にこの機構が設計された時の理屈はそうであった。

現在、lazy bindingはセキュリティの足かせとなっているし、パフォーマンス乗の利点というのも疑わしくなっている。最大の問題は、lazy bindingが機能するためには、GOTは実行時に書き込み可能でなければならないということだ。これは任意のコードの実行への攻撃ベクターとなってしまっている。近代的な強固なシステムはrelroを使う。これはGOTの一部ないしは全部を、ロード後にリードオンリーにする。しかし、lazy bindingするPLTのGOTスロットは、この防衛から外さねばならない。relroリンク機能の恩恵を受けるには、lazy bindingは無効にしなければならない。それには以下のようなリンクオプションを使う。

-Wl,-z,relro -Wl,-z,now

つまり、lazy bindingというのは、deprecated扱いされていると考えて差し支えない。

そういうわけで、musl libcはlazy bindingをそういう理由と他の理由で、サポートしていない。

lazy bindingとPLT

例5のPLT thunkの2行目と3行目を見よ。どのように動作しているかというと、bar@GOT(%ebx)は当初(lazy binding前)2行目へのポインターがダイナミックリンカーにより設定されている。2行目で定数0がpushされているのは、PLT/GOTスロット番号だ。jumpする先のコードはthunkで、スタックに引数としてpushされたスロット番号を使い、lazy bindingを解決するためのコードを呼び出す。

例6で、同じことを実現するのは簡単ではない。同等のことをしようとすれば、呼び出し側を遅くさせ、呼び出すたびにコード重複が必要だ。

つまり、効率的なx86 PIC関数呼び出しができないのは、大昔の間違った機能をサポートするためなのだ。

幸い、修正可能だ。

もし、lazy bindingを諦めることができればの話だ。

Alexander MonakovはGCCの簡単なパッチを用意した。これはPLT経由のPIC呼び出しを無効にするものだ。ひょっとしたら上流に取り入れられるかもしれない。

diff --git a/gcc/config/i386/i386.c b/gcc/config/i386/i386.c
index 3263656..cd5f246 100644
--- a/gcc/config/i386/i386.c
+++ b/gcc/config/i386/i386.c
@@ -5451,7 +5451,8 @@ ix86_function_ok_for_sibcall (tree decl, tree exp)
   if (!TARGET_MACHO
       && !TARGET_64BIT
       && flag_pic
-      && (!decl || !targetm.binds_local_p (decl)))
+      && flag_plt
+      && (decl && !targetm.binds_local_p (decl)))
     return false;

   /* If we need to align the outgoing stack, then sibcalling would
@@ -25577,15 +25578,23 @@ ix86_expand_call (rtx retval, rtx fnaddr, rtx callarg1,
       /* Static functions and indirect calls don't need the pic register.  */
       if (flag_pic
          && (!TARGET_64BIT
+             || !flag_plt
              || (ix86_cmodel == CM_LARGE_PIC
                  && DEFAULT_ABI != MS_ABI))
          && GET_CODE (XEXP (fnaddr, 0)) == SYMBOL_REF
          && ! SYMBOL_REF_LOCAL_P (XEXP (fnaddr, 0)))
        {
-         use_reg (&use, gen_rtx_REG (Pmode, REAL_PIC_OFFSET_TABLE_REGNUM));
-         if (ix86_use_pseudo_pic_reg ())
-           emit_move_insn (gen_rtx_REG (Pmode, REAL_PIC_OFFSET_TABLE_REGNUM),
-                           pic_offset_table_rtx);
+         if (flag_plt)
+           {
+             use_reg (&use, gen_rtx_REG (Pmode, REAL_PIC_OFFSET_TABLE_REGNUM));
+             if (ix86_use_pseudo_pic_reg ())
+               emit_move_insn (gen_rtx_REG (Pmode,
+                                            REAL_PIC_OFFSET_TABLE_REGNUM),
+                               pic_offset_table_rtx);
+           }
+         else
+           fnaddr = gen_rtx_MEM (QImode,
+                                 legitimize_pic_address (XEXP (fnaddr, 0), 0));
        }
     }

diff --git a/gcc/config/i386/i386.opt b/gcc/config/i386/i386.opt
index 301430c..aacc668 100644
--- a/gcc/config/i386/i386.opt
+++ b/gcc/config/i386/i386.opt
@@ -572,6 +572,10 @@ mprefer-avx128
 Target Report Mask(PREFER_AVX128) SAVE
 Use 128-bit AVX instructions instead of 256-bit AVX instructions in the auto-vectorizer.

+mplt
+Target Report Var(flag_plt) Init(0)
+Use PLT for PIC calls (-mno-plt: load the address from GOT at call site)
+
 ;; ISA support

 m32

筆者は手元のGCC 4.7.3ツリーに似たような変更を加えて試してみたところ、以下のような出力を得られた。

foo:
    call    __x86.get_pc_thunk.cx
    addl    $_GLOBAL_OFFSET_TABLE_, %ecx
    movl    bar@GOT(%ecx), %eax
    jmp *%eax
__x86.get_pc_thunk.cx:
    movl    (%esp), %ecx
    ret

理想的な関数からはまだ遠いが、今の出力よりははるかにマシだ。

ドワンゴ広告

この記事はドワンゴ勤務中に書かれた。C++標準化委員会の文書集が公開されたが、これもすてがたかったのだ。

ドワンゴは本物のC++プログラマーを募集しています。

採用情報|株式会社ドワンゴ

CC BY-ND 4.0: Creative Commons — Attribution-NoDerivatives 4.0 International — CC BY-ND 4.0

2015-04-14

Ted Ts'oがEXT4で暗号化を実装

Encryption Support For EXT4 - Phoronix

GoogleのTed Ts'oが、EXT4に直接暗号化機能を実装したそうだ。

Linuxカーネルにおけるストレージの暗号化にはいくつか方法がある。ファイルシステム自体が暗号化機能を備えているというのはわかりやすいが、その他にも方法がある。

eCryptfsはEXT4のような通常のファイルシステムの上に、さらに暗号化されたファイルシステムのレイヤーをかぶせる方法だ。eCryptfsは、、ホームディレクトリ以下を暗号化するのによく使われている。

dm-cryptは、ブロックデバイスレベルの暗号化で、これは通常のファイルシステムの下で暗号化を行うレイヤーだ。

今回は、ファイルシステム自体に暗号化機能をもたせたわけだが、[PATCH 00/22] ext4 encryption patchesによると、Androidで使うことが主な目的らしい。

EXT4はどこまで魔改造する気だろうか。

ドワンゴ広告

この記事はドワンゴ勤務中に書かれた。そろそろC++標準化委員会の次の論文集が出てもよさそうな時期なのだが。

ドワンゴは本物のC++プログラマーを募集しています。

採用情報|株式会社ドワンゴ

CC BY-ND 4.0: Creative Commons — Attribution-NoDerivatives 4.0 International — CC BY-ND 4.0

2015-04-13

餃子

週末は餃子パーティをした。ひき肉を800g使って、餃子を100個以上作った。餃子の包み方が嫌でも上達した。

ところで最近、コンソメスープに凝っている。圧力鍋にコンソメを溶かしたお湯を沸かしブロッコリーをゆで、ベーコン、タマネギ、キャベツを炒めて投下、適当に煮るだけで素晴らしいスープになる。

今の悩みは、ブロッコリーが高いことだ。なぜブロッコリーはあんなにも高いのだろうか。タマネギや人参に比べて需要が低いから供給量も少なく高いのだろうか。しかし、たいていの青果店にはブロッコリーが陳列してある以上、供給量や需要の問題ではないのではないか。あるいは、ブロッコリーは外国産を輸入販売できないほど足が速いので高いのだろうか。

江添ボドゲ会@5月

江添ボドゲ会@5月 - connpass

上記告知通り、5月10日の日曜日に、妖怪ハウスでボドゲ会を行う。

今回もステーキ。手巻き寿司も作るかもしれない。

2015-04-09

libclangを使ったC++のセマンティックハイライトのVimプラグイン、color_coded

https://github.com/jeaye/color_coded

Color_Coded: Bringing LibClang Highlighting To Vim - Phoronix

color_codedは、libclangを利用して、C/C++/Objective-Cの文法を考慮したセマンティックハイライトをしてくれる。単なるキーワードや正規表現指定によるハイライト以上に優れたハイライトが得られる。

なお、Ubuntu 14.10のvimで試そうとしたところ、Vimのバージョンが古いとして動かなかった。

ドワンゴ広告

この記事はドワンゴ勤務中に書かれた。

ドワンゴ社内にはEmacsユーザーよりもVimユーザーの方が多いと思われる。

ドワンゴは本物のC++プログラマーを募集しています。

採用情報|株式会社ドワンゴ

CC BY-ND 4.0: Creative Commons — Attribution-NoDerivatives 4.0 International — CC BY-ND 4.0

500マイル以上離れた場所にメールが送れないのだが

http://web.mit.edu/jemorris/humor/500-miles

From: Trey Harris <trey@sage.org>

今から私が書く話は、起こりようのない問題についてだ。この話を広く一般に公開してしまうのは惜しい。というのも、いい酒の話のネタになるからだ。この物語は、退屈な詳細や問題を隠すために、多少事実を変えていて、物語を面白く脚色している。

数年前、私はキャンパスのメールシステムを保守する仕事をしていて、統計学部の学部長から電話を受けた。

「大学の外にメールを送るのに不具合が発生しているのだが」
「どんな問題でしょう?」と私はたずねた。
「500マイル以上メールを送れないのだよ」と学部長は説明した。

私はラテを吹き出した。「何だって?」

「ここから500マイル以上離れた場所にメールを送信できないのだよ」と学部長は繰り返した。「実際は、もう少しあるのだが。520マイルぐらいだろうか。とにかく、それ以上は無理なのだ」

「えっと・・・メールはそういう風には動かないのですが・・・普通」と、声の調子をできる限り抑える努力をしながら、私は言った。学部長と話すときは、興奮した声を出さないものだ。たとえそれが、統計学部であったとしてもだ。「なぜ500マイル以上メールを送れないと考えているのですか?」

「私の考えではないのだよ」と学部長は答えた。「つまり、この問題に気がついたのは、数日前のことだが・・・」

「数日も我慢したのですか?」と私は遮った。声が抑えられかった。「それで何日もメールが送れなかったのですか?」

「メールは送れたよ。ただその範囲が・・・」

「・・・500マイルというわけですね」と私は続けた。「わかりました。しかし、なぜもっと早く言ってくれなかったのです」

「うむ、さっきまで現状を把握するために十分なデータを収集できずにいたわけであるからして」。なるほど、彼は「統計」部門の学部長である。「とにかく、私は地球統計学者に問題を見てくれるよう頼んで・・・」

「地球統計学者・・・」

「そうだ。彼女は、我々がメールを送れる距離は500マイルをわずかに超える程度の半径であると地図上に図示してくれた。その半径内にも送れない場所はいくつかあり、あるいは散発的に送れるところもあるが、この半径の外には絶対に送れないのだ」

「なるほど」、と私は言った。そして頭を抱えた。「いつ問題が起こったのですか? 数日前とさっき言っていましたが、何かシステムに変更はありましたか?」

「そうだな。業者がやってきてサーバーにパッチをあててリブートした。業者に連絡してみたが、メールシステムには触っていないとのことだった」

「わかりました。見てみます。後ほどまた連絡します」と私は言った。自分でも会話について行けたのが信じられないくらいだ。今日はエイプリルフールではない。私は誰かにジョークの借りがあったかどうか思い出そうと試みた。

私は、その学部のサーバーにログインして、いくつかのテストメールを送ってみた。ここはノースカロライナにある研究室で、私のメールアカウント宛のメールは問題なく届いた。リッチモンドとアトランタとワシントンに送ったメールも同様だ。プリンストン(400マイル)に送ったメールも届いた。

だが、メンフィス(600マイル)にメールを送ろうとすると、失敗した。ボストン、失敗。デトロイト、失敗。私はアドレス帳を引っ張りだして、範囲を絞り込んでみた。ニューヨーク(420マイル)は届いた。プロビデンス(580マイル)は失敗だ。

果たして私は正気で物を見ているのだろうか。私はノースカロライナに住むが、ISPはシアトルにある友人にメールを送った。ありがたいことに、失敗した。もし、問題がメールサーバーではなく、人間の受け取り手の地理に左右されるのであれば、私は泣き出していたことだろう。

信じられないことに、報告された問題は事実であり、再現性があることが確認できた。私はsendmail.cfファイルを確認した。通常通りのように見える。実際、とても見覚えがある。

私はホームディレクトリにあるsendmail.cfとdiffを取ってみた。改変されていない。私が書いた通りのsendmail.cfだ。私は"FAIL_MAIL_OVER_500_MILES"のようなオプションを有効にしていないことは確かだ。わからなくなってしまったので、私はSMTPポートにtelnetしてみた。サーバーはSunOS sendmailバナーを返してきた。

ちょっとまてよ・・・SunOS sendmailバナーだと? その当時、SunはまだOSにSendmail 5をつけて出荷していた。すでにSendmail 8が安定しているというのにもかかわらずだ。良きシステム管理者である私は、Sendmail 8を使っていた。またもや良きシステム管理者である私は、sendmail 5の暗号のような記号文字ではなく、sendmail 8に追加されたわかりやすいオプションや変数名を使ってsendmail.cfを書いていた。

パズルはまさに解けた。そして、私は再び、もはや冷えてしまったラテを吹き出した。業者が「サーバーをパッチした」時、どうやらSunOSのバージョンをアップグレードしたようだ。そうするにあたって、sendmailがダウングレードされてしまったのだ。アップグレードはバージョン違いのsendmail.cfをそのまま残したのだろう。

たまたまだが、Sunの出荷したSendmail 5は、改変がなされていて、Sendmail 8のsendmail.cfに対応していて、ほとんどのルールはそのまま残った。しかし、新しい長い設定オプションは、ゴミとみなされて、無視された。そして、sendmailのバイナリはほとんどのオプションにデフォルト値を指定されていなかったつまり、sendmail.cfに該当な設定がみつからないために、ゼロに設定されていたのだ。

ゼロに設定されていたオプションの一つに、リモートSMTPサーバーに接続するタイムアウトがあった。ちょっと実験してみた結果、この環境と通常の負荷におけるゼロタイムアウトは、3ミリ秒を少し上回ると、切断されるようだ。

当時のキャンパスのネットワークの変わった機能として、100%スイッチされていたということがある。外向きのパケットはPOPを叩いて相手側のルーターに到達するまで、ルーターのディレイがなくなる。そこで、負荷の少ない近辺のネットワークのリモートホストへの接続は、ルーターのディレイよりも、光速の到達時間に左右される。

やや期待しながら、私はシェルを以下のように打ち込んだ。

$ units
1311 units, 63 prefixes

You have: 3 millilightseconds
You want: miles
        * 558.84719
        / 0.0017893979

「500マイル、実際はもう少しあるのだが」

Tery Harris

面白い話だ。ところで、unitsというコマンドを初めて知ったが、便利なものだ。

ドワンゴ広告

この記事はドワンゴ社内で書かれた。そろそろ次のC++標準化委員会の文書集が公開されても良さそうなものだが。

ドワンゴは本物のC++プログラマーを募集しています。

採用情報|株式会社ドワンゴ

CC BY-ND 4.0: Creative Commons — Attribution-NoDerivatives 4.0 International — CC BY-ND 4.0

2015-04-08

gitの10周年を記念したLinus Torvalsへのインタビューの翻訳

10 Years of Git: An Interview with Git Creator Linus Torvalds | Linux.com

gitの10週年を記念して、リーナス・トーバルズがインタビューに答えている。以下はその翻訳である。

なぜGitを作ったのか?

トーバルズ:俺はソース管理ツールなんて作りたくなかったし、コンピューターの業界において最も興味がないものだと見なしていた(データベースは別だが)。それにソース管理ツールなんてどれも嫌いだった。しかし、BitKeeperがやってきてからというもの、ソース管理に対する見方が変わったね。BitKeeperは大抵のことを正しく行っていた。レポジトリのローカルコピーがあることと、分散マージはでかかった。分散ソース管理の何がいいかというと、ソース管理ツールの問題を吹っ飛ばせることだ。「誰が変更を行えるか」といった政治問題があるが、Bitkeeperは、全員に自前のソースレポジトリをもたせれば、そんな政治問題なんか消し去ることができるということを示してくれた。だた、BitKeeperにも問題はあった。一部の技術的な設計も問題(リネームが辛かった)だったが、最大の問題点は、オープンソースではないために、使いたがらない人間が多くいたということだ。BitKeeperはオープンソースプロジェクトには無料で使えたため、何人かのコアメンテナーはBitKeeperを使っていたものの、全員が使うには至らなかった。カーネル開発には役だったが、そういう頭の痛い問題はあった。

そこに、Tridge(Andrew Tridgell)が、(それなりにシンプルな)BitKeeperのプロトコルをリバースエンジニアリングし始めた。これはBitKeeperの利用規約に反するものであった。俺は数週間(数カ月? それぐらいはかかったように感じた)、TridgeとLarry McVoyとの間を取り持っていたが、どうもうまくいきそうになかった。その時点で、俺はもうBitKeeperは使えないと判断した。とはいえ、俺はもうBitKeeper以前の時代に戻りたくはなかった。残念ながら当時、分散型のソース管理ツールはいくつかあったが、どれもうまくやっていなかった。当時存在したツールはどれも、俺の要求するパフォーマンスに全然追いついていないのだ。それに、コード品質や全体のワークフローのことも気にしていたので、自分で書くことにした。

どうやって書いたのか? 週末を徹夜して書いたのか、あるいは昼間だけ作業したのか。

トーバルズ:いやwww、gitのソースコードレポジトリの中に、実際に記録されているよ。最初の一日分はないがね。gitでgitのコミットをする、「セルフホスト」をするのに一日ほどかかったので、最初の一日かそこらは謎だ。だが、それ以外は全部残っている。作業は日中に行われたが、真夜中のコミットや、午前2時のコミットもいくつかある。すぐに形になったというのはとてもおもしろいことだ。gitツリーに対する最初のコミットは、そんなにコードがない。しかし、基本的なことはすでにやれていた。自分自身をコミットするには十分なほどに。問題はコード量ではなくて、どのようにデータを扱うかだった。

そういうわけで、10日間かそこらでモノにはなったものの(その時点で、俺は最初のカーネルのコミットをgitで行った)、素早くコードを書き散らしたというわけではないということは言っておきたい。初期の実際のコード量は、かなり少ない。基本的な考え方が正しいかどうかにかかっている。開発を始める前からそのアイディアについて考察していたわけだ。他のツールの問題は知っていた。何を避けるべきかも知っていた。

結果は期待通りであったか? 現在の状況は推定通りか? なにか限界はあるか?

gitには満足している。カーネルに対して実によく動くし、いまだに期待通りの働きをしてくれる。俺にとって興味深かったのは、実に速く他の多くのプロジェクトを取り込んでいったかということだ。驚くほど早かった。ソース管理システムを変更するには様々な障害があるものだ。CVSやRCSが長年生き残っているのを見ても明らかだ。しかし、gitが取り込んでしまった。

なぜこんなにひろく受け入れられたと思うか?

トーバルズ:俺がソース管理ツールを嫌っていた原因となる問題を、他の皆も感じていたのだろう。いくつかの些細な問題を解決しようと試みるプロジェクトはあったものの、根本的な問題に取り組むgitのようなものがなかったのだろう。「分散型」の部分の重要性を認識していない者でも(結構な者が反対しているが)、それにより簡単で信頼できるバックアップが可能になり、中央レポジトリに対する書き込みアクセス権などという政治問題に関わることなく、全員が自前のプライベートなテストレポジトリが持てると分れば、もう後戻りしたいと思うものなどいやしない。

Gitは一生続くのだろうか。あるいは、今後10年で別のリビジョン管理システムが現れるのだろうか。それを書くのはあなたか。

トーバルズ:それを書くのは俺ではないよ。たぶん、10年以内になにか新しい物が出てくるだろう。だが、それはだいたいgitに似ているだろう。gitがすべてを正しく行っているためではない。gitは基本的な問題を正しく行っているのだ。既存の他のソース管理ツールがどれもできていなかったことだ。

自画自賛ではないよ^^;

なぜGitはLinuxに対してうまく動くのか。

トーバルズ:そりゃあ、gitは我々のワークフローに対して設計されたからで、設計の一部だったのだ。すでに「分散型」の部分は何度も言ったが、何度も繰り返す価値があるものだ。また、Linuxのような大きめのプロジェクトに対しても十分に効率的であるように設計されている。そして、git以前ならば「難しい」と感じる作業を想定して設計されている。というのも、その作業は他ならぬこの俺が毎日行うことだからだ。

例えばだ。「マージ」という概念は、大抵のソース管理ツールではとても頭が痛くて難しい作業だ。マージのための計画を練る必要がある。とても重要だからだ。それは俺にとって許容できないことだ。なぜならば、俺はマージ期間中に一日あたり何十ものマージを普通に行うからだ。それも、マージ自体が面倒なのではなく、結果をテストすることのほうが難しくあるべきだ。gitでは、マージはたったの数秒のことだ、マージよりも、マージの説明メッセージを書く方に時間がかかるべきなのだ。

つまり、gitは基本的に俺の要求を満たすために設計されて、結果はご覧のとおりだ。

Gitは超絶天才な人間だけのものだと言う者がいる。Andrew Mortonでさえ、Gitは、「自分が思っているよりも頭が悪いと思わせるために意図的に設計されている」と言っている。これに対してどう思うか。

トーバルズ:それはかつては正しかったが、もはや違う。そういう風に思う理由はいくつかあるが、今となっては、ひとつしか残っていない。残っている一つの理由は簡単なものだ。「あることをやる方法がたくさんある」

gitでは実に多くのことが行える。多くの、こう行うべきだというルールは、技術的な制約によるものではなくて、他人と協調作業をするときにそうしたほうがよいというだけのことだ。つまり、Gitはとても強力なツール群であって、最初は圧倒されるのみならず、あること、あるいは似たようなことを、複数の異なる方法で行うことができて、どれも「動く」ということだ。一般に、gitを学ぶ方法としては、おそらく、最初は基本的なことしかせずに、十分に慣れて、基本に自信がつくまで、他のことには手を出さないことだ。

gitがとても複雑であると思われていることには、歴史的な経緯がある。理由の一つに、かつては複雑だったということがある。カーネル開発のためにgitを最初期に使った者は、作業をするために、とても基本的なスクリプト集と格闘しなければならなかった。労力はすべて、基本的な技術を動かすことに使われていて、使い方を簡単にすることには、ほとんど労力が割かれていなかった。そのため、gitは当然の結果として、自分が何をしているのか完璧に理解しなければならないという印象を得てしまった。しかし、それは最初の半年か一年ぐらいのもので、今は違う。

別の大きな理由としては、gitを難しいと感じる理由は、gitは全然別物だからだ。CVSに10年や20年慣れ親しんだ者がいる。gitはCVSではない。全く似通っていない。考え方からして違う。コマンドが違う。gitはCVSに似せようとしたことなどない。実際は真逆だ。もしCVSのようなシステムを長年使っていたのであれば、gitは複雑で、不必要に変わっているように見えるだろう。もはやリビジョン番号というものはない。なぜgitはCVSにあったように、"1.3.1"のような番号と、便利なインクリメントされる番号がないのだ? いったいこの不思議で恐ろしい40文字のHEX番号は何なのだ? と。

だが、gitは「不必要に変わっている」のではない。変わっている部分は必要なのだ。一部の人間が複雑であると考えているだけなのだ。何故ならば、彼らは全く別の経験をすでに持っているからだ。「CVS経験」は消え去りつつある。今、多くのプログラマーはCVSなど使ったことがなく、CVSの方法をとてもわかりにくいと考えるだろう。何故ならば、彼らはgitを先に学んだからだ。

Linuxカーネル開発の速度は、Gitがなくても現在の速度に成長できたと思うか。その理由はなぜか。

トーバルズ:まあ、「gitがなくても」成長したとは思う。しかし、それは誰か別の人間が、gitの同等品、分散型ソース管理システムでgitと同程度に効率的であるものを書く必要があっただろう。gitのようなものを我々は必要としていた。

GitHubに対する最近の考えは。

トーバルズ:Githubは優秀なホスティングサービスだ。特に反対意見はないよ。GitHubに対する不満は、開発プラットフォームとしてのものだ。コミット、pull request、issue管理などなどが、GitHubではうまくやれない。うまいどころではなくまるで下手だ。カーネルのような開発に対しては、あまりにも制限が多すぎる。

これは、カーネルの開発体制にもよろうが、GitHubのインターフェースが、悪い方法を推奨しているせいでもある。GitHubに対するコミットは悪いコミットメッセージが書かれているなど。GitHubのWebインターフェースは、悪い行動をするように推奨しているせいである。GitHubの連中は一部を修正したので、多分多少はマシになっただろうが、Linuxカーネルの開発には適切ではない。

GitやGitHubを使った最も興味深いと思うことは?

トーバルズ:新しいプロジェクトを始めるのがこんなにも簡単になったことに満足している。かつて、プロジェクトホスティングはとても面倒だったが、gitとGitHubのおかげで、適当な小さなプロジェクトを始めるのがとても簡単になった。プロジェクトが何であるかは重要ではない。重要なのは、誰でもできるようになったということだ。

いま温めているプロジェクトはあるか。何か素晴らしい、ソフトウェア開発の業界を何年も牛耳れるようなソフトウェアプロジェクトはあるか。

トーバルズ:何もない。何かあったら知らせるよ。

たしかに、gitは素晴らしい。筆者にもバージョン管理ツールの重要性を認識させてくれた。筆者は、初めてバージョン管理ツールを使った時、まだgitは存在しておらず、Subversionが最も有名だった。Subversionは実にクソだったので、筆者は、バージョン管理ツールの重要性を、最近まで認識しないままであった。gitを使うのも数年遅れてしまった。gitは一瞬で気に入った。もっと早く手を出しておくのだったの公開している。

週末

週末の土曜日はボドゲ会を行った。

今回は3kgのサーロインブロックを買い込んで、ステーキを焼いた。

ステーキの焼き方は回数をこなさないとうまくならないと思うので、今後もステーキを焼いていきたい。

日曜日には代々木公園に花見に行った。雨の中の花見であったが、むしろ、なかなかに印象深い思い出となることだろう。

月曜日には、秋パンに行って、4級をひとつ登った。

2015-04-02

江添ボドゲ会@4月

江添ボドゲ会@4月 詳細

毎月第一週の土曜日は妖怪ハウスでボドゲ会をしようと計画している。ボードゲーム好きの人間は是非ともボドゲをしにきてもらいたい。

4月のボドゲ会は、4月4日の土曜日に、妖怪ハウスで21時頃まで行われる。

翌日はシェアハウス界隈で大掛かりな花見が行われるらしいので、今回は徹夜なし。

また、今回はカレーではなく、盛大にステーキを焼く予定だ。サーロインブロックを3kg買ってきた。カレーより費用が倍以上かかっているので、参加費を取りたいところだ。ステーキは結構お高い。

2015-04-01

結婚することになった

突然だが、結婚をすることになった。年内に籍を入れることを考えている。あまりに唐突に決まったため、周囲からも本人も、ノリで結婚することになったと言っている。

「契約結婚しましょう」

と女は言った。その言葉は江添に対して発せられたようではあるが、どうにも意図が曖昧である。女は江添に対して結婚を申し込んでいるようにも聞こえ、あるいは、江添は誰かと結婚をすべきであると言っているようにも聞こえる。前者の解釈が誤りである場合、江添は大変なうぬぼれをしたことになる。前者かどうかを確かめるには、ここでわざとらしいにやけ顔を作り、おどけた声で、「私と?」などと答えればよいだろう。たとえ解釈間違いであったとしても、うぬぼれやを気取った冗談で済む対応だ。

ここは筆者の住むシェアハウスのリビングであり、たまたま何人かの男女が集まって、軽く酒を飲みながら話をしている。となりでは、いかにも恋愛経験の豊富そうな大人の女が、たまたまその場に居合わせた若いイケメンを口説いていた。その口説き方は極めて滑稽で、まるでマンガにでも出てきそうな光景である。この大人の女は、相当に退屈だとみえる。口説かれているイケメンは、やんわりと断りながら、しかし話は続けているようだ。まことに精神的にもイケメンである。

場の雰囲気は、恋愛観や結婚観を語り合う方向に進んでいたので、そういう言葉が出てくることは自然な流れである。江添には恋愛というものがよくわからない。無論、これまで恋愛の経験がないまま歳のみいたずらに取ってしまったということもあるだろう。しかし、やはり恋愛が理解できないために、恋愛経験がなかったのではないかとも思う。

まず江添には、誰か特定の人を好きになるということがよくわからない。一体、人はどうしてある個人を好きになるのだろうか。容姿だろうか、金だろうか、社会的地位だろうか、はたまた価値観の一致だろうか。

江添には容姿の良し悪しがうまく評価できない。確かに、不健康的にやせ細っていたり太っていたりする人間は容姿が劣っているとは思う。しかし、容姿が優れているとはどういうことをいうのだろうか。世間の評価を参考にすると、まず年齢が若く、痩せていて、髪が長いというのが、美男美女に共通する項目のようだ。美女の場合、厚化粧という項目も加わる。また、画像の場合は、多くの修正を加えている画像が、容姿が優れていると評価されているようだ。

仮に容姿が優れていたとして、それがどうして恋愛や結婚につながるのであろうか。容姿というものは経年劣化するものであり、容姿を元に相手を選ぶとすれば、数年おきに相手を取り替えねばならないだろう。

金目当てというのは、まったく理解できない。社会的地位もそうだ。価値観の一致は一番理解できるが、それでも他人の価値観が完全に一致するわけがない。

ただし、結婚と育児となると話は別だ。もし育児をするならば、やはり社会制度が結婚を前提に設計されている都合上、結婚しておいたほうが何かと便利であろう。また、育児には年齢という生物学的な限界がある。そのため、10代後半から20代のうちに結婚出産育児をしておくべきだろう。40や50になって育児というのは極めて難しいように思われる。そのため、結婚をもしするのであれば、そろそろ年齢的に限界だろう。

と、恋愛観や結婚観について問われて答えていたところでの、この言葉だ。

「契約結婚しましょう」

素知らぬ顔で言われたので、特に江添に対して話したわけでもないように思う。

そもそも契約結婚とは何か。甲と乙は結婚をする、から始まり、期間や離婚の条件まで事前に契約によって取り決めておくことだろうか。

「そうです」

この女は、シェアハウス界隈の人間で、自身もやはり、とあるシェアハウスに住んでいる女だ。かなりのフェミニストで個人主義者だ。江添が東京に出てきた直後からシェアハウス界隈のつながりで身近にいた人間ではある。

ともかく、その日はこの女の考える理想の契約結婚について話をして終わった。女は、恋愛とか人を好きになるということは分からず、結婚のリスクも最小化したいなどという考えを持っているようであった。。江添に対しての話だったようにも解釈できるし、単に契約して結婚するという一般的な価値観の話のようにも解釈できる、なんとも曖昧な話であった。

後日、2kgのサーロインブロックが贈られてきたので、大いに肉を食べる会を催したのだが、満足に肉を食べて参加者もほぼ帰った後で、やはりまた同じ話になった。

「契約結婚してください」

と言って、女は頭を下げた。その場には男二人女二人しかおらず、もう一人の男に向かって言ったのではないことは明白である。江添は特に何の考えも持たなかった。断るほどの悪条件もなさそうではあるが、受けるほどの好条件も見当たらない。

その場にいたもう一人の女が、二人の間に取り行って仲人となり、質問をして、条件をまとめていった。その結果、お互い、特に相手に求める要求もなく、またこれだけは許せない条件というのも、私はタバコで、相手は大麻(何でも、昔の男が大麻を吸っていたことが判明したので別れたのがトラウマなのだとか)だ。

お互い個人主義で、たとえ結婚相手であろうとも、自分の時間や環境を犠牲にしたくはないという考え方なので、自分の権利を守るために相互に干渉することがなく、案外うまく行くのかもしれないとも思った。

結婚しても、お互いにシェアハウス暮らしが好きなので、しばらくはたまに会うぐらいで同居しないだろうし、お互いに金銭的、精神的に独立しているので、いい相手に恵まれたのかもしれない。

もちろん、先のことはわからないが。

2015-03-30

コンパイラーを負かす

roguelazer's website: beating the compiler

なかなか面白かったので翻訳して紹介する。

たとえば、97%の場合において、僅かな効率など忘れるべきである。。早すぎる最適化は諸悪の根源である。とはいえ、残りの重要な3%の機会を逃すべからず。

-- Donald Knuth

計測せよ。計測するまで速度の最適化を施してはならぬ。たとえ計測したにせよ、一部のコードが残りを圧倒するまではまだ最適化してはならぬ。

Rob Pike

最新のWebサービスを主体とした技術の業界に長年浸かった我々は、パフォーマンスの問題を忘れがちである。SQLAlchemy ORMの中で行うリクエスト一つが8,9秒かかる中で、関数呼び出しひとつを3ミリ秒最適化したところで何になるというのか。とはいえ、時にはそのような最適化スキルを養っておくのもいいことだ。今回は、ある簡単な課題を最適化して、やっつけ仕事よりどれだけよくできるのかを見ていくことにする。普通はやらないだろうが。

課題

今回の課題はとても簡単なものだ。n個の整数を合計せよ。この課題を公平にするために、整数値は32bit符号付きintに収まるものとし、その合計も32bit符号付きintに収まるものとする。

もちろん、我々は最適化をするのであるから、パフォーマンスの計測には注意を払わねばならぬ。通常なら、我々は単なる時刻を使う(Pythonのtimeitモジュールなどで計測するものだ)。ただし、time.clock()をPythonからただ呼び出すだけで、30マイクロものコストがかかる。おやおや、これはあまりよくないぞ。言い換えてみよう。time.clock()をPythonで呼び出すと、最近のCPUでは60,000サイクル消費するのだ。かわりに、筆者はCPUサイクルをカウントする手法を用いるものとする。これには問題もある(特に、周波数スケーリングが有効になっていたり、複数のコアがあってOSのスケジューラーがリスケジュールを行いたくなった場合など)。だが、これはCPUのパフォーマンスの計測として、最も正確でオーバーヘッドの最小な単位である。Intel x86では、RDTSC命令で実装できる(より具体的に言うと、シリアライズされるRDTSCP命令だ)。筆者はこれを使いやすいようにPythonライブラリとしてラップした。

これがテスト計画だ。

  1. n個のされた範囲(0,\(2^{12}\))の整数値のデータファイルを乱数で生成する。固定値のseedで乱数生成する。
  2. テストは、まずデータをすべて読み込む
  3. テストは、RDTSC命令でサイクル数を計測する
  4. テストは、少なくとも100回実行する

この記事のテストは、Intel Core i7-4850HQを搭載した2013年の15インチのrMBPで行われた。また、Intel Core i5-4308Uを搭載した2014年の13インチ rMBPでも実行して、結果がまともであることを確かめた。

最初の試み:Python

Pythonは好きだ。簡単でダックタイピングな言語で、オブジェクト指向と関数型の便利な機能がたくさんあるプログラミング言語だ。ファイルからデータを読み込むのは簡単だ。

import array

data = array.array("i")
with open(sys.argv[1], "r") as f:
    for line in f:
        data.append(int(line.strip()))

最もやっつけな解決法がこれだ。

def sum_naive_python():
    result = 0
    for i in data:
        result += i
    return result

コードはあまりに自明だ。美しい。このコードは、筆者のコンピューターでは、50,000,000 ± 3%サイクルかかる。これは約24ミリ秒だ。だが、筆者は絶対時間についてはそれほど気にしていない。問題なのはスケールするかどうかだ。そこで筆者が使う指標は、1サイクルあたり何個処理できるかだ。理想的な単一命令実行の非スーパースカラーで完璧な分岐予測を持つCPUでは、サイクルあたり1.0個処理できるはずだ。この指標では、最初の試みは悲惨だ。0.01個/サイクルだ。整数の個数辺りのスケールは以下の通り。

naive python

とても簡単な改良方法がある。Pythonにはネイティブのsum関数があり、これはCで実装されていて、もっと速いはずだ。次のテスト関数はこうだ。

def sum_native_python():
    return sum(data)

これはマシになった。12,000,000 ± 2%サイクル、0.04個/サイクルだ。まだ悲惨だが、4倍速い。

native python

Python用のイケてる数値ライブラリを使うとどうなるだろうか。numpyはどうだ。このライブラリは最低聞かされたFORTRANコードを呼んでくれる。すげー速いはずだ。

numpy

ダメだ。NumPyのオーバーヘッドで相殺されるようだ。0.004個/サイクルしかでない(ネイティブsum関数を組み込みのarray型に適用する場合の10分の1だ)

より高みへ:C

Pythonでそれ以上速くできないのであれば、Cに変えろというのは、Pythonでよくあるパターンだ。Cで同様のテスト環境を用意した。ファイルからヒープに確保されたintの配列に読み込み(少々長いのでわざわざコードはみせない)。この数値群を足し合わせるC関数の最初の実験がこれだ。

int sum_simple(int* vec, size_t vecsize)
{
    int res = 0;
    for (int i = 0; i < vecsize; ++i) {
        res += vec[i];
    }
    return res;
}

最初のPythonコードと同じくらい綺麗だ。さて結果は?

3,423,072サイクル(~1.5ミリ秒)、0.162個/サイクルだ。これはPythonより10倍速い。

注意:Apple LLVM 6.0 / clang-600.0.56 -O0でコンパイルした。

C O0 Simple

もっと速くできるだろうか。

そういえば昔、ループの手動展開は高パフォーマンスであると聞いたことがある。ループを展開すると、ループの最後のジャンプのオーバーヘッドを隠して、分岐予測の為事を代わりに引き受けたことになる。4回展開版が以下だ。

int sum_unroll_4(int* vec, size_t vecsize)
{
    int res = 0;
    int i;
    for (i = 0; i < vecsize - 3; i+=4) {
        res += vec[i];
        res += vec[i+1];
        res += vec[i+2];
        res += vec[i+3];
    }
    for (; i < vecsize; ++i) {
        res += vec[i];
    }
    return res;
}

展開されたループのパフォーマンスはどうか。ほぼ同じだ(0.190個/サイクル)

C O0 Unroll 4

ひょっとしたら展開数が足りないのではないか。では、8回ループ展開をしてみよう。

int sum_unroll_8(int* vec, size_t vecsize)
{
    int res = 0;
    int i;
    for (i = 0; i < vecsize; i+=8) {
        res += vec[i];
        res += vec[i+1];
        res += vec[i+2];
        res += vec[i+3];
        res += vec[i+4];
        res += vec[i+5];
        res += vec[i+6];
        res += vec[i+7];
    }
    for (; i < vecsize; ++i) {
        res += vec[i];
    }
    return res;
}

やれやれ、0.192 PPC(Points Per Cycles)だ

C O0 Unroll 8

どうやら、最近のプロセッサーの分岐予測というのはめちゃめちゃ優秀らしい。ループ展開は、とても短いループか、あるいは何らかの理由で分岐予測が最悪の結果になるループには有効だが、まず時間の無駄というものだ。

最適化コンパイラー

Cを書いた経験がある読者は、ここで疑問に思っていることだろう。なぜ筆者は-O0でコンパイルしているのだ?、と。最新のコンパイラーは何十年もの最適化手法が積み上げられており、筆者ごときが書いた程度のものは余裕でこすことができるだろう、と。

実にそのとおりだ。-O3を使うと、精製されたコードは他の方法を消し炭すら残らないほどの煙にしてふっ飛ばしてしまう。4.462個/サイクルだ。

C O3 Simple

待てよ・・・1個/サイクル以上だと? どうやったらそんなことが可能なのだ。

さて、読者よ。プロセッサーの歴史のお勉強の時間だ。

複数命令発行、スーパースカラー、アウトオブオーダー、ベクトル計算機

今は昔、コンピュータープロセッサーというものは、とても簡単だった。プロセッサーは命令を一つづつ読み込み、1ないしは数サイクルを使って処理し、結果を吐き出すものだった。

simple architecture

これは、1965年にCray 6600がリリースされる以前の話で、デスクトップでも1995年にP6 Pentium Proが発売されたことで過去の話になった。この2つのプロセッサーは、スーパースカラーアーキテクチャの代表例だ。

簡単に言うと、スーパースカラーアーキテクチャーとは、複数の実行ユニット(大抵目的別に特化されている。たとえば2つの汎用数値演算ユニットといくつかのアドレス計算ユニットとひとつのSIMDユニットとひとつのFPUユニットなど)があり、複数の命令を並列に実行するために別々の実行ユニットに振り分けることができる。

superscalar architecture

筆者の持っているIntel Haswell CPUのアーキテクチャーは、以下のようになっている。

haswell

つまり、複数の命令を同時に実行できるということで、1.0加算/サイクルというのは、遅すぎるということだ。

最後の要素は、謎めいた略語であるSIMD(Single Instruction, Multiple Data)だ。SIMD命令はベクトル命令とも呼ばれていて、演算を複数のデータに対して一度に適用できる。例えば、IntelのSSEユニットでは、演算を4つの32bit整数値か、2つの64bit浮動小数点数に対して適用できる。

x86には多数のSIMDユニットがある。MMX拡張から始まり、SSE, SSE2, SSE3, SSE4.1, AVX, AVX2、そしてまだリリースされていないAVX-512。ここではその技術的詳細については述べないが、詳しく知りたければ、ここにすべて載っている

Haswellプロセッサーでは、2つのベクトルInt ALUがある、理論上、2つのAVX2処理を並列に行える。それぞれが8個の32bit整数値を同時に処理できる。Intel 64 and IA32 Architectures Optimization Reference Manualによれば、それぞれの命令は1サイクルのレイテンシーがあり、つまり理論上のPPCは16.0になる。だいぶ高い。実際、これは不可能なほどに高い。1サイクルごとに16個32bit整数値をCPUに転送するには、64バイト/サイクルのメモリースループットが必要であり、これは、筆者のCPUに当てはめれば、147GB/sのメモリースループットになる。筆者の環境のPC3-12800 RAMは、最大一本あたり12.8GB/s(トータルで25.6GB/s)であり、これはたったの11バイト/サイクルだ。つまり、メモリの最大のスループットを考えれば、2.75PPCをすこし上回るほどだ。これ以上高いスコアは、筆者のCPU上の128MBものeDRAM L4キャッシュに残っていたものである。

コンパイラーを負かす

さて、コンパイラーは負かせるか? 4.462 PPCより上を叩き出せるか。もちろん無理だが、データが広帯域のL3やL4キャッシュに乗るほど小さければ、まあ、可能ではある。いかがその例だ。PPCは4.808だ。

int sum_avx2_unroll4(int* vec, size_t vecsize)
{
    unsigned int vec_out[8] __attribute__ ((aligned(64)));
    size_t processed = 0;
    asm(
        "XORQ %%rax,%%rax\n\t"
        "VPXOR %%ymm0,%%ymm0,%%ymm0\n\t"
        "VPXOR %%ymm5,%%ymm5,%%ymm5\n\t"
        "VPXOR %%ymm6,%%ymm6,%%ymm6\n\t"
        "VPXOR %%ymm7,%%ymm7,%%ymm7\n\t"
        "VPXOR %%ymm8,%%ymm8,%%ymm8\n\t"
        "top%=:\n\t"
        "VMOVDQA (%1, %%rax, 4),%%ymm1\n\t"
        "VMOVDQA 32(%1, %%rax, 4),%%ymm2\n\t"
        "VMOVDQA 64(%1, %%rax, 4),%%ymm3\n\t"
        "VMOVDQA 96(%1, %%rax, 4),%%ymm4\n\t"
        "ADDQ $32,%%rax\n\t"
        "VPADDD %%ymm1,%%ymm5,%%ymm5\n\t"
        "VPADDD %%ymm2,%%ymm6,%%ymm6\n\t"
        "VPADDD %%ymm3,%%ymm7,%%ymm7\n\t"
        "VPADDD %%ymm4,%%ymm8,%%ymm8\n\t"
        "CMP %2,%%rax\n\t"
        "JL top%=\n\t"
        "VPADDD %%ymm5,%%ymm0,%%ymm0\n\t"
        "VPADDD %%ymm6,%%ymm0,%%ymm0\n\t"
        "VPADDD %%ymm7,%%ymm0,%%ymm0\n\t"
        "VPADDD %%ymm8,%%ymm0,%%ymm0\n\t"
        "VMOVDQA %%ymm0, 0(%3)\n\t"
        "MOVQ %%rax, %0\n\t"
        : "=r"(processed)
        : "r"(vec), "r"(vecsize), "r"(vec_out)
        : "rax", "ymm0", "ymm1", "ymm2", "ymm3", "ymm4", "ymm5", "ymm6", "ymm7", "ymm8", "cc"
    );
    int res = 0;
    for (int i = 0; i < 8 ; ++i) {
        res += vec_out[i];
    }
    for (; processed < vecsize; ++processed) {
        res += vec[processed];
    }
    return res;
}

これは4回展開したAVX2ベクトル化版だ。また、独立した複数の加算レジスターに合計させることにより、IPCを僅かに稼いでいる。

C O0 AVX2 Unroll4

本当にコンパイラーを打ち負かしたのだろうか。PPCの違いを比べてみる。

comparison

技術的に言えば、筆者のコードは負けることはない。参考に、これがコンパイラーの生成するアセンブリだ。


## BB#2:                                ## %vector.body.preheader
    pxor    %xmm0, %xmm0
    xorl    %ecx, %ecx
    pxor    %xmm1, %xmm1
    .align  4, 0x90
LBB8_3:                                 ## %vector.body
                                        ## =>This Inner Loop Header: Depth=1
    movdqa  %xmm1, %xmm2
    movdqa  %xmm0, %xmm3
    movdqu  (%rdi,%rcx,4), %xmm0
    movdqu  16(%rdi,%rcx,4), %xmm1
    paddd   %xmm3, %xmm0
    paddd   %xmm2, %xmm1
    addq    $8, %rcx
    cmpq    %rcx, %rax
    jne LBB8_3
## BB#4:
    movq    %rax, %rcx
LBB8_5:                                 ## %middle.block
    paddd   %xmm1, %xmm0
    movdqa  %xmm0, %xmm1
    movhlps %xmm1, %xmm1            ## xmm1 = xmm1[1,1]
    paddd   %xmm0, %xmm1
    phaddd  %xmm1, %xmm1
    movd    %xmm1, %eax
    cmpq    %rsi, %rcx
    je  LBB8_8
## BB#6:                                ## %.lr.ph.preheader13
    leaq    (%rdi,%rcx,4), %rdx
    subq    %rcx, %rsi
    .align  4, 0x90
LBB8_7:                                 ## %.lr.ph
                                        ## =>This Inner Loop Header: Depth=1
    addl    (%rdx), %eax
    addq    $4, %rdx
    decq    %rsi
    jne LBB8_7

コンパイラーは2回展開のSSE2レジスターを使う選択をしている。いえい。

結論

Pythonや、その他の動的型付け言語は、十分に速いかもしれないが、速いことは常にいいことだ。Cのような低級言語で速度を稼げるかもしれないし、アセンブリを手書きすることでもう少し上を行けるかもしれないが、コンパイラーを信頼したほうがいい。しかし、もし大量の12bitの数値を限りなく高速に足し合わせたいのであれば、まあ、これが答えだ。

comparison

Algorithm Cycles for 500k Points Points per Cycle
sum_numpy110681288 ± 0% Cycles0.005 ± 0% PPC
sum_naive_python48255863 ± 1% Cycles0.010 ± 1% PPC
sum_native_python12063768 ± 2% Cycles0.041 ± 2% PPC
-O0 simple3095247 ± 1% Cycles0.162 ± 1% PPC
-O2 simple2889994 ± 1% Cycles0.173 ± 1% PPC
-O0 unroll_42630305 ± 2% Cycles0.190 ± 2% PPC
-O0 unroll_82597596 ± 0% Cycles0.192 ± 0% PPC
-O2 unroll_82461759 ± 1% Cycles0.203 ± 1% PPC
-O2 unroll_42419625 ± 1% Cycles0.207 ± 1% PPC
-O2 unroll_4_asscom1134117 ± 1% Cycles0.441 ± 1% PPC
-O0 unroll_4_asscom1091086 ± 2% Cycles0.458 ± 2% PPC
-O3 unroll_4393973 ± 4% Cycles1.269 ± 4% PPC
-O3 unroll_8334532 ± 0% Cycles1.495 ± 0% PPC
-O0 asm_unroll_8266224 ± 3% Cycles1.878 ± 3% PPC
-O3 unroll_4_asscom254489 ± 1% Cycles1.965 ± 1% PPC
-O2 asm_unroll_8227831 ± 1% Cycles2.195 ± 1% PPC
-O3 asm_unroll_8227390 ± 0% Cycles2.199 ± 0% PPC
-O0 avx2_and_scalar_unroll149051 ± 0% Cycles3.355 ± 0% PPC
-O3 avx2_and_scalar_unroll146261 ± 0% Cycles3.419 ± 0% PPC
-O2 avx2_and_scalar_unroll144545 ± 0% Cycles3.459 ± 0% PPC
-O3 sse2125397 ± 2% Cycles3.987 ± 2% PPC
-O0 sse2123398 ± 0% Cycles4.052 ± 0% PPC
-O2 sse2119917 ± 0% Cycles4.170 ± 0% PPC
-O3 simple112045 ± 0% Cycles4.462 ± 0% PPC
-O0 avx2111489 ± 0% Cycles4.485 ± 0% PPC
-O2 avx2110661 ± 0% Cycles4.518 ± 0% PPC
-O3 avx2_unroll4_mem_address110342 ± 0% Cycles4.531 ± 0% PPC
-O3 avx2_unroll4109213 ± 1% Cycles4.578 ± 1% PPC
-O3 avx2107011 ± 0% Cycles4.672 ± 0% PPC
-O2 avx2_unroll4106253 ± 0% Cycles4.706 ± 0% PPC
-O2 avx2_unroll4_mem_address105458 ± 1% Cycles4.741 ± 1% PPC
-O0 avx2_unroll4103996 ± 0% Cycles4.808 ± 0% PPC
-O0 avx2_unroll4_mem_address101976 ± 0% Cycles4.903 ± 0% PPC

おめでとう。

2015-03-25

Ubuntu(Debian)でインストール済みのパッケージ一覧を得る方法

Ubuntuはインストール済みのソフトウェアを一覧表示することすらできない。 | ask.fm/EzoeRyou

すこし調べた結果、

dpkg --get-selections

で、全パッケージのインストールされたものと、インストールされたが除去されたものを得ることができるようだ。除去されたものは文末にdeinstallとついているので、まずはこれをgrepで取り除いたうえで、sedで文末の空白文字とinstallを除去すればよい。

dpkg --get-selections | grep -v deinstall | sed -e "s/[[:space:]]*install$//"

メモ代わりに書いておく。

2015-03-24

メンバー関数へのポインターを返すメンバー関数へのポインターを返すメンバー関数

class Foo;が存在したとして(1)Fooのメンバ関数ポインタ(2)を戻すメンバ関数のポインタが欲しいと思った(なお(1)で戻すメンバ関数もFooのメンバ関数ポインタを戻す)のだが、どうあがいても記述出来ないものだったりするのだろうか?

ようするに、以下のようなことがしたいわけだ。

class Foo
{
public :
    // メンバー関数a
    void a() { }
    // メンバー関数aへのポインターを返すメンバー関数b
    ??? b() { return &Foo::a ; }
    // メンバー関数aへのポインターを返すメンバー関数bへのポインターを返すメンバー関数c
    ??? c() { return &Foo::b ; }
}

ここで、???の部分に戻り値の型を記述しなければならない。

もちろんこれは記述できる。ただしその記述は、C++の規格のバージョンにより難易度が異なる。

C++14

最新の素晴らしい標準規格であるC++14では、この程度の問題は赤子の手をひねるより簡単だ。

C++14に追加された戻り値の型推定は、戻り値の型を書くべき場所にautoキーワードを書くことで、return文のオペランドの式の型を戻り値の型として書いた扱いになる。

// C++14
// 戻り値の型推定
class Foo
{
public :
    // C++14
    void a() { }
    auto b() { return &Foo::a ; }
    auto c() { return &Foo::b ; }
} ;

return文のオペランドの式の型はコンパイル時に決定できるため、当然、戻り値の型もコンパイル時に決定できる。これは具体的な形名を手で書くのと全く同じである。コンパイラーができることはコンパイラーにやらせれば良い。人間様が手をわずらわす必要はない。

C++11

残念ながら、4年も前の大昔の標準規格であるC++11には戻り値の型推定がない。そのため、戻り値の型を手で書かなければならない。ただし、C++11には、戻り値の型を後置する新しい関数記法がある。戻り値の型を書くべき場所にautoキーワードを書き、関数宣言の最後に->を書いて、その後に戻り値の型を書く。

// C++11
// 新しい関数記法
class Foo
{
public :
    void a() { }
    auto b() -> void (Foo::*)() { return &Foo::a ; } 
    auto c() -> auto (Foo::*)() -> void (Foo::*)() { return &Foo::b ;}
} ;

関数aの型は、void (Foo::*)()である。あるいは、auto (Foo::*)() -> voidである。とすると、この型を返す関数は、auto b() -> void (Foo::*)()となるここまでくればもう明白だろう。そう、関数cが返すのは auto (Foo::*)() -> ???で、???に入る戻り値の型はvoid (Foo::*)()だ。

C++03

C++03を今使うものは何か苦行でも行っているとしか思えない。

まず、関数型がある。


void (int)

しかる後に、関数へのポインター型がある。


void (*)(int)

関数ポインターを返す関数は以下のように書ける。


void f(int) { }

void (* g())(int)
{
    return &f ;
}

わかるだろうか。g()が関数gの関数名と仮引数リストだ。void (* ...)(int)の部分が戻り値の型だ。関数型には仮引数リストやその他の修飾などが含まれる。関数の型の文法上、仮引数リストと修飾はg()を囲む形で記述される。

つまりこういうことだ。

void (* // 戻り値の形名
    g() // 関数名と仮引数リスト
)(int) // 戻り値の形名
;

関数gへのポインターを返す関数hは以下のように書ける。


void (* (* h())() )(int)
{
    return &g ;
}

つまりこういうことだ。

void (* // 戻り値の形名
    (*  // 戻り値の形名
        h() // 関数名と仮引数リスト
    )() // 戻り値の形名
)(int)  // 戻り値の形名
;

そして、メンバーへのポインター型がある。

struct X { void f() ; } ;

void (X::* p1)() = &X::f ;

これらを組み合わせると、C++03という化石の様な古代の標準規格でも書くことができる。

// C++03
class Foo
{
public :
    // C++03
    void a() { }
    void (Foo::* b())() { return &Foo::a ; }
    void (Foo::* (Foo::* c())())() { return &Foo::b ;}
} ;

typedefを使うことで、いくらかマシにはできる。

class Foo
{
public :

    typedef void (Foo::* a_ptr) () ;
    typedef a_ptr (Foo::* b_ptr)() ;

    // C++03
    void a() { }
    a_ptr b() { return &Foo::a ; }
    b_ptr c() { return &Foo::b ;}
} ;

結論としては、早くC++14に移行しろということだ。

ドワンゴ広告

この記事はドワンゴ勤務中に書かれた。

ドワンゴは本物のC++プログラマーを募集しています。

採用情報|株式会社ドワンゴ

CC BY-ND 4.0: Creative Commons — Attribution-NoDerivatives 4.0 International — CC BY-ND 4.0

2015-03-22

妖怪ハウスで肉会をした

本の虫: 2kgのサーロインブロックが届いたので21日の夜はステーキ会

妖怪ハウスの住所の私宛に、突然2kgのサーロインブロックが届けられた。真空パックの冷蔵品で、何と賞味期限が週末までだ。これはまずい。なんとか週末に使ってしまわねばならない。

しかし、物は2kgの牛肉の塊である。私はそこまで大量の肉を食べたいとは思わない。こんなに急で人が集まるかわからないが、とにかく週末に人を呼んで肉会でも行うしかないだろう。

かくして、21日の夜に妖怪ハウスで肉会が計画された。

さて、2kgの牛肉をどうやって調理するべきだろうか。やはり、無難にステーキにするべきだろうか。ローストビーフにするという意見も出た。妖怪ハウスにはガスオーブンがあり、ローストビーフは調理可能だ。シチューにするという意見も出た。しかし、せっかくの巨大な肉の塊なのだから、やはりステーキにするべきだろうと結論した。

さて、サーロインブロックをステーキにするには、まず脂を切り取り、塩をすり込んで、包丁で軽く切れ目を入れた後、コショウを振ってフライパンで焼く。最初は強火で、ひっくり返して弱火で焼くとよいらしい。今回はバターも使った。

ステーキは、想定していたより手間も時間もかからなかった。これがカレーとなると、まずタマネギを5,6個みじん切りにするところからはじめなければならないのだから、段違いの手軽さだ。

また、妖怪ハウスにはガスオーブンが設置されているので、付け合せに野菜を焼いてみた。人参とタマネギとピーマンをオリーブオイルをかけて焼くだけだ。人参は最も調理に時間がかかるので先に焼く。人参を皮付きのまま数センチの厚みに切り、オリーブオイルをかけてすこし焼く。その後にタマネギとピーマンも入れて焼く。何の味付けもしなかったが、十分にうまかった。

さて、ステーキの味はなかなかのもので、客の食欲も旺盛であり、2kgの肉塊が一瞬にしてなくなってしまった。2kgでは足りなすぎる。

なかなか盛況であったので、今度は自前で肉を調達して、また近いうちに肉会をやりたいものだ。

2015-03-18

2kgのサーロインブロックが届いたので21日の夜はステーキ会

妖怪ハウスに私宛で以下のような品物が送りつけられた。

[The Meat Guy] サーロイン 2kg ブロック

箱を開けてみると、2kgのサーロインブロック、真空パックの冷蔵品だ。賞味期限を見ると、なんと3月の23日、週末までではないか。丸鶏の冷凍品が送られてきた時は、何ヶ月も期限があったので、しばらく放置した後にスタッフドチキンを作ったが、これには猶予がない。週末には調理しなければならない。

すこし考えたが、やはりなにも考えずにステーキにするのが一番だと結論した。

そんなわけで、3月21日土曜日の夜は、ステーキを焼く会を開こうと思う。ステーキを焼くのは初めてだが、食べたい人は妖怪ハウスまで来るといい。

2015-03-16

金の使い道が分からない

「毎日のように飲みに行っている」と男は我々に語った。我々は土曜日を丸一日エンジニアボードゲーム会@本郷に費やした後、会場の近くの中華料理屋で食事をしているのであった。

「毎日のように飲みに行っている。だいたい終電を逃してタクシーで帰る」

よく金が続くものだ。

「結局、タクシー代を考えると、都内に住んだほうがいいので、都内に住んでいる。しかし、家など寝るためにあるようなものなので、いらないのではないか。最近は荷物をどんどん減らしている」

「貯金がないし、たまらない。どうやって貯金をしたらいいのだろう」

贅沢な悩みだ。私と、その場にいたある女は、金の使いどころがなくて貯金額のみむなしく増えていく一方だというのに。

毎月の給与所得というものが発生するようになって早一年、最初こそ、東京に身ひとつで引っ越してきたばかりであり、色々と物入りであったが、基本的な日用品を買い揃えてしまえば、後はなにも要らなくなってしまった。一体、世間の人は金をなにに使っているのだろうか。

酒に金を使う種類の人間がいる。外で飲むと高くつく。これは、飲み屋には酒と食材の他にも、場所や建物や人員が必要なためである。また、終電を逃して家に帰りたければタクシー代もかかる。私は飲み歩きたいとは思わない。まず、飲み屋というものは大抵がタバコという薬物に中毒している救いようのない人間の多い場所であり、そのような場所に身を置きたくはない。また、酒も、相手が入ればこそ飲んでも楽しいが、一人寂しく飲みたいとは思わない。また、東京の飯はまずいので、食べ歩きたいとも思わない。

旅はどうか。旅には金がかかる。新幹線や飛行機はそれなりの値段であるし、宿も高い。私は旅に出たいとは、今のところ思っていない。特に見たい名所もないし、温泉というものにも興味がない。広い湯船につかりたければ、銭湯に行けばいい。

車やバイクにも興味がない。

ビデオゲームはどうか。私にとって、ビデオゲームとはマウスとキーボードで行うものである。当然、十分な性能のCPUとGPUとメモリ容量と高速なストレージを備えたコンピューターでなければならぬ。これを用意するには、数十万円かかる。それでもたったの数十万円だ。一度PCを組んでしまえば、一年以上使えるので、やはり月あたり数万円の出費でしかない。それに、今のところ、あまりおもしろそうなゲームがないため、数年はビデオゲームを控えるつもりである。

ボードゲームはどうか。ボードゲームは高い。僅かなコンポーネントの、実質ルールを書いた紙切れだけのようなボードゲームが数千円する。とはいえ、どんなに高いものでも、せいぜい一万円程度であり、一度買ってしまえば何年も遊べる。それを考えれば、それほど高くはない。MTGや遊戯王のような、高くつくゲームもあるが、私はデッキ構築ゲームにはそれほど興味がない。

最近は、ボルダリングをしている。ボルダリングは高くつくかというと、それほどでもない。ボルダリングジムの使用料は、一回あたり二千円弱だからだ。ボルダリング用の靴は高いが、一年以上使えることを考えると、やはりそれほどでもない。

妻子でもいれば色々と物入りなのかもしれぬが、あいにくと結婚とは縁遠いようだ。

最近、月一で江添ボドゲ会を開いているが、菓子や飲み物やビールを用意したり、カレーを5リットル作ったりするのに、それなりに金がかかる。とはいえそれほどでもない。

ただ、妖怪ハウスの賃貸契約が3年なので、来年以降存在しているかどうか怪しい。ボドゲ会のために、都内に広いリビングを備えた物件を借りるのにはそれなりに金がかかる。とりあえずはまだ先の話だ。

そういえば、このエンジニアボードゲーム会の後の食事の席で、コミット申請書とかコンパイル申請書などと言った闇の話を聞いたのだが、それはまたの機会に。

2015-03-11

最近のC++17事情

C++1z、あるいはC++17とも呼ばれている次のC++規格の、最近の事情はどうなっているのか。すでにドラフトに取り入れられた機能もあるので、現在の最新の状況を見ていこう。もうすでに紹介したものも含まれているが、おさらいとしてみていく。また、ここで解説する新機能は、いずれもすでにドラフト入りしているが、正式に規格制定される際に変わる可能性がある。

N3928: メッセージなしstatic_assert

C++11で入ったstatic_assertは、必ず文字列リテラルを書かなければならなかった。

static_assert( INT_MAX >= 2147483647, "This code assumes at least 32-bit int." ) ;
static_assert( true == true, "You're compiler is fundamentally wrong." ) ;

しかし、この文字列リテラルの使われ方は規定されていない。特にメッセージを書きたくなくても、文字列リテラルは、文法上必ず書かなくてはならない。


static_cast( std::numeric_limits<double>::is_iec559, "" ) ;

C++1zでは、文字列リテラルを書かなくてもよくなる。


static_cast( false ) ;

N4086: Removing trigraphs??!

トライグラフが取り除かれる。

// C++14までは#
// C++1zでは??=
std::cout << "??=" ;

N4051: Allow typename in a template template parameter

テンプレートテンプレートパラメーターにtypenameキーワードが使えるようになる。

template <
    template < typename T >
    class U
>
struct X ;

が、

template <
    template < typename T >
    typename U
>
struct X ;

とも書けるようになる。

N3922: New Rules for auto deduction from braced-init-list.

auto指定子で直接初期化でリスト初期化を書いた場合の挙動を変更する。C++14で合法なコードが違法になる珍しいケースだ。


auto a{ 1, 2, 3 } ;

このコードは、C++14までは合法なコードであり、decltype(a)は、std::initializer_list<int>となる。

C++1zでは、このコードは違法である。auto指定子で直接初期化でリスト初期化の場合は、単一のinitializer-clauseしか書くことができなくなった。

// aの型はint
auto a{ 1 } ;

N4295: Fold expressions

今回紹介する新機能の中で、一番面白い機能がこれだ。パラメーターパックに対するfold式がC++に入る。

パラメーターパック全てに対して演算子を適用したい場面がある。

// 与えた実引数にoperator +を適用して合計値を返す関数テンプレートsum
sum( 1, 2, 3 ) ; // 6
sum( 1, 2, 3, 4 ) ; // 10

このようなsumをC++14で書くと以下のようになる。

template < typename T >
T sum( T && t )
{
    return t ;
}

template < typename T, typename ... Args >
T sum( T && t, Args && ... args )
{
    return t + sum( std::forward<Args>(args)... ) ;
}

やりたいことは、パラメーターパックのそれぞれの実引数にoperator +を適用したいだけなのに、やたらと面倒なコードだ。

そこで、C++1zには、パラメーターパックに対するfold式が入る。

template < typename ... Args >
auto sum( Args && ... args )
{
    return ( args + ... ) ;
}

これは、sum( 1, 2, 3, )に対して、 (((1 + 2) + 3) + 4)のようにleft foldされる。

逆に以下のように書けば、

template < typename ... Args >
auto sum( Args && ... args )
{
    return ( ... + args ) ;
}

(1 + ( 2 + ( 3 + 4 ) ) )のように、right foldされる。

fold式は、必ず括弧で囲まなければならない。


( pack + ... ) ; // OK
pack + ... ; // エラー、括弧で囲まれていない

fold式には、unary(単項) foldとbinary(二項) foldがある。binary foldは、(e1 op1 ... op2 e2)という形を取る。op1とop2は同じfold演算子でなければならず、e1とe2は、どちらか片方だけがパラメーターパックでなければならない。e2がパラメーターパックであった場合はleft fold、e1がパラメーターパックであった場合はright foldとなる。


template < typename ... Types >
void f( Types ... args )
{
    ( 1 + ... + args ) ; // binary left fold
    ( args + ... + 1 ) ; // binary right fold
}

それぞれ、(( ( 1 + args0 ) + args1) + ... + argsN )と、args0 + ( args1 + ( ... argsN + 1) )のようにパック展開される。

N4267: Adding u8 character literals

UTF-8文字リテラルの追加。プレフィクスu8の文字リテラルで、UTF-8のコード単位一つで表現可能な文字が記述できる。


char a = u8'a' ; // OK
char b = u8'あ' ; // エラー、UTF-8で符号化するにはコード単位が3個必要。

N4230: Nested namespace definition (revision 2)

名前空間の宣言をネストできるようになる。

namepsace A {
    namespace B {
        namespace C {
            // ...
        }
    }
}

のようなコードが、

namespace A::B::C {
// ...
}

のように書ける。

N4266: Attributes for namespaces and enumerators

名前空間とenumeratorにattributeが記述できるようになる。C++14までは、文法上の制約で記述することができなかった。これにより、名前空間やenumeratorにdeprecatedが指定できる。記述する場所は、名前空間ならnamespaceキーワードの前、識別子の後、enumeratorならば、識別子の後だ。


namespace [[deprecated("This namespace is deprecated.")]] libv1 { }

enum class E { value [[deprecated("This enumerator is deprecated.")]] = 0 ; }

N4268: Allow constant evaluation for all non-type template arguments

すべての非型テンプレート実引数でコンパイル時評価を可能にするように制限を緩和する。

以下のコードは違法である。

template<int *p> struct A {};
int n;
A<&n> a; // ok

constexpr int *p() { return &n; }
A<p()> b; // エラー

理由は、定数として渡せるポインターはnullだけだからである。

constexpr int *p() { return nullptr ; }
A<p()> b; // OK

constexprがある今、この制約は時代にそぐわない。そこで、非型テンプレート実引数に、任意の定数式を渡せるように制限を緩和された。つまり、上のコードは違法ではなくなる。

ドワンゴ広告

この記事はドワンゴ勤務中に書かれた。

ドワンゴは本物のC++プログラマーを募集しています。

採用情報|株式会社ドワンゴ

CC BY-ND 4.0: Creative Commons — Attribution-NoDerivatives 4.0 International — CC BY-ND 4.0

江添ボドゲ会3月の様子

江添ボドゲ会3月が開催された。

当日は16人ほどのボドゲ好きの男が集まり、なかなかできない大人数ボドゲをした。

筆者は、ソビエトロシア・・・もとい妖精の国の原子力潜水艦を火災や漏水から協力して救うゲーム、レッドノーベンバーを救えを人から借りて用意したが、展開しただけでやらなかった。このゲームは火災を消すために日に飛び込む必要があり、そのためにはウォッカをひとあおりしなければならないという、とても面白い設定のゲームなのだが、残念ながら肝心のゲームの設計が悪く、同じく協力ゲームであるパンデミックやフラッシュポイントに面白さで劣るゲームとなっている。

なお、前日は花金を満喫して会社で徹夜でボドゲをしており、帰ってから、カレーを作って、またボドゲをして、日曜日も残った面子でやはりボドゲをした。実にボドゲ三昧の週末であった。

ドワンゴでは、ボドゲは社内で昼からやっているのだが、なかなかそういう環境にいない人にはボドゲを日常的にするのは難しいようだ。

そういえば、火曜日にギャモンボードが妖怪ハウスに届いた。

2015-03-09

ChromiumがLinuxカーネル3.17より前のサポートを打ち切り

Chrome/Chromium To Require Newer Version Of Linux Kernel - Phoronix

ChromiumがLinuxカーネル3.17以前のサポートを打ち切るようだ。

というのは釣りタイトルだが、Linuxカーネル3.16までで、最新のChromiumを使うと、ブラウザー拡張がインストールできないという問題がある。この理由は、Linuxカーネルのサンドボックス機能であるseccompに最近入ったTSYNC(SECCOMP_FILTER_FLAG_TSYNC)をChromiumが使っているためだ。

興味深いは、LinuxカーネルにTSYNCをいれたのは、Chromium開発者で今はGoogle社員でもあるKees Cookだ。Ubuntuの12.04と14.10のLinuxカーネルには、TSYNCをbackportするパッチがあたっているが、これもKees Cookが用意したらしい。

これは適切に情報共有されなかったらしく、かやの外の人間は、最新のChromiumが動かなくなった理由を、手探りで解明する必要に迫られた。

Issue 758063005: Linux sandbox: report TSYNC status in chrome://sandbox - Code Review

さて、ChromiumがLinuxカーネル3.17以上を必要とするようになったというバグ報告は、WontFixとされてしまった。なぜならば、「これは技術的にはregressionではあるが、ユーザーにはカーネルのアップデートという、十分にリーズナブルなworkaroundが存在するから」だそうだ。はたして、本当に十分にリーズナブルだろうか。

Linuxカーネル3.16というのは、去年リリースされたばかりのだいぶ新しいバージョンである。3.17以上を要求するのはあまりにも難しい。3.17以前のカーネルでChromiumを使うには、TSYNCのbackportが必要だが、少なくともDebianは、backportに消極的のようだ。

Debian 8/jessie - SECCOMP_FILTER_FLAG_TSYNC

2015-03-08

毎月第一週の土曜日は江添ボドゲ会

4月4日に妖怪ハウスでボードゲーム会を開催する。

江添ボドゲ会@4月 - connpass

毎月第一土曜日に開催する定例会にしていきたいところだ。

2015-02-27

range-based forで固定回ループ

本の虫: Dwangoプログラミングコンテストの感想で固定回のループがあればいいと書いたが、range-based forにそういうイテレーターを与えてやればいいのではないかと気がついた。つまり、こういうことだ。

class counter_iterator
{

    std::size_t i ;

public :
    

    counter_iterator() : i(0) { }
    counter_iterator( std::size_t n ) : i(n) { }

    bool operator == ( const counter_iterator & rhs ) const
    {
        return i == rhs.i ;
    }

    bool operator != ( const counter_iterator & rhs ) const
    {
        return i != rhs.i ;
    }

    std::size_t & operator * ( )
    {
        return i ;
    }

    counter_iterator & operator ++ ()
    {
        ++i ;
        return *this ;
    } 
} ;

class nth_loop
{
private :
    std::size_t i ;

public :
    nth_loop( std::size_t n ) : i(n) { }

    counter_iterator begin() const
    {
        return counter_iterator() ;
    }

    counter_iterator end() const
    {
        return counter_iterator(i) ;
    }
} ;

nth_loop rep( std::size_t n )
{
    return nth_loop( n ) ;
}
 
int main()
{
    for ( auto elem : rep(10) )
    {
        std::cout << elem << std::endl ;
    }
}

とはいえ、for ( elem : 10 )と書きたいところだ。

追記:ユーザー定義リテラルを使う方法を思いついた。unsigned long long intに書き変えるのは省略。

nth_loop operator "" _( unsigned long long int n )
{
    return nth_loop( n ) ;
}

int main()
{
    for ( auto i : 10_ )
    {
        std::cout << i << std::endl ;
    }
}

だいぶ簡略化された。

なお、Boostに同等のライブラリ、boost::iragenがある。

ドワンゴ広告

ドワンゴは本物のC++プログラマーを募集しています。

採用情報|株式会社ドワンゴ

CC BY-ND 4.0: Creative Commons — Attribution-NoDerivatives 4.0 International — CC BY-ND 4.0

2015-02-26

C++11/14の採用が進んでいないのはだいたいRHELのせい

C++11やC++14は、すでにGCCやClangの最新の安定版で実用的に使えるようになっているが、なかなか現場で広く使われるようにはなっていないように見える。これはなぜか。やはり教育者の不足か。参考書がないのか。それもあるかもしれないが、最大の理由がある。

RHELが悪い。

RHEL 6のGCCのバージョンは4.4である。これは。C++11をまともにサポートしていない。GCC 4.4当時といえば、まだC++11がC++0xと呼ばれていた時代で、一部機能を当時のドラフトに基づいて実験的実装をしていた。正式な規格とはだいぶ異なっているだろうし、不具合もたくさんあるものと思われる。

次のRHELのバージョンは7であるが、これにはGCC 4.8が入るものと思われる。しかし、すでにGCCの安定版は4.9だ。GCC 4.8もC++11実装に不具合が色々あってあまりお勧めできない。これがあと何年も使われるのかと思うとげんなりする。

結局、LTSというのは本当に有益なのだろうかという疑問が湧いてきた。挙動が変わらないとはいっても、不具合もそのままなので、そのコストは支払わなければならない。そして、いずれはソフトウェアをアップデートせねばならず。5年10年もの積みかさなかった挙動の変更を一気に修正しなければならない。

とはいえ、C++の簡単な入門書は必要だと思うので、簡単に読めるC++の解説を今書いている。

ドワンゴ広告

この記事はドワンゴ勤務中に書かれた。

ドワンゴは本物のC++プログラマーを募集しています。

採用情報|株式会社ドワンゴ

CC BY-ND 4.0: Creative Commons — Attribution-NoDerivatives 4.0 International — CC BY-ND 4.0