RでIntel Math Kernel Libraryを使う方法


Intel Math Kernel Library(以下MKL。フリーソフトウェアではないが無料)を使うようにRをビルドして高速化する方法です。R benchmarksで公開されているR benchmark 2.5なら,3倍くらい速くなります。こんな手間をかけなくても,Microsoft R Openを入れるだけで同じ効果を得られるのですが,「-O3 -march=native」でビルドしたら速いのかな?などと思う方のためのメモです。

https://software.intel.com/en-us/mklで「Free Download」をクリックします。サインアップあるいはサインインして,Intel Math Kernel Libraryをクリックすると,l_mkl_2018.1.163.tgzのようなファイルがダウンロードされます。

ダウンロードしたファイルのあるディレクトリで,以下を実行します。

tar zxf l_mkl_2018.1.163.tgz
cd l_mkl_2018.1.163
sed 's/ACCEPT_EULA=decline/ACCEPT_EULA=accept/' silent.cfg > s.cfg
sudo ./install.sh --silent ./s.cfg
cd ..

Rのソースをダウンロードし,展開します。

wget https://cloud.r-project.org/src/base/R-3/R-3.4.2.tar.gz
tar zxf R-3.4.2.tar.gz
cd R-3.4.2

ビルドに必要なパッケージを入れます。

sudo apt install r-base xorg-dev libcurl4-openssl-dev -y

MKLのための環境変数を設定します。

source /opt/intel/mkl/bin/mklvars.sh intel64

MKL="-Wl,--no-as-needed -lmkl_gf_lp64 -Wl,--start-group -lmkl_gnu_thread  -lmkl_core  -Wl,--end-group -fopenmp  -ldl -lpthread -lm"

configure! (Anacondaをふつうに入れていると,システムのcurlが隠されているせいで失敗します。この作業の間だけのことなので,AnacondaをPATHからはずすか,$HOME/anaconda3の名前を変えておきましょう。)

CFLAGS="-O3 -march=native" CXXFLAGS="$CFLAGS" FFLAGS="$CFLAGS" FCFLAGS="$CFLAGS" ./configure --with-blas="$MKL" --with-lapack

ビルドします。

make -j $(nproc)

bin/Rで実行します。make installでインストールできますが,そこまでしなくてもいいでしょう。

参考:R でインテル® MKL を使用する

岩波文庫の書誌として最も信頼できるのは何か


4000612093『岩波文庫解説総目録』(以下,総目録)の新版(90年版 1927~2016)を堪能したので,これに関連した話を書いておこうと思います。

90年を超える歴史,約6000冊のコレクションを誇る岩波文庫が重要な文庫であることは間違いありません。しかし,その歴史のためか,書誌に関してはいろいろと難しいことがあります(そもそも,6000の数え方がよくわかりません)。次のような問題があります。

  1. ISBNの使い回し
  2. ISBNのないタイトルの存在
  3. 記録に残らない重版時の内容変更

順番に行きます。

ISBNの使い回し

最も有名なのは,ISBNの使い回し(Wikipedia)でしょう。改版時に翻訳者が変わってもISBNはそのままになることがあります。そのせいで,「オンライン書店で新訳を買ったつもりが旧訳が届いた」などというトラブルも発生するようです。参考:岩波文庫がISBNコードを上書き使用している件について(togetter)

総目録はこの問題の解決には役立ちません。原書が同じものはまとめられ,ISBNは変わったとしても最新のものしか載っていないからです。たとえば『インディアスの破壊についての簡潔な報告』は,1976年版(ISBNは334271)2013年版(ISBNは358001)があります。訳者が同じなのにISBNが変わった(岩波文庫としては)珍しい例なのですが,総目録に載っているISBNは358001だけなので,総目録だけを見てもISBNが変わったかどうかはわかりません。(総目録に倣って,ISBN-13の先頭の978400と末尾の1桁,ISBN10の先頭の400と末尾の1桁は割愛しています。)

ISBNが使い回されても,CiNii BooksのデータベースはNICDという独自のIDを振っているので,CiNii Booksで検索すれば,ISBNが変わったことがわかります。(ただし,CiNii BooksからGoogle BooksへのリンクにはISBNが使われているので,CiNii BooksからGoogle Booksに飛ぶときは注意が必要です。間違っているものが少なくとも60件はあります。)

CiNii BooksのAPIで,岩波文庫(親書誌IDがBN00015783であるもの)を検索し,ISBNは同じでもNICDは違うもの,つまりISBNが使い回されているものを挙げると,次のようになります。

ISBNを使い回している岩波文庫タイトル(機械的抽出)

旧字→新字のような,ISBNを使い回してもいいと思う人が多そうなものを除くと,次のようになります。

ISBNを使い回している岩波文庫タイトル(深刻なもの)

使い回しに比べると少数ですが,翻訳者の変更などの際に,ISBNが変わったものもあります。そのようなものを挙げると,次のようになります。

ISBNが変わった岩波文庫タイトル

ISBNのないタイトルの存在

1927年に創刊された岩波文庫ですが,ISBNが使われるようになったのは1980年頃なので,その間に刊行されたものには,そもそもISBNが付いていません。

CiNii Booksに登録されていて,ISBNがないものを挙げると,次のようになります。

ISBNのない岩波文庫タイトル

CiNii Booksが最も信頼できる情報源なのかというと,そういうわけではありません。

第1に,このデータベースは大学図書館のデータベースなので,大学図書館が所蔵していないものは登録されていません。第2に,NICDという独自のIDを振っているとは言っても,ISBNがない時代のものには,かなりの混乱がありそうです。

第2の例として,同一の書籍に複数のNICDが振られているものを14件見つけました。ついでに,翻訳者が間違って登録されていたもの1件,ISBNがあるはずなのに登録されていなかったもの1件,タイトルの「他二編」の漢数字「二」がカタカナの「二」になっているものも見つけました。いずれも報告し,修正してもらいましたが,最後のものは発生理由を想像すると闇を覗いた気になります。

CiNiiがダメなら国会図書館はどうかというと,『人間に就いて』が誤って『人間について』として登録されているなど,完璧というわけにはいきません。(本稿執筆時点)

記録に残らない重版時の内容変更

ISBNの問題を別にすれば総目録は完璧なのかというと,そういうわけではありません。

総目録には,「改版」は記録されていますが,「重版」は記録されていません。ふつうはそれでいいのですが,岩波文庫の場合は,重版時に解説が追加されることがあります。これが記録されていないのは困ります。以下の挙げるのは,記録されるべきなのに記録されていないものです。(*の部分は岩波書店の「愛読者の窓」係に教えてもらいました。ありがとうございました。)

版元でも確認できないものについて,現物を確認できる人がいたら教えていただきたいです。

総目録には細かい間違いもあります。

たとえば,手元の初版では,『草の葉』の翻訳者が「杉木喬, 鍋島能弘, 酒本雅之」になっていますが,岩波書店の「愛読者の窓」係に確認したところ,正しくは「酒本雅之」のみだそうです。正誤表の公開が待たれます。

ウェブサイトは直っていますが,こちらには,旧版(有島武郎選訳とは別)の情報が出てこないという問題があります。

4003500210総目録では,旧版(上中下)の翻訳者は「杉木喬, 鍋島能弘, 酒本雅之」になっていますが,3人訳なのは上巻だけで,中と下の翻訳者は「鍋島能弘, 酒本雅之」だそうです。この点について,『岩波文庫の80年』は正しいです。総目録と『岩波文庫の80年』で統一のデータベースを使っているわけではないようです。『岩波文庫の80年』は単なる年表なので,書誌としては不十分なのですが,それでもとりあえず,『岩波文庫の90年』は出してほしいです。CiNii Booksのデータは間違ってますね(本稿執筆時点)。

結論:岩波文庫の書誌として信頼できるものを見つけるのは難しいです。

機械より人間らしくなれるか?(祝ノーベル文学賞)


4794220804囲碁や将棋のトッププロが人工知能に負け,「人間の棋士にしか見せられないものを」みたいなことが言われるようになって久しいわけですが,そういうことは,これまでもあったし,これからもあるでしょう。

チューリングテストに参加する人間も,似たように状況に直面するようです。

チューリングテストとは,コンピュータが知能を持っているかどうかを判定するためのテストで,電子的にチャットにおいて,審判(人間)から「人間」と認められるなら,「知能がある」ということにするというものです。審判(人間)には「知能」があることが前提になっているところが洒落ています。

チャットの一方の側には審判(人間)が,もう一方の側には人間またはコンピュータがいるわけですが,人間は,どちらの側に立つにしても,相当なプレッシャーになるでしょう。審判のときは,相手がコンピュータであることを見破れないと恥ずかしい,審判が相手のときは,コンピュータと見なされるのが恥ずかしい。

この後者,つまり審判とチャットする立場に立った経験をもとに書かれたのが,『機械より人間らしくなれるか?』(文献リストあり・索引なし)で,出版されたのは今日の人工知能ブームの前ですが,今読んでも十分楽しめます。

4151200517これに関連して,ノーベル文学賞作家カズオ・イシグロの『わたしを離さないで』を思い出します。これはすごい小説で,村上春樹さんも,『夢を見るために毎朝僕は目覚めるのです』の中で,「最近,深い感銘を受けた本はありますか?」という質問に応えて次のように言っています。

小説で最近ノックアウトされたのは、カズオ・イシグロの『わたしを離さないで』かな。彼は僕が最も高く評価する同世代の作家の一人です。上手なだけではなく魂がこもっている。(p.462)

この小説には二つの大きな秘密が隠されています。①主要登場人物が生まれた理由と,②彼らが芸術作品を作らされた理由です。ノーベル文学賞に関連する報道で,第1の秘密は大々的にバラされてしまいましたが,映画化もされた作品ですし,これからこの作品を読もうという人も,これについてはすでに知っているでしょう。実際,イシグロ自身,『知の最先端』に掲載された「愛はクローン人間の悲しみを救えるか」という対談で,「この小説は最初から読者が結末を知っているかどうかは、重要ではないと思います(p.179)」と言っています。

打ちのめされるのは第2の秘密のほうで,囲碁の時も将棋の時も,それが私に『わたしを離さないで』を思い出させました。そういう意味で,最近の状況をうまく切り出した作品だと思います。

そうは言っても,イシグロが登場人物に求めさせた「人間らしさ」は,囲碁や将棋,その他あらゆる分野において,小説に描かれたのとは別の理由で「それで?」と言われてしまうものになりつつあり,未来はこの小説よりもつらくなる気がしています。

おまけ1:ノーベル賞をきっかけに,既存の翻訳が見直されるといいですね。たとえば『遠い山なみの光』では,「chess」が「将棋」になっているのですが,両者はかなり違うゲームなので,そういうことはしない方がいいと思います。

Do you remember, Jiro, when I first taught you this game, how I always warned you about using the castles too early?

おまえに初めて将棋を教えたころ、あまり早くから角を使うんじゃないと、しじゅう言っていただろう?(p.181)

おまけ2:『知の最先端』p.197より

英語で書く作家が注目されますから、ほかの言語で書く、非常に貴重な作家が無視されているという危険がすでにありますね。従来は、たとえば、ラテンアメリカもそうですが、世界のいろいろな国から頭角を現してくる、非常に興味深いスタイルをもった作家がたくさんいたのです。少なくとも日本は、孤立した状態で成長したので、そこで作られた映画は非常に興味深いといえます。

英語の覇権で、世界が文化的にあまりにも均一化されてしまうと、こういう多様性を見逃してしまうでしょう。料理に譬えると、みな同じ料理を食べたら、面白くないということです。インド料理、日本料理などいろいろあるから面白いのです。ですから、私は世界中の作家が同じような方向で書いている時代に急速に向かっていることに、いささか当惑しています。

おまけ3:村上春樹『夢を見るために毎朝僕は目覚めるのです』(p.345)

彼自身は「日本語は話せない」と言うけれど、スコットランド人の彼の奥さんは「カズオはけっこう流暢に日本語が話せるわよ」と僕にこっそり教えてくれました(笑)。でも彼は日本語を話したくはないのだろうと、僕は理解しています。とくに日本では。たぶん彼にとって、彼の日本語は十分ではないからでしょう。

外国語というのはそういうものだろうと思います。

インテルによる「ポーカーの手の判別の並列化」について


概要:インテル デベロッパー・ゾーンで公開されている「C++ 11 でマルチスレッド・コードを記述する」という記事の,「ポーカーの手の判別は並列化しても速くならない」という主張について調べた結果,私の環境で4.9秒かかる元記事の処理が,0.68秒で終わるようになりました.(コード

計測すべし。計測するまでは速度のための調整をしてはならない。(ロブ・パイク)

オリジナルのpokereval/pokereval.cppを見ながら作業しましょう.

Step 1:実験の意義

タスクの中でスリープするのはやめましょう.そういうことをすればマルチスレッド版がシングルスレッド版より速くなるのは当然ですが,意味がありません.105行目を書き換えます.

//Sleep(1);

Step 2:データセット

2万件(正確には19981件)ではすぐに終わってしまうので,100万件のデータを使いましょう.207行目を書き換えます.

std::ifstream filein("hands1million.txt");

Step 3:シングルスレッド版の実行時間

シングルスレッド版はこれで動くので,この実行時間を基準にチューニングします.

筆者のPC(i7-4930K,Visual Studio Community 2017,Win32 Releaseモード)では,4.9秒でした(シングルスレッド版・オリジナル).

Step 4:バグ修正

マルチスレッドのコードにバグが2個あるので修正します.(補足:この修正はpull requestしました.)

(1) 入力の終わりの判定が間違っていて,hands1million.txtの最後の空行をカウントしています.283, 284行目の,

if (filein.eof()) break;
std::getline(filein, str);

を次で置き換えます.

if (!std::getline(filein, str)) break;

(2) スレッド数の倍数だけ処理したところで出力していますが,最後に余った分を出力し忘れています.299行目の#elseの前に,次を挿入しましょう.

if (count != MaxThreads - 1) {
  for (int i = 0; i < count; ++i) {
    fileout << futures[i].get() << '\n';
  }
}

Step 5:マルチスレッド版の実行時間

269行目の#define Multi 1を有効にして,マルチスレッド版の実行時間を計ります.

筆者のPCでは5.7秒でした(マルチスレッド版・オリジナル).

マルチスレッド版がシングルスレッド版(4.9秒)より遅いと悔しいのは確かです.

Step 6:計測

Visual Studioの,デバッグ→パフォーマンス プロファイラーで,関数ごとのCPU使用率を計測します.

測定結果の上位は次のようなものでした.

プロファイル結果

std::basic_istreamに一番時間がかかっています.もう少しよく見ると,その大部分は,std::endlで費やされているようです.

Step 7:パフォーマンスチューニング

『C++の教科書』の8.2.3項に,endlはバッファをフラッシュさせると書いてあります.100万件のデータの出力する際に,1件ごとにフラッシュするのはまずいでしょう.294行目のstd::endl'\n'に置き換えます.

fileout << e.get() << '\n';

筆者のPCでは2.5秒でした(マルチスレッド版・修正後).

残念ながら,これでシングルスレッド版(4.9秒)を上回ったわけではありません.シングルスレッド版のコードでもstd::endl'\n'に置き換えて(256行目),実行時間を計ります.

筆者のPCでは1.4秒でした(シングルスレッド版・修正後).

依然として,マルチスレッド版よりシングルスレッド版の方が高速です.

とはいえ,元記事の「評価の実行時間が短すぎて、タスクを非同期に呼び出すオーバーヘッドが原因である」という考察は疑問です.ここで解いているのはいわゆる embarrassingly parallel というやつで,並列化の効果が,「あって当然」だからです.

Step 8:OpenMP

OpenMPを使いましょう.『C++の教科書』の12.4節に使い方が載っています.

マクロで場合分けしてもかまいませんが,シングルスレッド版のコード(300から306行目)を書き換えます.データをメモリに読み込んで,OpenMPで並列に判別し,結果を出力します.

std::vector<std::string> hands;
while (std::getline(filein, str)) {
  hands.push_back(str);
}
rowCount = hands.size();

std::vector<std::string> results(rowCount);
#pragma omp parallel for
for (int i = 0; i < rowCount; ++i) {
  PokerHand pokerhand(hands[i]);
  auto result = EvaluateHand(pokerhand);
  results[i] = pokerhand.GetResult(result);
}

for (int i = 0; i < rowCount; ++i) {
  fileout << results[i] << '\n';
}

筆者のPCでは0.68秒でした(OpenMP版).これでシングルスレッド版・修正版(1.4秒)より速くなりました.シングルスレッド版・オリジナル(4.9秒)よりはかなり速いです.ちなみに,OpenMPを有効にしない状態でのこのコードの実行時間は1.7秒でした.

まとめ:ポーカーの手の判別は並列化で速くなります.

「大学への数学」2017年6月号掲載の拙稿「機械まかせの数学 Mathematicaで解く東大入試数学」について


B06XWF34YQ月刊誌「大学への数学」2017年6月号で,「機械まかせの数学」という記事を書きました。内容は副題「Mathematicaで解く東大入試数学」のとおりです。

高校生向けの雑誌ですが,大学生,というか,「コンピュータと数学」ということに興味を持てるすべての人が想定読者です。高校数学の知識を前提にしていますが,大学入試問題を解く数学力は不要です。「Mathematicaでしょ,知ってる」という方も,Ver. 10以降のMathematicaを知らないなら,ちょっと驚くかもしれません。

膨大なネタを4ページに収めました。行間をすべて説明するのは大変なので,ここでは一つだけ。

「プログラミング入門=アプリ制作」や「プログラミング入門=ロボット制御」という風潮がありますが,この記事のように,数学を題材にしてプログラミングを学ぶという話は,もっとあっていいと思います。その際,題材とする数学は,高校レベルがいいでしょう。多くの方が基本的知識を持っていますし,問題の難しさも比較的判断しやすいからです(受験勉強のおかげ?)。大学入試問題を使うと,解けても解けなくてもそれなりの驚きがあります。

というわけで,高校生がメインターゲットの雑誌ですが,この記事は高校生以外にもお勧めです。

高3の一年間,この雑誌の学力コンテストで名前を載せ続けたのですが,著者として名前が載ることになるとは思いませんでした。いわゆる,「当時の自分に読ませたい記事」のつもりです。息子に読ませたいかというとちょっと微妙で,ちょうど1歳になる息子が読めるようになる2020年頃には,状況がまったく変わっているかもしれません。

記事中のコードはWolfram Cloudにまとめてあります。Mathematicaは高価なソフトウェアだと思っている人が多いようですが,基本機能はクラウド上で無料で使えます(こちら)。「別の言語でやってみた」という反応も歓迎です。

電子版はないので,お早めにどうぞ。

見本

Taro Yabukiさん(@taroyabuki)がシェアした投稿 –