最新実機に触れながら開発ノウハウを学べる!! Windows Mixed Reality をフィーチャーした講義、今週日曜開催


セミナー・イベント情報@プログラミング+
第20回

2017年11月12日(日)VR/ARビジネスと開発技法の最前線2017 Part.2

2017年11月10日 18時00分更新

文● プログラミング+編集部

VR技術体験が今まさにすべての人のものになろうとしている?

 その用途として「ゲームや映像を楽しむもの」というイメージを持たれている方も多いと思われる、VR(仮想現実)ヘッドセットなどの活用範囲が、デバイスと技術の著しい進化や入手しやすい価格帯の製品増加に伴い、デザイン・教育・医療など多くの産業分野へと今まさに拡がりを見せ始めています。

 特に注目したい動きは、マイクロソフトが普及を進めている、Windows Mixed Reality ヘッドセットの登場です。ヘッドセットそのものの登場に加え、2017年10月に提供されたWindows 10の最新アップデート “Windows 10 Fall Creators Update” を通じて、マイクロソフトはより多くのコンシューマー向けPCで、仮想現実をより気軽に体験できるようにサポートを進めています。

 これはつまり、ハイスペックな対応ゲーミングPCなどを必要とすることなく、我々が家庭やオフィスで日頃使用しているPCを用いて、VR/AR/MRといった技術にグッと触れやすくなっていることを意味します。体験への敷居が一段と低くなったことが、あらゆるビジネスにおいてその導入検討を加速させていく。そうであるのならば、これからVR/AR/MR技術は様々な産業へどのような影響を与えうるのかを考えるのは、今まさに旬なトピックだと言えます。

 こうした背景を踏まえ、これまではあまりVRなどの技術へ関心をお持ちでなかった方にも、是非この分野の可能性を知っていただきたいという思いから、2017年11月12日(日)に東京・飯田橋にて、角川アスキー総合研究所はリアル講座『VR/ARビジネスと開発技法の最前線2017 Part.2』を開催いたします。

 今年4月の開催に引き続き講師を務めるのは、Oculus Japan Teamを立ち上げ、現在のVR/AR/MRムーブメントを常にリードしてきた、近藤 “GOROman” 義仁 (@GOROman) 氏(株式会社エクシヴィ 代表取締役社長)。またゲスト講師として、日本マイクロソフト株式会社のテクニカルエバンジェリストである高橋 忍氏の登壇も決定いたしました。

 講座においては、なかなか知る機会の少ないVR先進諸国の最新事情から、実機を用いたデモンストレーション、対応コンテンツ開発における実践的ノウハウまで分かりやすく知っていただくことができます。

 現状スマートフォンが優勢を占める企業・ブランドと顧客のデジタルなタッチポイント(接点)や、体験を通じてしか学ぶことのできない研修や危機管理体験など、これからVR/AR/MRが変化させていく可能性を秘めたビジネス用途は多数考えられます。こうした最新事情と今後の可能性へ、本講座で是非触れてみてはいかがでしょうか。ご参加いただきやすい週末日曜13時からスタートの本講座へ、より多くのご参加をお待ちしております。

2017年11月12日(日)開催講義概要について

VR/ARビジネスと開発技法の最前線2017 Part.2

  • 日時:2017年11月12日(日)13:00 開演 19:00 終了予定
  • 会場:角川第3本社ビル(東京都千代田区富士見1-8-19)
  • 講師:近藤 “GOROman” 義仁 氏(株式会社エクシヴィ 代表取締役社長)
  • ゲスト講師:高橋 忍 氏(日本マイクロソフト株式会社 テクニカルエバンジェリスト)
  • 進行:遠藤 諭(株式会社角川アスキー総合研究所 取締役主席研究員)
  • 参加費:1万8900円(税込)
  • 詳細情報・参加お申込みhttp://lab-kadokawa36.peatix.com/

講義アジェンダ

1.『世界のVR/AR/MRビジネス最前線』
VR/AR/MR技術を活用したビジネスにおいて先を行く、中国深セン・上海・北欧フィンランド、また最近急速に注目され出しているエストニアのVRコミュニティを実際に視察してきた講師が、現地におけるVR/AR/MRの盛り上がりやビジネス事情について、その最新状況をお伝えします。また開発者のあいだで話題を集めている、フィンランドVarjo社の超高解像度HMD体験についてもお話します。

2.『最新のVR/AR/MR基礎知識』
VR/AR/MR技術の基本から最新のVRデバイス “Windows Mixed Reality ヘッドセット” まで、実機デモンストレーションを交えながらわかりやすく解説します。

3.『VR/AR/MRコンテンツ開発、運用の落とし穴とその対策』
コンテンツ開発における基本の技法から、最新デバイスでの開発方法と対策、実際に体験展示、運用する際のコツについて知見をシェアします。

4.『実践Windows MRコンテンツ開発』
ゲームエンジンであるUnityを用いながら、Windows Mixed Reality ヘッドセットによるコンテンツを実際に開発する手法を学びます。

5.『登壇者トークセッション:VR/AR/MRの未来』
Windows Mixed Reality ヘッドセットの登場で、今後の未来はどう変わるのか。質疑応答も交えながら、ビジネスのターゲットも含めて、VR/AR/MRの未来について登壇者による対談を行います。

※講義内容は変更になる場合があります。

講師プロフィール&メッセージ

近藤 “GOROman” 義仁 (株式会社エクシヴィ 代表取締役社長)

ゲームプログラマとして大学を中退し上京。PlayStaion/2/Xbox等のコンシューマタイトル制作に関わり、描画エンジン・アニメーションエンジン等を開発。2012年Oculus Rift DK1に出会い、自らVRコンテンツの開発を行いVR普及活動をはじめる。2010年株式会社エクシヴィを立ち上げ代表取締役社長となる。並行して2014年からOculus Japan Teamを立ち上げ、Oculus VR社の親会社であるFacebook Japan株式会社に所属。国内パートナー向けに技術サポート、数多くの講演を行う。現在はエクシヴィにてVRコンテンツ開発を行っている。個人でも”GOROman”として、VRコンテンツの開発、VRの普及活動を広く行っている。代表作はMikulus, Miku Miku Akushu,「初音ミク VR Special LIVE -ALIVE-」ロート デジアイ,DMM GAMES VR × 刀剣乱舞ONLINE 三日月宗近Ver.など多数。

高橋 忍(日本マイクロソフト株式会社 テクニカルエバンジェリスト)

日本マイクロソフトでテクニカルエバンジェリストととして、主にクライアントアプリケーション開発技術を中心に啓もう活動を行う。特にWindows 10のアプリ開発やMixed Reality、またUI/UX技術に加えて最近はクラウド技術にいたるまで、セミナーやハンズオン、もしくは各種案件を通じて開発者のための支援活動を続けている。

【高橋氏からのメッセージ】日本でHoloLensが発売されてから、コミュニティの盛り上がりはもちろん、多くの企業様でこの新しいデバイスと技術が注目されています。実際に私も様々な企業様とお話をして、すでに数多くの分野やプロジェクトで開発が進められています。先日は日本からもマイクロソフトの Micxed Reality 認定パートナーが発表されました。このセミナーを通じて講師の近藤氏と一緒に、日本と世界での状況や技術について私が知っている限りの情報をお話ししたいと思っています。

■参考記事

■関連サイト



カテゴリートップへ


この連載の記事

第二の故郷「香港」で私もドローンを飛ばしてみた


遠藤諭のプログラミング+日記
第28回

そろそろ上海蟹や煲仔飯(土鍋飯)のおいしい季節ですが

2017年10月13日 19時00分更新

文● 遠藤諭(角川アスキー総合研究所)

 10月1日(日)テレビ東京「ABChanZOO(えびチャンズー)」で、ドローン(UAV=無人模型航空機)に関してレクチャーさせてもらった。登場シーンでいきなりズッコケたのは置いておくとして、見た人からはドローンの最新事情が分かってよかったという反響ももらった(ドローンは“ただ凄い”と紹介する番組が多いのに対して割りと踏み込んだ説明ができましたからね=ちなみにスタジオの小物が機械式計算機などとてもマニアック)。

 その1週間後、1年半ぶりで香港にでかけてきた。今回の目的は、日本では売っていない“あるもの”の購入と香港でドローンを飛ばすことである。

 思い返せば、2013年頃から、YouTubeにDJI Phantomシリーズを中心にドローン映像が上がりはじめた。DJIは、香港からほど近い中国本土の深センに本社があるからだろう、香港を空撮したものが目立つ。それを見て、俄然空撮がしたくなりDJI Phantom 2 Vision+の発売を待って買った私としては、第二の故郷といっていい(渡港40回以上)香港でドローンを飛ばすことが1つの夢だったのだ。

 具体的に、ドローンツアー決行となったきっかけは離陸重量300gの「DJI Spark」を買ったのと、池澤あやかさんの「ハワイでドローン飛ばしてみた」という記事である(現地のルールや申請関連にも触れたとてもいい記事)。先日も「中国でドローン飛ばしてみた」が公開された。その池澤さんとツイッターでやりとりしていたら、香港のドローンの規制やルールについて「こんな感じでは?」と教えてくれた。

DJI Sparkは、スマホでは100m、送信機では500mの距離で操作可能。キロ単位で飛べるPhantomやMavicに比べると自分がいるまわりを散策する感じに近い。上位機種にはない「おまかせ撮影」(被写体のまわりを旋回しながら撮影など)の機能もあるライトユーザー向け新世代ドローンといえる。

 香港のドローン規制については、香港特別行政区の民航處(CAD=Civil Aviation Department)の関連ページに説明があるのだが、DJIの「安全飛行 フライトマップ」で具体的な制限区域も分かる(警告が出たり離陸できない場所など)。

DJIの「安全飛行 フライトマップ」は各国の飛行制限区域やルールの概要が分かる。

 現地についてからは銅鑼湾にある「DJI 香港旗艦店」に立ち寄って、店員さんにアドバイスをもらうこともできた。今回のような場合は申請不要とのこと。もらった民航處のパンフレットでは、7kg以下の機体、高度は300フィート(91.44メートル)を超えない、昼間の飛行に限るとしたうえで、避けるべき場所や行為、飛行場など具体的な制限区域があげられている(実際に飛行を計画する場合はあらためて民航處のホームページを確認のこと。先の池澤さんの記事によると中国は6月1日以降登録が必要になったとのこと)。

「崇光 – SOGO」(香港そごう)の裏側にあるDJI 香港旗艦店。1階には実機を飛ばせるミニブースもある。

店内は広々としてとてもキレイ。プロ向けの上位機種やこんな品も展示されている。スタッフがとても親切だ。

 東京23区の場合、申請なしには200g以上のドローンを上げることができないのに対して、香港の制限区域はかなり限られている。しかし、香港の市街地は超人口過密状態だ。ふつうに街を歩い回って安全を確保できそうな場所を見つけるのは、正直なところ至難のワザといえる。私の場合は、香港の知人家族がハイキングをかねて車で香港島を西側からまわりこんでくれたりしたのでたすかった(というか楽しかった)。そうでない場合は、Google Street Viewなど可能な限りの情報をもとに少しでも下調べをしておくしかない。

 ということで、飛行高度の関係もあって2013年頃にYouTubeで見た絶景といえるほどの映像ではないのだが、いくつか空撮映像の拙作を紹介させてもらいます。

 

南Y島を望む海岸にて撮影。あとで調べて分かったのだが最後のほうに出てくるのは第二次世界大戦時に英国が作ったトーチカとか。

 

ビクトリア湾が制限区域だが港のようすは撮影したくて鯉魚門の近くまで移動して撮影。船舶も近寄れないので距離を置いてだが気分はもう「ミスターBOO!」だ。

 香港に出かけはじめた89年には、鯉魚門には、まだハードディスクメーカーのMaxtorの工場なんかがあった。1980年代は、缶詰工場などが次々コンピューター関連に切り替わったなどといわれたが、急速に台湾へ、そして中国へとコンピューターの製造が移っていったのはご存じのとおり。それでも、香港の電脳街は楽しくて『月刊アスキー』で何度やったか分からない。やっぱり、アジアと電脳とエスニックフードは三位一体なのだと思う。

 冒頭に掲げたビルの写真は、映画『トランスフォーマー4』に登場した怪物ビル(海景樓、福昌樓、海山樓、益昌大厦、益發大厦の5つのビルの合体)。裏手の山道にある休憩所からあげさせてもらったのだが、さすがにビルに寄っていく余裕はなかった。このビルに関しては以下のTHETA画像をご覧あれ(2016年1月の撮影)。

Post from RICOH THETA. – Spherical Image – RICOH THETA

 ところで、この夏は、クオリティソフト株式会社の浦社長に誘われて同社の南紀白浜オフィスに出かける予定だったのだが、私のスケジュールの都合で中止になってしまい迷惑をかけてしまった。同社は、プライベートビーチも持っているうえに、ドローンスクール事業でも定評がある。ということで、なんとなく2カ月遅れでドローンを飛ばすことはできたのだが、やっぱりドローン空撮は楽しい。

 どう楽しいのかというと、一眼レフが、世界を違ったレンズで切り取ることで世の中の見方を変える道具だとすると、ドローンは視点を変えて世界の見方を変える道具なのだ。

遠藤諭(えんどうさとし)

 株式会社角川アスキー総合研究所 取締役主席研究員。月刊アスキー編集長などを経て、2013年より現職。角川アスキー総研では、スマートフォンとネットの時代の人々のライフスタイルに関して、調査・コンサルティングを行っている。著書に『ソーシャルネイティブの時代』、『ジャネラルパーパス・テクノロジー』(野口悠紀雄氏との共著、アスキー新書)、『NHK ITホワイトボックス 世界一やさしいネット力養成講座』(講談社)など。

Twitter:@hortense667
Mastodon:https://mstdn.jp/@hortense667

■関連サイト

DJI(https://www.dji.com/jp
クオリティソフト株式会社(http://www.qualitysoft.com/
ドローンビジネスカレッジ(https://drone-bc.jp/


カテゴリートップへ


この連載の記事

教室数は1000校以上!! ヒューマンアカデミー『ロボット教室』責任者から“プログラミング教育”の本質を聴いた


スペシャルトーク@プログラミング+
第14回

「プログラミングして意のままにロボットを動かした、という体験を多くの子どもたちへ届けたい」

2017年06月27日 19時00分更新

文● プログラミング+編集部

 2020年からプログラミングの義務教育化を控えている現状だが、教育現場からはカリキュラムづくりや先生に求められる能力など、依然として不安や課題を多く抱えている旨の声が聞かれる状況だということも否定できない。そんな中、未就学児からの“習いごと”として、プログラミング教育の重要性が広く認識される以前から『ロボット教室』を展開してきた存在がある。教育事業大手の『ヒューマンアカデミー』だ。

 今回編集部は、この『ロボット教室』事業を牽引してきた、ヒューマンアカデミー株式会社 児童教育事業本部 チーフマネジャーの神野佳彦氏から、事業に関するお話をうかがうことができた。ロボットクリエイターの高橋智隆氏をアドバイザーに迎え、2009年6月からロボット教室事業を展開してきた同社の経験や工夫からは、プログラミング教育の導入を考えるうえで非常に学ぶ点が多い。教育現場へのプログラミング教育を具体的に検討されている方々にとって、貴重なノウハウを含んでいるインタビューを、今回はお届けしたい。

“自分が作り上げたものが実際に動く”経験を年少時に体験しておくことの重要性

―― いまロボット教室は、教室数でいうとどれくらいまで増えているんですか?

神野氏(以下、敬称略) 2017年5月末時点で、1000校を超えたくらいですね。

―― いつ頃から急に増え始めた、などあるのでしょうか?

神野 ここ4、5年で急激に増えている状況です。

―― ここ4、5年で急増した要因や背景としては、どのような社会の変化が挙げられますか?

神野 やはり、世の中がプログラミング教育を含め、ロボットなどの分野へ力を入れていこうという流れになっていますし、その流れを汲んだ習いごとも増えており、保護者の方々も意識をし始めていることが、ロボット教室が急増した一番の要因ではないかと思っています。

―― 教室は1000校を超えているとお話してくださいましたが、それに応じて生徒数も増えているんですよね?

神野 そうですね。生徒数は2017年5月末時点で、1万5000名を上回ってまいりました。

―― 教室に通われている生徒さんの年代は、どのあたりがメインなのでしょうか?

神野 7~9歳くらいまでの年齢層の生徒さんが多いですね。

―― 7~9歳だと、具体的にはどういったことをロボット教室では学ぶのでしょうか?

神野 オリジナルのブロックキットで時間内に1体のロボットを完成させます。“自分でロボットを作って実際に動かす”という一連の流れを、教室に通うお子さんたちには必ず経験してもらうにしています。

―― そうした経験によってお子さんたちは、どういった能力を伸ばしていけるのでしょうか?

神野 まずは、ロボットクリエイターの高橋智隆さんに監修いただいたロボットを、オリジナルのテキストに沿って製作します。そこで様々なことを学んでいただきます。その後、ロボットを作る過程で得た知識をいろいろと応用していくことで、想像(創造)力を育むことに注力しています。実際に自分の手を動かして、ブロックを用いながら立体的にロボットを作っていくため、空間認識能力などを養うことができます。また、作ったロボットを改造していくことで、表現力・思考力も高めることができます。

―― 2020年からプログラミング教育が必修化するわけですが、プログラミング教育には、やはり小さな頃から取り組んだほうがよいのでしょうか?

神野 低学年で最初から、プログラミング言語を用いてプログラミングをすることは難しいかなと思いますが、“ビジュアルプログラミング言語”を用いて、プログラム自体の成り立ちや、プログラミングの流れをまず学び、接するところから始め、そのうえで本格的にプログラミングを学ぶというのがスムーズではないかと考えています。

―― “ビジュアルプログラミング言語”を学習の導入に用いることには、具体的にどういった教育上のメリットがあるのでしょうか?

神野 “ビジュアルプログラミング言語”では“動きの箱”を組み合わせる、ということをします。“命令の箱”とも表現できると思いますが、それらを組み合わせることによって、ひとつのスクリプトを完成させることができます。そして、自分で作ったロボットを、自分で作ったプログラムで、自分の思うように動かしていきます。そうすると、単に“作る”だけでも、“作って動いた”というだけでもなく、“自分の意のままに動かすことができた”という経験を得ることができます。これを体感していただけることこそが、小学生を対象としたロボット教室でビジュアルプログラミング言語を用いることの、一番のメリットではないかと思います。

想像(創造)力や課題解決能力を育む場を、学年で制限する必要はない

研修を受けた先生が教壇に立つが、プログラミング経験がなければ先生が務まらないというわけではないそうだ。

―― プログラミングの義務教育化を控えていますが、では学校の先生たちがプログラミングをどのように教えればよいかというのは課題としてよく挙げられる点です。貴校ではどのような方々が先生を務められているのでしょうか?

神野 本校のロボット教室で先生として活動されている方々は、ヒューマンアカデミーで研修を受けて合格点が出た方と、研修を受け一定期間教室を運営されている先生からOJT(オン・ザ・ジョブ・トレーニング)などで指導を受け、本部担当者より承認された方のみ、教壇に立っていただくようにしております。

―― 自社で育成された方々を先生として採用されているということでしょうか?

神野 採用しているというよりは、フランチャイズシステムで展開しておりますので、ご契約いただいたロボット教室を担当する方には、研修できちんとしたレクチャーを受けることで、先生として教壇に立つ準備をしていただいているというほうが正しいですね。

―― ロボット教室の先生になるうえで、資格などは必要なんですか?

神野 資格などは必要ないですよ。子どもたちには、理系分野へ興味を持っていただくことが一番の目標ですが、発想力や想像(創造)力を引き出す目的も、ロボット教室にはあります。ですから、子どもたちが作ったロボットやプログラムをきちんと評価することができる人、コミュニケーション能力が高い人のほうが先生に向いていると思います。

―― では、プログラミング経験を持っていなければ先生が務まらないというわけではない、と?

神野 どなたでも、お子さんと接することが好きな方でしたら、先生になっていただけますよ。

―― 小学校低学年からロボット教室を受講可能とのことですが、学習面での段階やコース分けはあるのでしょうか?

神野 未就学児向けの『プライマリーコース』から始まりまして、『ベーシックコース』、『ミドルコース』、そして『アドバンスプログラミングコース』と、4つのコースをご用意しております。

―― 4つのコースは内容面でどのように分けられているんですか?

神野 『プライマリーコース』『ベーシックコース』『ミドルコース』は、プログラミング的な要素を含んでいません。これら3つのコースは、モノづくりにフォーカスした内容です。オリジナルのブロックキットでロボットを製作し、モーターと歯車でそれを動かすといった“動きの仕組み”を最初に学びます。モーターの回転運動をどのようにパーツを組み合わせれば上下や左右の運動へ変換できるかな、とか、歯車の組み合わせをどう変えればスピードが出るようになるか、パワーを出すためにはどう改善すればよいのかなどを、手を動かしながら考えます。そうして動きの仕組みを学んだうえで、『アドバンスプログラミングコース』でプログラミングを学びます。ここからは動きをも想像(創造)すると言ったらよいでしょうか。このような流れで各コースを展開しています。

―― 各コースは学年や年齢で分けられているのでしょうか?

神野 小学1年生以上のお子さんは『ベーシックコース』から、5~6歳で未就学児のお子さんは『プライマリーコース』から学んでいただく、というように目安は設けておりますが、まずは体験授業を受けていただき、お子さんの状況に応じてスタートのコースを提案しています。

―― 生徒数が1万5000人を超えていると、なかには天才的なお子さんというのもいるのでしょうか?

神野 ブロックが大好きで、普段からご自宅でよく遊んでいるお子さんや、お父さんとプラモデルをよく作るというお子さんですと、低学年であっても高学年の生徒さんより早く作ってしまう、というのはよく見受けられます。なかでもすごいお子さんは、私どもが年に1度開催している全国大会の創作ロボットコンテストで、大人も思いつかないようなロボットを発表されることが多々ありますね。

―― 大会で発表できる機会があるというのは、お子さんにとっても記憶に残る経験になりますね。

神野 私たちもそう考えて毎年開催しております。昨年、東京大学の安田講堂で開催した全国大会には、1600名ほどの方にご来場いただきました。その大勢の観客の前で自分が製作したロボットについてプレゼンするという、大人でも足がすくんでしまうような状況は、なかなか経験できないことだと思いますね。

2016年8月に開催された『第6回ヒューマンアカデミーロボット教室全国大会』の様子。

―― そんなすごい場で発表されるのは小学生のお子さんでしょうか?

神野 年長のお子さんから中学3年生まで、幅広い参加者がいらっしゃいます。

自発的な学びの姿勢を育て、その結果として理系好きが増えてほしい

―― ロボット教室の今後についてはどのようにお考えですか?

神野 私たちが最初にロボット教室を開始した時のコンセプトとしては、サイエンスや理系分野へ興味を持ってくれるお子さんをもっともっと増やしたいということを一番に考えていました。理系分野へ興味を持つ、その足がかりになるような学びの場を作れないかということです。もちろん、理科好き・理系好きに育っていただきたいという願いは今も続いております。それに加え、自分からものごとに取り組む姿勢や、想像(創造)力、論理的思考力、ロボットを製作するなかで失敗した時にその原因をつきとめ、改善し、次に活かすという課題解決能力、そして“ロボットを作りきった”という達成感を、成功体験として積み重ねることで、自分に自信が持てるお子さんたちが増えればいいなと思っています。

―― 理系ありきではなく、ロボット教室を受講する一番のメリットは、想像(創造)力や表現力というところにまずあるということでしょうか?

神野 そうですね。形が複雑なロボットや、歯車の組み合わせが難解なロボット、微調整がとても必要なロボットなど、さまざまなロボットを製作するなかで、想像(創造)力や表現力が問われる経験を教室で積んでいただいて、いつか自分で考えだした、誰も思いつかなかったような創作ロボットを作っていただけたらなと思っています。

―― 創作性豊かなロボットを作り出せるお子さんというのが、今後どんどん登場しそうですね。楽しみです。

神野 日本国内だと、都市部では教室数もかなり増えてきています。今後は郊外など、ロボット教室がまだないエリアにも展開を拡げていく計画を進めています。また、海外でもロボット教室を展開しており、現在はアジアを中心にロボット教室が増えていますが、もっと興味をもっていただける国が増えればと思っています。

―― 教室だけでなく、例えば教材を他の機関がされている授業で使わせてほしい、といった話もあるのではないでしょうか?もともと“これ”といった教材が存在していなかった分野だとうかがいましたが。

神野 私どもも、最初はまったくのゼロからロボット教室を立ち上げましたので、教材開発については苦労したところです。そういった意味では今、学校教育でもアクティブラーニングや、プログラミング的思考力の育成がうたわれておりますので、教材の学校導入などについても、少しずつではありますがお問い合わせをいただいております。

―― 今後の教育現場において有益な価値を貴校は蓄積されているということでしょうか。

神野 そうですね。学校さまに導入いただけるパッケージもご用意しておりますので、是非お声掛けをいただければと思います。

―― いま、プログラミングを小さい時から学ぶというのはどういう意味か、改めて教えていただけますか?

神野 今後AIなどの分野が伸びるにつれ、新たな職業の選択肢が増えていくと言われています。逆にある分野では、仕事がAIに取って代わられるということも起こり得る、という時代になっていきます。そうなると、プログラミングやAIの分野に関する知識の有無が、大人になってから大きな違いを生み出すと思います。現在の子どもたちが、私どものロボット教室に通うことを、そうした分野へ興味を持つ第一歩にしてもらえたらなと思っています。

―― 英語を学ぶよりもプログラミングを学ぶほうが重要、なんていう極端な意見も聞かれますが……?

神野 どちらも習ったほうがよいと思います。例えばロボット分野でどんどん名を馳せていくにしても、そこには世界という選択肢が当然視野に入ってくると思うんです。そのときに英語(語学)に長けているといないとでは、だいぶ違うと思います。私どもも、子どもたちの未来のために、英語教育には力を入れております。これからの時代の流れを見据えると、プログラミングと英語(語学)は、絶対両方学んでおいたほうが良いと言い切れます。

神野 佳彦(かんの よしひこ)

大学卒業後、大手予備校FC事業のチェーンマネジメントや学習塾運営・生徒募集のコンサルを経て2008年、ヒューマンアカデミーに入社。現在、児童教育事業本部 チーフマネージャーを務める。

■関連サイト


カテゴリートップへ

この連載の記事

Go言語とコンテナ


Goならわかるシステムプログラミング
第20回

Go言語によるプログラマー視点のシステムプログラミング

長かった本連載も今回が最終回です。 この連載では、プログラムがコンピュータ上で動くときに何が起きているのかを、Go言語のコードを通して覗いてきました。 今回は、その締めくくりとして、コンテナについて紹介します。 現在広く利用されているコンテナ技術であるDockerのコアは、Go言語製のlibcontainerというライブラリです。 このライブラリを使って自作のコンテナを仕立ててみます。

今回の原稿にあたっては、仮想化周りでsyohexさんに細かく指摘をいただきました。ありがとうございました。

仮想化

コンテナの話に入る前に、コンテナと目的がよく似た技術である仮想化について説明します。 仮想化は、コンテナよりも先に広く使われるようになった技術ですが、 歴史的にさまざまなソリューションがあり、どのような仕組みか、どのようなメリットがあるか、どのような制約があるか、どこにフォーカスするかで分類の仕方なども大きく変わります。 ここでは、仮想化とは何かについて、軽く概要だけ触れます。

ソフトウェアは、ハードウェア、そしてその上で動くOSの上で動作します。 ハードウェアを制御し、効率よくストレージやネットワークの入出力ができるようにしたり、メモリを管理したり、CPUの処理時間を割り振ったりするのは、OSの役割です。 そのハードウェアとOSの間にもう一つOS(もしくはOSのようなもの)の層を差し込むことで、1台のハードウェア上に複数のOSやシステムを安全に共存させ、ピークの異なる複数のサービスをまとめるなどして効率的にハードを使うのが仮想化です。 具体的には、完全な機能を持った普通のOS(ホストOS)の上にハードウェアをエミュレーションする仮想化のためのソフトウェアを作り、その上にゲストOSをインストールして使います(ゲストOSも普通のOSです)。

仮想化のためのソフトウェアとしては、Oracle VirtualBoxやHyper-V、Parallels、VMWare、QEMU、Xen Serverといったものが利用されています。 これらは、大きく分けて次の2つの方式に大別できます。

  • CPUを完全にエミュレーションすることで別のハードウェアのソフトウェアも使える方式。CPUをエミュレーションするため、どうしてもパフォーマンスは大きく落ちるが、現世代の高速なCPUを使って旧世代のCPU向けに書かれたアプリケーションを動作させるのに利用される
  • 同じアーキテクチャのCPUに限定されるものの、エミュレーションが不要で高速な方式

仮想化は、クラウドコンピューティングを支える大事な技術です。 仮想化そのものをサービスとして提供しているものは、Infrastructure as a Service(IaaS)と呼ばれます。 Amazon EC2やGoogle Compute Engine、さくらのVPSなど、OS環境を提供するサービスが登場したおかげで、ソフトウェアビジネスの構造は大きく変わりました。

仮想化は低レイヤの技術の組み合わせ

仮想化そのものは、OSの上でOSを動かすという、大掛かりで複雑な仕組みです。 ゲストOS上で動いているプログラムから、システムコール呼び出しなどの特権的な命令でホストOSが呼ばれてしまうと、仮想化していたつもりがおかしなことになってしまいます。 あるゲストOSで設定したCPUの状態(例えば、例外発生のモード変更)が、別のゲストOSに影響を与えてはいけません。ホストOSとゲストOSの間も同様です。 こうした要件は、システムを仮想化するのに満たすべき必要条件として、1974年に論文化されています。

最新のIntel系CPUは、この「PopekとGoldbergの要件」を満たす仮想化支援機能として、VT-xというものを備えています。 これは、第5回 Goから見たシステムコールで触れたユーザモード3)、特権モード0)の下に、ハイパーバイザー用OSのモード-1)を追加し、それを使うことで、ゲストOSからホストOSへの処理の移譲が必要な操作を効率よくフックできる仕組みです。 一見すると、WindowsやmacOSというホストOSがあり、その上のアプリケーションとして仮想化のシステムがいて、その中でゲストOSが実行されているように見えますが、CPUから見ると、ホストOSの下により強力な権限をもったレイヤーが追加されているというわけです。

現在のIntel系CPUには、VT-x以外にも、ゲストのメモリアドレスとホストのメモリアドレスとを変換する拡張ページテーブル、外部ハードウェアとのアクセスでホストOSを介さずに実行(PCIパススルー)できるようにするVT-d、ネットワークの仮想化のVT-cなど、さまざまな仮想化支援機能が実装されています。 これらの支援機能により、ハイパーバイザーが毎回割り込みをしてホストに投げなくても済むようになり、ネイティブに近い速度で仮想化が動くようになってきました。

ハイパーバイザーの側では、WindowsのHyper-V、macOSのHypervisor.framework、LinuxのKVMといった、OSが提供する支援機能を利用します。 高効率な仮想化は、CPUやOSなどさまざまな低レベルのレイヤーの手助けにより実現しているのです。

準仮想化

本文で紹介した仮想化の手法は「完全仮想化」と呼ばれるものです。これに対し、「準仮想化」について紹介している書籍も多くあります。 完全仮想化と準仮想化とでは、ハイパーバイザーやVirtual Machine Manager(VMM)と呼ばれるシステムがホストOS(あれば)やゲストOSの調停を行うのは同じですが、 OSの実行に必要だけど他に影響を与えうる命令(センシティブ命令)の扱いが異なります。

  • 完全仮想化では、ゲストOSがホストOS上にインストールされ、ゲストOSは自分が仮想環境で動いているのを意識する必要がない。 他に影響を与えうる命令を実行すると、割り込みが発生し、ハイパーバイザーがその処理を代行する

  • 準仮想化は、ハードウェア上にインストールされたハイパーバイザー(ホストOSはない)上で動作する。 ゲストOSは、自分がハイパーバイザーの上で動作していることを意識しており、他に影響を与えうる命令の代わりにハイパーバイザーを呼ぶ(hypercall)

準仮想化では、事前にセンシティブ命令を書き換えておくことで仮想化を実現します。 ハイパーバイザーの仕様に合わせてカスタマイズされたゲストOSを使うため、ホストOSは不要であり、全体のレイヤーが薄くなります。 また、センシティブ命令をハイパーバイザーによる例外処理の割り込みではなく効率の良い呼び出しに書き変えたり、パフォーマンス・チューニングを施したりすることで、完全仮想化よりも高いパフォーマンスを実現できます。 Amazon EC2も、初期には準仮想化メインで運用されていました。

完全仮想化では、センシティブ命令などの呼び出しに対してハイパーバイザーが割り込みを行い、適切に変更を局所化させます。 この方法ではパフォーマンスに難がありますが、本文で紹介したようなさまざまな支援機能をCPUが提供することで高速化が施され、欠点が改善されてきました。

準仮想化には、Windowsなどの外部からのカスタマイズが難しいOSは開発元の協力がないと動かせず、GPUなどの新しいハードウェア対応が難しいといったデメリットもあります。 そのため、徐々に完全仮想化のほうが優勢になっていきました。 Amazon EC2も、リストを見る限り、現在ではほぼ完全仮想化となっています(ただし、インスタンスタイプごとに、完全仮想化(HVM)と準仮想化(PV)とで対応具合が異なります)。

コンテナ

ここまでの説明からわかるように、仮想化は、使いたいサービスだけでなくOSも含めてまるごと動かすことが前提の仕組みです。 そのため、たとえばゲストOSとホストOSが同じLinuxであればカーネルやシステムのデーモンを重複してロードすることになり、無駄にメモリを消費してしまいます。

そこで、「OSのカーネルはホストのものをそのまま使うが、アプリケーションから見て自由に使えるOS環境が手に入る」の実現に特化したのがコンテナと呼ばれる技術です。 「アプリケーションが好き勝手にしても全体が壊れないような、他のアプリケーションに干渉しない・されない箱を作る」という機能だけ見ると、仮想化もコンテナも同じであるため、コンテナのことを「OSレベル仮想化」と呼ぶこともあります。

仮想化ではストレージをまるごとファイル化したような仮想イメージを使ってアプリケーションを導入しますが、コンテナでもイメージと呼ばれるものを使います。 Amazon EC2 Container Service、Azure Container Service、Google Container Engine(GKE)、さくらインターネットのArukas(β版)など、コンテナのイメージ上でアプリケーションのデプロイが可能になるサービスもあります。

一口にコンテナ技術といっても、内部では複数の機能を組み合わせて実現されています。 たとえばLinuxでは、コンテナを実現するためのOSカーネルの機能として、コントロールグループ(cgroups)および名前空間(Namespaces)があります。 これらの機能を組み合わせることで、さまざまなOSのリソースを、仮想メモリを用意するように気軽に分割できます。

コントロールグループ(cgroups)は、次の項目の使用量とアクセスを制限できるようにするカーネルの機能です。

  • CPU
  • メモリ
  • ブロックデバイス(mmap可能なストレージとほぼ同義)
  • ネットワーク
  • /dev以下のデバイスファイル

また、カーネルでは、次のような項目について名前空間(Namespaces)を分離できるようになっています。

  • プロセスID
  • ネットワーク(インタフェース、ルーティングテーブル、ソケットなど)
  • マウント(ファイルシステム)
  • UTS(ホスト名)
  • IPC(セマフォ、MQ、共有メモリなどのプロセス間通信)
  • ユーザー(UID、GID)

これらの機能については、次節でコンテナを自作しますが、そのコードを見れば概要がつかめるはずです。 より詳しい情報は、Surgoさんによる下記の記事などを参照してください。

コンテナと仮想化の関係

仮想化ではOSを起動する必要があるため、起動には長い時間がかかります。一方、コンテナはプロセスを起動するように仮想環境を構築できます。 コンテナのほうが効率が良いので、コンテナが仮想化を置き換えるとか、仮想化は古いというわけではありません。 コンテナのツールとして人気のDockerは、Linuxではコンテナだけを利用しますが、macOSやWindowsではOSが提供する仮想化の仕組みを使ってLinuxを動かし、その中でコンテナを利用します。 コンテナの中で動かしたいシステムがLinuxであれば、一度Linuxを動かす必要がありますが、現在サーバー開発ではLinuxがよく使われ、提供されているDockerイメージもほぼLinuxなので、Linux以外には厳しい状況です。

例外がFreeBSDです。 FreeBSDには昔から、Linuxバイナリを動かすエミュレーション機能があるので、FreeBSDをホストにしてDockerでLinuxイメージを使うと、FreeBSDのカーネルのままJailでコンテナ化し、コンテナ内部ではLinuxバイナリを動かすという動きになるようです。

また、Windowsは軽量なLinuxのコンテナ相当のWindowsコンテナと、仮想化もプラスして他のOSも起動できるHyper-Vコンテナの2種類あります。

libcontainerでコンテナを自作する

現在、Dockerのコアとなっているのは、Go言語で書かれているlibcontainerというライブラリです。 このライブラリを利用してGo言語でコンテナを実装し、Linux上での起動に挑戦してみましょう。 今回のコードのサンプルはLinuxバイナリが直接実行できる環境でしか動作しません。 WindowsやmacOSをお持ちの方は、VirtualBoxなどの仮想環境をインストールして、Ubuntu Linuxなどを入れて試してください。

なお、言うまでもありませんが、コンテナの実装はDockerだけではありません。 Dockerにはrktというライバルもいて、こちらを推す人も少なくないのですが、rktはlibcontainerと違ってライブラリとして使うことが想定されていないので、今回はlibcontainerを取り上げます。

Dockerとlibcontainer

当初、Dockerは、Linuxにおけるコンテナ機能のためのユーティリティであるLXCのラッパーでした。現在のDockerは、libcontainerをベースに書き直されたものです。 さらにその後、libcontainerを含むコア部分はrunCというツールになりました。 それによってlibcontainerがなくなったわけではなく、現在もrunCのディレクトリ内に同梱されています。

runCは、Open Container Initiativeに寄贈されています。 OCI傘下になったことで、今後はさまざまなプラットフォームにも対応されていくことでしょう。

なお、ここで紹介する自作はコンテナは実験的なものであり、実用面ではrunCコマンドやDockerを使うほうがはるかに簡単でお手頃です。

OSのブートに必要な下準備

コンテナとなるコードを書く前に、コンテナ内で新しいOSを起動するために必要なファイル一式を用意する必要があります。 ここではサイズが小さいAlpine Linuxのイメージに含まれているファイルを利用させてもらうことにします。 必要なファイルは下記の要領で取得できます。

$ docker pull alpine ⏎
Using default tag: latest
latest: Pulling from library/alpine
2aecc7e1714b: Pull complete
Digest: sha256:0b94d1d1b5eb130dd0253374552445b39470653fb1a1ec2d81490948876e462c
Status: Downloaded newer image for alpine:latest
 
$ docker run --name alpine alpine ⏎
 
$ docker export alpine > alpine.tar ⏎
 
$ docker rm alpine ⏎

これで、ファイルシステムの中身がtarファイルとして取り出せました。 作業フォルダにrootfsというフォルダを作り、このtarファイルを展開しておきましょう。

$ mkdir rootfs ⏎
 
$ tar -C rootfs -xvf alpine.tar ⏎

libcontainerを利用してコンテナを作る方法は、libcontainerライブラリのREADMEにほとんどそのまま書いてあります。 それを参考にコンテナを作成してみましょう。

まずは必要なライブラリを取得してきます。 libcontainerライブラリのほかに、unixパッケージをインストールしてください。

$ go get github.com/opencontainers/runc/libcontainer ⏎
$ go get golang.org/x/sys/unix ⏎

それでは実装を見ていきましょう。

libcontainerを使うときは、main()関数のほかに、init()関数を定義します。 これは、Linuxの起動時には最初に呼ばれるinitプロセスが必要なためで、その処理をinit()以下に集約します。

コンテナの生成とinitプロセスの起動とを1つの実行ファイルで実現するには、自分自身をinitプロセスとして呼び出すモード(InitArgsの部分)をmain()の中で利用します。 その部分までのコードを下記に示します。

package main
 
import (
    "github.com/opencontainers/runc/libcontainer"
    "github.com/opencontainers/runc/libcontainer/configs"
    _ "github.com/opencontainers/runc/libcontainer/nsenter"
    "log"
    "os"
    "runtime"
    "path/filepath"
    "golang.org/x/sys/unix"
)
 
func init() {
    if len(os.Args) > 1 && os.Args[1] == "init" {
        runtime.GOMAXPROCS(1)
        runtime.LockOSThread()
        factory, _ := libcontainer.New("")
        if err := factory.StartInitialization(); err != nil {
            log.Fatal(err)
        }
        panic("--this line should have never been executed, congratulations--")
    }
}
 
func main() {
    abs, _ := filepath.Abs("./")
    factory, err := libcontainer.New(abs, libcontainer.Cgroupfs,
                                     libcontainer.InitArgs(os.Args[0], "init"))
    if err != nil {
        log.Fatal(err)
        return
    }

main()関数の以降の大部分は、生成するコンテナの環境設定です。 ホスト名(Hostname)やマウントするファイルシステム(Mounts)などをconfigインスタンスに設定していきます。 あらかじめ用意した仮想OSの実行環境もここで指定します(Rootfs)。


    capabilities := []string{
        "CAP_CHOWN",
        "CAP_DAC_OVERRIDE",
        "CAP_FSETID",
        "CAP_FOWNER",
        "CAP_MKNOD",
        "CAP_NET_RAW",
        "CAP_SETGID",
        "CAP_SETUID",
        "CAP_SETFCAP",
        "CAP_SETPCAP",
        "CAP_NET_BIND_SERVICE",
        "CAP_SYS_CHROOT",
        "CAP_KILL",
        "CAP_AUDIT_WRITE",
    }
    defaultMountFlags := unix.MS_NOEXEC | unix.MS_NOSUID | unix.MS_NODEV
    config := &configs.Config{
        Rootfs: abs+"/rootfs",
        Capabilities: &configs.Capabilities{
            Bounding: capabilities,
            Effective: capabilities,
            Inheritable: capabilities,
            Permitted: capabilities,
            Ambient: capabilities,
        },
        Namespaces: configs.Namespaces([]configs.Namespace{
            {Type: configs.NEWNS},
            {Type: configs.NEWUTS},
            {Type: configs.NEWIPC},
            {Type: configs.NEWPID},
            {Type: configs.NEWNET},
        }),
        Cgroups: &configs.Cgroup{
            Name: "test-container",
            Parent: "system",
            Resources: &configs.Resources{
                MemorySwappiness: nil,
                AllowAllDevices: nil,
                AllowedDevices: configs.DefaultAllowedDevices,
            },
        },
        MaskPaths: []string{
            "/proc/kcore", "/sys/firmware",
        },
        ReadonlyPaths: []string{
            "/proc/sys", "/proc/sysrq-trigger", "/proc/irq", "/proc/bus",
        },
        Devices: configs.DefaultAutoCreatedDevices,
        Hostname: "testing",
        Mounts: []*configs.Mount{
            {
                Source: "proc",
                Destination: "/proc",
                Device: "proc",
                Flags: defaultMountFlags,
            },
            {
                Source: "tmpfs",
                Destination: "/dev",
                Device: "tmpfs",
                Flags: unix.MS_NOSUID | unix.MS_STRICTATIME,
                Data: "mode=755",
            },
            {
                Source: "devpts",
                Destination: "/dev/pts",
                Device: "devpts",
                Flags: unix.MS_NOSUID | unix.MS_NOEXEC,
                Data: "newinstance,ptmxmode=0666,mode=0620,gid=5",
            },
            {
                Device: "tmpfs",
                Source: "shm",
                Destination: "/dev/shm",
                Data: "mode=1777,size=65536k",
                Flags: defaultMountFlags,
            },
            {
                Source: "mqueue",
                Destination: "/dev/mqueue",
                Device: "mqueue",
                Flags: defaultMountFlags,
            },
            {
                Source: "sysfs",
                Destination: "/sys",
                Device: "sysfs",
                Flags: defaultMountFlags | unix.MS_RDONLY,
            },
        },
        Networks: []*configs.Network{
            {
                Type: "loopback",
                Address: "127.0.0.1/0",
                Gateway: "localhost",
            },
        },
        Rlimits: []configs.Rlimit{
            {
                Type: unix.RLIMIT_NOFILE,
                Hard: uint64(1025),
                Soft: uint64(1025),
            },
        },
    }

最後に、ここまでの部分で作ったconfigインスタンスをCreate 関数に渡してコンテナを作ります。 コンテナ内部で起動するプログラムは&libcontainer.Processで指定できます。 ここではシェルを起動するようにしましょう。


    container, err := factory.Create("container-id", config)
    if err != nil {
        log.Fatal(err)
        return
    }
 
    process := &libcontainer.Process{
        Args: []string{"/bin/sh"},
        Env: []string{"PATH=/bin"},
        User: "root",
        Stdin: os.Stdin,
        Stdout: os.Stdout,
        Stderr: os.Stderr,
    }
 
    err = container.Run(process)
    if err != nil {
        container.Destroy()
        log.Fatal(err)
        return
    }
 
    _, err = process.Wait()
    if err != nil {
        log.Fatal(err)
    }
 
    container.Destroy()
}

以上でコンテナの実装は終了です。 さっそく実行してみましょう。 コンテナの中でシェルが起動するはずです。 そのシェルで/bin/hostnameを実行すると、上記コードで設定したホスト名である「testing」が表示されることがわかります。

$ go build -o container container.go ⏎
 
$ sudo ./container ⏎
[sudo] shibuのパスワード: [sudoパスワードを入力] ⏎
/bin/sh: can't access tty; job control turned off
/ # /bin/hostname ⏎
testing

なお、このconfig構造体も含め、各サブ構造体はJSONでのシリアライズが可能なタグが付いており、JSONから読み込むこともできます。 コンテナ構造体を使えば、プログラムを使って外部からコンテナを操作することも可能です。

// コンテナ内部で動作しているプロセスIDのリストを[]int形式で返す
processes, err := container.Processes()
 
// CPU、メモリ、I/O、コンテナの統計情報取得
stats, err := container.Stats()
 
// コンテナを停止
container.Pause()
 
// コンテナを再開
container.Resume()
 
// コンテナのinitプロセスにシグナル送信
container.Signal(signal)

まとめ

最終回として、最近話題になることが多いコンテナについて説明しました。 どちらかというと、Kubernetes、Docker Swarm、Mesosといった大規模なオーケストレーションの方面に話題がシフトしていっていますが、今回の話はその下で行われている基礎の説明になります。

コンテナはOSが持つリソースに「壁」を作って、そのプロセス専用の環境を作ることでした。 連載ではネットワーク、ファイルシステム、プロセス、並列処理、メモリをそれぞれ何回かに分けて紹介してきました。 コンテナが扱うものはそのすべてです。

付録:この連載で扱わなかった低レイヤの話題

Go言語でサポートしていないPOSIXの機能

次のような機能はGo言語の標準ライブラリでサポートしていないため説明しませんでした。 例えば、共有メモリのシステムコールも定数だけはコードにあったりはしますが、Go言語のランタイムにないということはあまり需要もなかったり、代替手段があったりします。

  • POISXの共有メモリ
  • POSIX MQ
  • POSIX非同期I/O
  • UDPConn.RecvMsgUDPConn.SendMsgの制御命令バイト列の読み書き

IEEE752の浮動小数点数や数値の表現、SIMD

数値周りも、本来であればきちんと理解すべきトピックです。しかし本連載ではCPU内部の話は取り扱わず、OSとのやりとりに関係する部分にフォーカスしたこともあり、システムで数値をどう扱っているかについては触れませんでした。

Go言語でのSIMDは昔よりはやりやすくなっていて、x86命令のバイナリ表現を手書きする必要はなくなってきていますが、アセンブリを書かなければならない点は変わっていません。

ロギング

ロギングに利用される定番ツールとしては、syslog/logstash/fluentdなどがあります。 syslogは、かつてはシステムプログラミングの話題の中で説明されることもありましたが、現代においてはデータ処理のパイプラインを担うミドルウェアとして扱われることが多く、OSのサービスからは少し離れます。 そのため、本連載では特に取り上げませんでした。

GUI

GUIは、OSごとに差異が大きすぎる上に、それなりに中身のある話をするとなるとボリュームが極めて大きくなるので、本連載では取り扱いませんでした。 おそらくフォントのハンドリングとテキスト描画だけで本が書けます。

暗号化/乱数

OSには、暗号論的擬似乱数生成器など、暗号化にかかわるAPIもいくつかあります。 macOSのSecurity FrameworkであるSecKeychainAddGenericPassword()や、WindowsのCryptProtectData、GnomeキーリングやKwalletなどのセキュアなデータ保存など、便利な機能がたくさんありますが、OS間の差異も大きく、暗号も背後にある理論的な説明が多くなりすぎてしまうため本連載では取り上げませんでした。

Go言語で作成したプログラムのブートストラップ

システムプログラミングやカーネルの本の締め括りとして必ずといっていいほど取り上げられるのは、プログラムの起動シーケンスの説明です。 興味がある方は、英語ですが、次のブログ記事などをご覧ください。

あとがき

20回に渡り、Go言語によるシステムプログラミングについて取り上げてきました。 連載にあたって心がけたのは次の3点です。

  • システムコール周辺の関数の使い方を紹介するだけではなく、その後ろで一生懸命仕事をしているカーネルのようすが見えるようにする
  • 2020年が近い年に新しく書き下ろすので、過去の本になかった新しいトピックを入れる(古いトピックを削る)
  • Linuxなどの特定の環境だけでなく、WindowsやmacOSを使っている人でもなるべく幅広く読めるようにする

Go言語を題材として選択したのはランタイムの奥まで気軽にのぞける言語だからです。Go言語そのものの説明が目的ではなかったため、Go言語を学ぼうと思っていなかった人にはとっつきにくい内容もあったかもしれません。 しかし、コンピュータシステムを学ぶ言語の選択肢が事実上C言語ばかりというなかで、間口を広げる貢献はできたと思います。 次は皆さんが、自分の得意な言語でトライしてみてください。

記事の執筆にあたっては、会社のメンバーとの会話や社内の読書会、プログラマー仲間との普段からのチャットなどから学んだこと、着想を得たことが助けとなりました。 時には、分からないことを質問して教えてもらったり、原稿のレビューをしてもらったりと、一人では超えられなかった壁を超えることができました。 また、編集の鹿野さんには、日本語の校正にとどまらず、読者に伝わりやすくするために説明の順序を大幅に入れ替えたり、雪だるまの挿絵をたくさん入れていただきました。 もちろん、完走できたのは、最後までお付き合いしていただき、時には厳しく指摘もしていただいた読者のみなさんのおかげです。 どうもありがとうございました。

脚注


カテゴリートップへ

この連載の記事

落合陽一のライバルはエジソン!? 「現代の魔法使い」の頭の中


スペシャルトーク@プログラミング+
第13回

みんな違う生き方をしてもそれを吸収できるように、コンピューターを発達させている。10年後には、きっとそうなっている

2017年06月15日 12時00分更新

文● 吉川あかり

 5月20日(土)、落合陽一氏による講演会「魔法の世紀を生きる子供たちへ」が秋葉原にて開催された。この講演は、株式会社UEIが手がける秋葉原プログラミング教室が主催し、子どもたちだけでなく一般にも解放したもので、これからを生きる子どもたちに向けて、メディアアーティストの落合氏がメッセージを送った。

 落合氏といえば、研究者、筑波大学学長補佐、またPixie Dust Technologies.IncのCEOである。「現代の魔法使い」とも呼ばれ、最近はテレビなどに出演し、そのたくさんの肩書きや見た目がミステリアスだ。彼は一体何者なのか、疑問に思われている方も多いだろう。今回の講演では、そんな彼の頭の中を覗けるような、興味深い考え方を聞くことができた。落合氏の研究分野であるプログラミングや表現だけでなく、これからの生き方・働き方にまで触れられ、大人が聞いても参考になる非常に充実した内容となった。今回は、この講演の中から特に面白かった部分を選んでお届けする。

みんなで同じことをしていてもしょうがない時代になってきた

 こんにちは、落合陽一です。僕は29歳なので、いま10歳の人はあと19年するといまの僕と同い年になりますね。最近、僕も子供が生まれたのですが、その子が10歳になる頃には、僕は40歳になるんだなぁと思っています。僕は、ふだんは大学の先生、自分の会社の経営、そして、メディアアーティストをしています。それ以外は世界経済フォーラムとか東京都とか大学運営とかに関わりながら、次の時代を探っています。

僕が最近にテーマにしているのは、人類にとっての「近代」と「現代」を終わらせるためにどうやって技術インフラを作れるのか。ちょっとお子さんには難しいと思うんですが。今日はその話をしていこうと思います。

 まず、何の話から始めようかな。制服って何のために着ていると思う? 「着ろ」って言われているから? 僕らが制服を着始めたのって、結構昔なんですね。例えばこの写真は明治時代くらいの、教育勅語が発令された頃の風景なんですけど、この頃から日本の小学校って前習え、気をつけをして、机で同じ教科書を持って勉強するってスタイルになっています。

明治時代の小学校の写真。このころから制服を着ている

 確かに、制服がみんなばらばらだったら統一感がないじゃん。変な服を着たら同じ服を着ている人たちからいろいろ言われるじゃん。もしかしたら普通の服を着るより安いかもしれない。全員同じ服をたくさん作ったら安いから。でもこの理屈って、軍隊教育にきわめて近くって、なぜかというと、近代では日本の国を強くしなきゃいけなかったから。全員で同じ目標に向かって、全員で同じことをして、全員で同じ教科書を読んで、全員で同じ言葉を喋って、全員で同じ時間割で行動するっていうのがものすごく大切だったんですね。そうすると何ができるかって言うと、日本が工業化していくときに、みんなで同じものが作れるようになる。工業製品が、お店で買ってきて全部バラバラな形してたら困るよね。たとえば、Mac買ってきてディスプレイが最初から動かなかったら困るじゃん。そんな風に、近代では全部の品質を保つというのがすごく大切なことだった。たとえば車とかiPhoneとか、20世紀の感覚の中では最高傑作だとは思うんだけど、僕らの時代ではもっと違うことをしていかないといけないんじゃないかなって僕は思っています。つまり、決まりきった工業製品みたいな人を作っても、それはコンピューターの方が得意なので、人間の出る幕はないなと僕は思っているわけです。つまり、みんなで同じことをしていてもしょうがない時代になってきたなあと僕は思っています。

 でも、ちょっと考えてみようと。なんでみんな朝同じ時間に学校に行かないといけないと思いますか? 学校って8時45分くらいに始まると思うんだけど、僕らが時間を決めて行動し始めたのって、今はみんなあたりまえに思っているんだけど、実は1700年代の終わりくらいから1800年くらいからなんですよね。それよりも前の人たちって、業界によって何時に起きても良いし、1年の分け方も違ってたりしたんだけど、昔マルクスっていうドイツの偉い人がいまして、その人が人間の生産活動を時間で決めると良いよって言ったんですね。それから僕たちはみんな腕時計をつけて時間に間に合うように行動したりとか、待ち合わせには遅れないのが普通だよって教えるようになりました。だから近代って時間でコントロールしてたんだけど、今では地球の裏側と仕事してる人にとっては時間って関係なかったりするし、みんな同じ時間に学校に行かなくても、頭いい子は午前中に授業終わっちゃっても良いと思わない? 本当は。僕はカリキュラムにそって同じ時間で行動する必要はないと思うんだけど。そういったような、みなさんが学校で習っていることって、今後全く当たり前じゃなくなっていくと思います。こういうことを今知っていると楽しいかなと思って、今日この講演を企画してもらいました。

バラバラのようなんだけど、でも全部やっているのはコンピューターをプログラミングすること

 僕自体は、普段は研究者をしています。何の研究をしているかというと、俗にいうとAIとか、コンピューターサイエンスの研究をしています。たとえば、レーザーで空間に絵を描く研究とかをしています。空気を直接光らせて空中に絵を描く研究なんですが、どうやっているかっていうと、レーザーを使って、空中に光を直接描くっていう。仕組みは非常に単純で、プラズマなんですね。あと最近やっている研究っていうと、これは赤いところだけ音がでるスピーカーとか。そういう研究をしています。

外出先ではホロレンズをつけて仕事をすることもある

 これらを会社で作って売ったりとか、研究室で研究したりとか、日本や海外で発表したりとか、そういうことをしています。僕らが得意にしていることは何かっていうと、そういう物質自体、モノとか光とかを、コンピューターを使ってプログラミングしていくことです。だから3Dプリンターでモノを作って、その構造がどうやって変形するのか、動くのか、っていうのをCGで設計して、コンピューターのプログラムに入れて、出てきたものを動かす、ということをしています。こんなことばっかり研究していて、3Dプリンターがいっぱいあったりとか、レーザーを打つ装置があったりとかします。全然バラバラのようなんだけど、でも全部やっているのは「コンピューターを使って人間には設計できないことをプログラミングする」っていうのが僕らの仕事です。プログラミングって言ったらだいたいプログラミングとかiPhoneのアプリを作ったりとか、画面にCGの映像を作ったりを思いつくと思うんだけど、それと同じ理屈で、どうやったら僕らが生きているこの世界にCGっぽいものを作ったり、形あるものを作れるか、って言う研究をしています。だから、やってることはすごい単純なんですよね。入出力の体験価値を見ながらソフトウェアの問題を解いているんです。

ライバルはエジソン

 僕がライバル視している人がいて、ぜひ紹介したいんですけど、この人です。知ってます? この人。トーマスエジソンです。いっぱい発明した人ですね。この人言っていることがめちゃくちゃでおもしろいんですが、たとえばこの人、映像装置は一人一個持つべきだって言っているんですね。エジソンはキネトスコープという覗き込んで映像を見る装置を作って、一方でリュミエール兄弟という兄弟がプロジェクターを作りました。プロジェクターはみんなで映像を見る装置で、キネトスコープは1人ずつ映像を見る装置です。どっちが当時売れたと思いますか? 当時、世界的にプロジェクターが普及しました。だけど、今我々が生きている世界ってスマホを持っているじゃないですか。つまり、1人1個映像装置を持っているんですね。エジソンの言ったことは、彼が生きていた時代にはコスト面で普及しなかったのですが、今は実現しているわけです。

 エジソンが言った面白いことは他にもあって、音楽は体験するものであって、蓄音機は他の用途で使うべきだ、例えば音声コミュニケーションとも言っています。蓄音機で音楽を聴いたエジソンは、音楽は生で聴いた方が良いよ、こんなもので聞くべきじゃないって言ったんですが、結局CDが流行ったわけじゃないですか。でも今スマホで音楽を聴くよりコミュニケーションをしていますよね。これも正しい見方だったと思うんですね。



ライバルはエジソン 人工知能で笑ってもらったエジソン

 極めつけは、エジソンは最初電気は直流でいこうと言っていて、もう一人テスラという人がいて交流がいいじゃんって言っていたんです。交流の方が遠くまで電圧を下げずに送れるんだけど、直流は直流で変換しなくていいから便利だと。コンセントって交流なんですけど、僕らが使っているものって、熱が発生するものとモーターで動いているもの以外は、だいたい直流で動いているんです。USBもそうだし、パソコンもそうだし、液晶もそうだし、そこにあるプロジェクターもそうだし、僕らは直流の世界で生きているんです。イメージしてもらうと、おうちのコンセントが全部USBだったらiPhone充電するの楽じゃん? でも、もし山奥でUSBしかなかったら家電とか動かないよね。だから、僕らが普段使っている冷蔵庫とか洗濯機には交流が便利なんだけど、それ以上の情報で使っているようなものってほとんど直流で動いている。だから、インフラとしては交流が便利なんだけど、それ以上は直流の世界になっていくのは目に見えていることで、エジソンはそれをわかってたんじゃないかなって思うんです。

エジソンはメディアアーティストなんじゃないかなって思っています

 エジソンは発明家って言われているんですけど、僕はメディアアーティストなんじゃないかなって思っています。エジソンが作ったものって、最初大幅にはずしているんですよ。覗き込む映像装置も誰も使ってなかったし。でも、エジソンが最初に作るからエジソンすげー!ってみんな思うわけです。エジソンの時代はコンピューターがなかったので、どうやったら電気から直接見えるものを作れるかっていうのが仕事で、僕らの時代は、どうやったらコンピューターを使ってエジソンがやったことをもっと新しい観点でやるかっていうのが大事なんだと思います。

Googleがない世界では算数はできないかもしれないけど、Googleがある世界では算数ができるんだから、劣等感を持つ必要はない

エジソンとフォードができたことを今やってもしょうがない

 フォードって知ってますか? エジソンとフォードは仲が良くて、フォードはエジソン電気会社に入社してきた社員なんですね。彼はそのあと自動車を発明します。この自動車は多分世界で1番売れた車の一つです。フォードのなにがすごいかというと、全く同じものを工場で並べて作るっていうことを普通にした人です。僕らがイメージする工場は、部品が並べられていて、歯車をつけて…というものですよね。そしてエジソンがその考え方で電化製品を作った人です。初めて自動車を作った人と初めて電化製品を作った人は友だちだったんですね。他にも、この2人は最初に電気自動車を作りました。時速40キロで100キロ走る良い車を作ったんだけど、エジソンだったらこのまま商業化して大失敗するのがいつものパターンなんですけど、フォードは感覚が良いので、「エジソンさんバッテリーを作るのがとてつもなく大変です、やめましょう」っていうことで実用しなかったんですね。だけど100年たったいま、あらゆる電気自動車メーカーができていて、100年前にできなかったことが割とできるようになったんですね。半分はコンピューターのおかげで、半分はいろんな技術のおかげです。

 だから、この時代にできたことを今やってもしょうがないと思うんですよね。工場でみんなで並んで同じものを作ることって、みんなで同じことをやっていた時代の人には合っていたと思うんですね。みんなで同じやり方をしていたら、バラバラな形のものはできないから。なんだけど、今ってもうそういう時代じゃなくて、みんなばらばらでどうやって生きていくかって時代だと思います。

落合氏が、子どもたちの意見を聴きながら行われたので、終始楽しい雰囲気だった

 みんな多分あと10年くらいしたら、学校でせーのって授業を聞く必要はなくなると思うんだよね。あと、試験でもスマホを持ち込めるようになると思うんです。僕は大学の先生なので、いつもどうやったらスマホを持ち込んでテストできるかって考えています。だって、インターネット使ったことない小学生いますか? Google使ったことない小学生、いないですよね。Google検索できない場所に缶詰にされて問題を解くっておかしくないですか? わからなかったらググれよ、って。今はインターネットがあるのに、わざわざインターネットのない世界でテストをするっておかしいですよ。それでわかる能力って、僕はインターネットのない世界では正解が答えられますっていうことじゃん。実際インターネットがある世界では誰でも解ける問題なら、それって意味ないなって思うんですよ。たぶん、将来の僕らってものの解き方を変えていかなきゃ行けないんだろうなって思っています。

 テストの時、Googleがない世界では算数はできないかもしれないけど、Googleがある世界では算数ができるんだから、劣等感を持つ必要はないよね。逆上がりができなくても、身体にロボットがついていない世界ではできないけど、ロボットがついていれば逆上がりができるんだから、問題ない。たとえば僕はスクーターに乗っていれば100メートル10秒以下で走れるわけですが、スクーターのない世界ではできないわけです。でもそれはずるじゃなくてパラメーターの問題ですよ。だからあんまり人と自分を比べてもしょうがないので、比べるものはテクノロジーで解決する世界になれば良いなって思います。

テクノロジーが発展したら、健常者も障がい者も区別はないし、人と機械も違いはないし、男だろうが女だろうが関係ない

コンピューターがあると、いろんな人たちが出てくる

 みんなに覚えてほしい重要なことがあって、今僕らが標準って言っている人間って、昔の人が人間とは何かというのを考えて、その上で法律を決めてつくったものが多いです。男女を平等にしようって言っているのも、標準的な人間とは何かを考えたから男女の違いが生まれたし、機械と人間の差を考えたから、機械がやれば良い仕事を人間がしていたりするし、健常者というものを定めたから障がい者というものが生まれたと思うんです。僕らの時代はここはもっとハイブリッドになっているので、あんまり関係ないんですよね。だって肌の色が何色かって関係ないわけじゃないですか。ポケモンGOだって肌の色を選べたりするわけです。腕がなくたって3Dプリンターで作って動けば何ら関係ないわけです。コンピューターがあると、いろんな人たちが出てくるわけです。それは今までできなかったからいなかっただけで、テクノロジーが発展したら、健常者も障がい者も区別はないし、人と機械も違いはないし、男だろうが女だろうが関係ないわけです。その違いは200年以上前の人が決めたことで、今もこれを守る必要ってないんです。そういうことが当たり前だと思わない大人になってほしいなって僕は思います。

 最近は自動で動く車いすの研究をしています。後ろで車いすを押す人って要らないと思うんですよね。その位置に360°カメラが合って周りの環境を認識すれば、自動運転できるわけです。そうすれば車いすに乗っている人は自分で自由に動けるようになる。もしお店で買い物するときは、ヘルパーさんがVRモードとかで入って、おつりください、って人間がやって、それ以外のほとんどのことは機械が自動運転で動かすことができるんです。

 他にも、今、日本は高齢化社会って言われてるんだけど、きっとポジティブになると思うんだよね。少子高齢化ってみんな言ってるでしょ? 少子高齢化って、だいたいみんなでおじいちゃんたちを支えようっていうイメージですよね。

高齢化社会は、きっとポジティブになる

でも今僕たちがとるべき少子高齢化の形は、おじいちゃんたちはいっぱいいるけど、支えるのはロボットや人工知能に任せて、僕たちは好き勝手やってっていう世界です。もしかしたらおじいちゃんおばあちゃんも、ただ支えられるんじゃなくて、頭にホロレンズつけたり、自動車いすに乗ったりして、自由に動いてる世界にもなるかもしれない。そのような世界を作るために、どのような装置を作るかって言うのが重要だと思います。

ワークアズライフ

 大人になると、ワークライフバランスが大事だって言われます。どういう意味かって言うと、よいワークがよいライフを生み出して、よいライフがよいワークを生み出すってよく言うんですね。僕はこれ、嘘っぱちだと思っています。僕は最近、ワークアズライフだと思っています。どういう意味かって言うと、みんなはもうタイムマネージメントの時代じゃないと思うんですよ。何が楽しくて何が楽しくないか、何がストレスで何がストレスじゃないかって時代に生きていくんだと思います。時間割に従うことなく、みんな同じことをやらなくても、みんなばらばらのことをやれる時代だと思います。20世紀までの標準化の時代、何が標準かって言うのを考えている時代だったと思うのですが、僕らの時代はどういうパラメーターがあなたにありますかっていう時代になると思うんです。時間でコントロールするんじゃなくて、どこがストレスでどこがストレスじゃないかっていうのが重要になります。

ワークライフバランスではなく、ワークアズライフ

 それってどういうことかって言うと、全員にプログラミングをやれって言っているわけではないんです。その人にとってストレスがかかることはやらないほうがいいっていうだけです。でも、ストレスがちょっとかかることをどれだけ混ぜていくかということによって、全部ストレスがない状態になるとあんまり発達しないので。10%くらいはストレスがかかることで、90%くらいはストレスがかからない環境をつくることが、これから伸びるために大切なことだと思います。僕は1日19時間研究していてもストレスでないので全然苦じゃないです。だけど、Excelは10分で嫌になります。なのでExcelには触らないようにしています。学校でExcelの書類が来たら絶対に書きません(笑)。あと僕はeメールが嫌いなので打ちません。電話の方が好きなので。ストレスのかかるeメールより、電話の方が早くてストレスがかかりません。



会場ではふしぎなおもちゃが回され、子どもたちは大盛り上がり 底に置いた1円玉が浮いて見えるおもちゃ

 人によって全然違う生き方があって、社会や会社でいきなり変われるかっていうと、急に変わるとぐちゃぐちゃになるのできっと難しいと思います。だけど、人によって違うツールを使ったり違う生き方をしたりしても、それを吸収できるように、我々がコンピューターを今発達させている途中です。そんなふうに10年後にはできるようになっていると僕は思っています。

落合陽一(おちあいよういち)

1987年生まれの29歳。メディアアーティスト。筑波大でメディア芸術を学んだ後、東京大学学際情報学府博士課程を学際情報学府初の短縮修了(飛び級)して博士号を取得。2015年5月より筑波大学助教・デジタルネイチャー研究室を主宰し、Pixie Dust Technologies.incを起業しCEOとして勤務している。また、同年経産省より「未踏スーパークリエータ」、総務省の変な人プロジェクト「異能vation」に選ばれた。2017年より筑波大学学長補佐、大阪芸術大学客員教授、デジタルハリウッド大学客員教授を兼務。専門はCG,HCI,VR,視・聴・触覚提示法,デジタルファブリケーション,自動運転や身体制御。著書に『魔法の世紀(Planets)』『これからの世界をつくる仲間たちへ(小学館)』『超AI時代の生存戦略(大和書房)』など。


カテゴリートップへ

この連載の記事

Go言語のメモリ管理


Goならわかるシステムプログラミング
第19回

Go言語によるプログラマー視点のシステムプログラミング

ソフトウェアにとってメモリは不可欠です。 実行する命令も、メモリにロードしなければ実行できません。 ソースコードに書かれた定数値も、いったんメモリにロードしないと使えません。 関数を呼び出すにも、スタックと呼ばれるメモリ領域が必要です。 スタック以外に、ヒープと呼ばれるメモリ領域が必要なこともあります。

今回は、Go言語のプログラマーが作成するプログラムの下で、どのようにメモリが管理され利用されるかを探ります。 Go言語のメモリ管理というとガベージコレクターの話を思い起こすかもしれませんが、ガベージコレクターについては本連載では取り上げません。

メモリ確保の旅

コンピューターに接続されている物理的なメモリチップが、どのような過程を経てプログラムで使われるのか、順番に見ていきましょう。

(1): カーネル

最近のオペレーティングシステムでは複数のプロセスを同時に実行できます。 それらのプロセスのそれぞれに、そのプロセスだけが使うメモリ空間があります。 各プロセスのメモリ空間は、物理的なメモリとどう対応しているのでしょうか。

いま、メモリが8GBのマシンがあり、そのうちの1GBバイトをOSが消費しているとします。 残り7GBを使って、1GBずつのメモリを消費する7個のプロセスを順番に起動し、奇数番目に起動したプロセスを終了しました。残りのメモリは、1GBずつ細切れになった4GBです。 この状態で、4GBのメモリを消費するプロセスを追加で起動するとどうなるでしょうか? (以降、話を単純にするため、インテル製の64ビットCPUの場合について説明します。)

現代のOSでは、この追加のプロセスを問題なく起動できます。 その秘密は、CPUに内蔵されているメモリ管理ユニット(MMU)仮想メモリの仕組みです。 プロセスはメモリを読み書きするのに物理的なアドレスを直接使っているわけではなく、プロセスごとに仮想的なメモリアドレスがあり、それを使ってメモリにアクセスしているのです。

仮想メモリアドレスから実際の物理アドレス上のデータへのアクセスには、ページテーブルと呼ばれる階層型のデータ構造が使われます。 「ページ」というのは、メモリを管理する単位です。メモリ全体は4KBずつの「ページ」に分けられています。 それぞれのページが物理メモリのどのアドレスに確保されているかを示すのがページテーブルです。 OSは、プロセスがメモリを確保すると、このページテーブルを用意します。 仮想メモリのおかげで、物理的な保存領域が1GBずつの細切れになっていても、プロセスから見ると4GBのフラットなメモリ領域が確保されているように見えるのです。

ページテーブルによる変換が挟まるので遅くなるように思えますが、実際には変換テーブルをキャッシュして高速化する仕組みがCPUには備わっています。 このキャッシュをTLB(Translation Lookaside Buffer)と呼びます。

メモリ管理のこの部分は、Go言語から直接触れることはできません。とはいえ、パフォーマンスに影響を与えうるため、ページサイズの情報を返すAPIが用意されています。

package main
 
import (
    "fmt"
    "os"
)
 
func main() {
    fmt.Printf("Page Size: %d\n", os.Getpagesize())
}

(2): プロセスのメモリ空間

プロセスは、起動するとOSからメモリをもらいます。 OSは、プロセスごとに仮想メモリの領域を確保します。

確保される領域の大きさは、アドレスを示す番地の桁数によるので、基本的にはCPUのビット数に基づきます。 とはいえ、CPUのビット数で表現しうる範囲のメモリ領域へ自由にアクセスできるわけではなく、扱えるメモリの量とCPUのビット数は必ずしも一致しません。 たとえば、現在主流のインテルアーキテクチャの64ビットモードだと、リニアに扱える仮想メモリ長は47ビット(128テラバイト)が2つで、合計256テラバイトです。 ただし、実際にはWindowsもLinuxも、1プロセスあたりの最大のプロセスのメモリサイズは128テラバイトに制限されています。 そのため、プロセスから見える仮想メモリ空間の範囲は、128テラバイト分ということになります。

いま、プログラムAとプログラムBの2つのプロセスを起動すると、それぞれのプロセスに0番地~0x00007fffffffffff番地まで広がる空間が見えます。 同じ0番地であっても、プログラムAとプログラムBが見ている実際の物理的なメモリの場所は違います。 他のプログラムのメモリ領域は、覗き見たり書き込んだりできません。

47ビットの空間すべてをプロセスが自由に使えるわけではありません。「メモリをこれだけください」とカーネルにお願いして、47ビットの空間の一部と物理メモリの対応付けをしてもらいます。 現在は多くてもせいぜいギガバイト単位でメモリを使うプログラムがほとんどでしょう。 カーネルはこの要求されたサイズのみを、仮想メモリの仕組みを使って確保し、物理メモリと対応付けます。

ユーザーのメモリ空間は大きく3つの連続したメモリ領域に分かれます。 その間の空きスペース分はメモリの確保が行われません。 ユーザーのメモリ空間は図のようになっています。

若い番地のほうから、プログラム、プログラムの静的変数などが置かれます。 その先の番地に、カーネルから動的にもらったメモリ領域が置かれていきます。 中段には共有ライブラリがマッピングされて置かれます。

最上段にはカーネルがマッピングされ、通常はその下からアドレスが若くなる方向にスタックメモリが確保される、という説明をよく見かけますが、Go言語ではスタックメモリの管理は独自に行っているため、必ずしもこれとは一致しません。

空きスペースの領域はこれから説明するようなシステムコールで動的に割り当てたメモリとして使われます。

(3): システムコール

プロセスができると、隙間がなくフラットなプロセス固有のメモリ空間ができること、そして、仮想メモリ空間の中にプログラムが実際に利用するメモリのブロックができることを説明しました。 次は、このメモリブロックをOSに依頼してもらってくるためのシステムコールについて説明します。

POSIX系OSでメモリブロックの確保に使うシステムコールは、すでに何度か登場しているmmapです。 Go言語では、この連載の第12回「ファイルシステムと、その上のGo言語の関数たち(3)」でファイルをメモリ空間に配置するときに紹介した、syscall.Mmapを使います。 このシステムコールは、対象のファイルを指定せずにアノニマス(無名)フラグを付けて実行すると、ファイルを読み込まずに指定サイズのメモリブロックを確保します。 これによって実行時に必要な動的メモリを確保できます。 mmap以外にも、POSIX系OSには、ヒープサイズを拡張するbrkおよびsbrkという、割り当て済みのメモリのサイズを変更するシステムコールがありますが、これらはGo言語のランタイムでは使われていません。

Go言語のWindows実装では、mmap相当のCreateFileMappingではなく、アノニマスフラグを付けたときのmmapの挙動とほぼ同じVirtualAllocを使います。

なお、mmapシステムコール自体はマッピングしたメモリブロックを仮想メモリのどこのアドレスに配置したいかのヒント情報を指定できますが、これはメモリマップを管理している言語ランタイム向けの機能ですし、通常のアプリケーションで使う時は0を設定して自動設定にします。 Go言語のsyscall.Mmapでは最初から指定できません。

(4): ランタイム:ヒープメモリ

OSカーネルが物理メモリと仮想メモリのマッピングを管理しており、プロセスのメモリ空間へOSカーネルからシステムコールによってメモリを持ってくるところまで説明しました。 ここまでだと、まだメモリのかたまりが手に入っただけです。 ユーザーコードから扱いやすい形式で適切にメモリを確保したり解放したりするのはランタイムの仕事です。

それに、OS内部でのメモリ確保はコストのかかる可能性のある処理です。 物理メモリが足りなければ、優先度の低いメモリ領域を選んだり、HDDなどのストレージにスワップアウトしたり、それでも必要なメモリを確保できなければ他のプロセスを強制終了(LinuxのOOMキラー)させたりするといったことが裏で行われる可能性があります。

そこで、mmapなどのシステムコールを使って比較的大きめのメモリブロックをOSからもらっておき、細かいメモリのやりくりは効率よくユーザーランドの中で行って、不要になったらOSにまるごと返却したり、あるいは足りなければOSからメモリをもらったりします。 そのために使うメモリ領域としては、ヒープとスタックの2種類がありますが、まずはヒープから紹介します。

C言語でメモリ確保に使う有名な標準関数はmalloc()です。 これは必要な容量を指定するとそのサイズのメモリブロックが確保され、そのポインタを返す関数です。 mallocの仲間にはjemalloc、TCMallocなどがあり、Go言語ではTCMallocを採用しています。 TCMallocはGo言語と同じGoogle製です。

TCMallocは、マルチスレッド時代に合わせて設計されているmalloc実装で、主な特徴は2つあります。

  • 32キロバイト以下の小さなオブジェクトについては、スレッドごとにメモリブロックを管理する。これにより、ロックなどのスレッド競合によるパフォーマンス劣化を防ぐ
  • 32キロバイトよりも大きなオブジェクトについては、4キロバイト単位に丸め、共有の中央ページヒープで管理してメモリの無駄を減らす

32キロバイトより大きなオブジェクトは、中央ページヒープを直接利用してメモリが確保され、返されます。 中央ページヒープは、1ページあたり4キロバイト単位で、255ページまでのサイズごとの空きメモリブロックのリストになっています。 256ページを超えるオブジェクトはサイズごとに分類されずに1つのリストになっています。 たとえば、1001キロバイトのメモリが必要であれば、1004に丸めて251ページ長のリストを探しに行き、返却されている空きメモリがあればそれを返し、空きがなければ最低1メガバイト単位でOSからメモリをもらって、必要なサイズに切り出して返します。

小さなオブジェクトについても、同様に小さな単位の「クラス」という分類で空きメモリのリストを持っています。 これらはロックが必要がないため高速に処理できます。 大きなオブジェクトと同様に、リクエストされたサイズに近いクラスの空きリストがあればそこから返します。 なければ、スパンと呼ばれる空きメモリのストックから切り出して返します。 それもなければ、中央ページヒープからメモリをもらいます。

Go言語のヒープメモリの管理そのものの説明はネット上にもほとんどありませんが、下記の記事が参考になるでしょう。

(5): ランタイム:スタック

関数を呼ぶと、リターンアドレスや新しい関数のための作業メモリ領域(コンパイル時にサイズが分かるので固定量)を含む「スタックフレーム」と呼ばれるメモリブロックが確保されます。 このスタックフレームは、スレッドごとにあらかじめ確保されているメモリブロックに対して順番に追加したり削除したりされるだけなので、割当のコストはほぼゼロです。

デフォルトのスタックメモリのサイズは、Linuxではulimit -sで設定します。OSによって初期値が違うようですが、だいたい8MBぐらいが多いようです。 Windowsの場合はコンパイル時のフラグで設定され、32ビット、64ビット問わずデフォルトで1MBです。 ネイティブのOSスレッドは、作成時にこれだけの固定のメモリを確保する必要があるため、そのぶんだけ作成コストが上乗せされます。

これに対し、Go言語のgoroutineでは、最初は4キロバイトの小さなサイズのスタックを確保します。 OSスレッドと比べると極めて小さいサイズであり、goroutineの高速な起動にも貢献しています。 もし関数呼び出しで大きなサイズが必要なことがわかれば、別にスタックフレームを準備し、そちらに引数をコピーして、あたかもスタックがはじめて使われていたかのような状態で関数呼び出しを行います。 これにより、スタックサイズがギガバイトサイズになっても問題なく再帰ループが回せます。

なお、ランタイムのコードで次のコメントをよく見かけますが、これは、この関数の呼び出しではスタックの操作はしないという指示になります。

//go:nosplit

(6): ユーザーコード

ようやくユーザーコードにやってきました。 Go言語では、構造体やプリミティブの初期化の方法がいくつか提供されています。

// プリミティブのインスタンスを定義
var a int = 10
 
// 構造体のインスタンスをnewして作成
// 変数にはポインタを保存
var b *Struct = new(Struct)
 
// 構造体を{}でメンバーの初期値を与えて初期化
// 変数にはインスタンスを保存
var c Struct = Struct{"param"}
 
// 構造体を{}でメンバーの初期値を与えて初期化
// 変数にはポインタを保存
var d *Struct = &Struct{"param"}

配列やスライスの定義もいくつかあります。 varで定義するときは型を明示しますが、右辺で型が明確にわかるときは、:=と書くことで変数宣言と代入を同時に行えます。

// 固定長配列を定義
a := [4]int{1, 2, 3, 4}
 
// サイズ等を持ったスライスを定義
b := make([]int, 4, 8)
 
// バッファー無しのチャネル
c := make(chan string)
 
// バッファー有りのチャネル
d := make(chan string, 10)

これらのコードでは、「メモリを確保する」ことをコンパイラやランタイムに指示していることになります。

C/C++の場合は、ポインタを使わずにローカル変数として宣言するとスタックにメモリが確保され、newmalloc()を使うとヒープメモリにメモリが確保される、というシンプルな仕組みになっています。 Go言語の場合、どちらに置くかはコンパイラが自動的に判断します。newで作っても、その関数内でしか利用されなければスタックに確保されます。ローカル変数として宣言しても、そのポインタを他の関数に渡したり、関数の返り値として返すとヒープに置かれます。 そのため、C/C++で発生する「ローカル変数のポインタを関数の返り値として返すと、呼んだ側からアクセスしに行ったときにはもうスタックのフレームが巻き戻されて無効なメモリになっており、実行時エラーで落ちる」という問題は置きません。

メモリがスタックとヒープのどちらに確保されているかは、ビルド時に-gcflags -mを渡すと表示されます。

$ go build -gcflags -m sample.go ⏎
./sample.go:7: can inline sub
./sample.go:23: inlining call to sub
./sample.go:10: &b escapes to heap
./sample.go:9: moved to heap: b
./sample.go:17: b escapes to heap
./sample.go:16: make([]int, 10) escapes to heap
./sample.go:14: test_make make([]int, 10) does not escape
./sample.go:17: test_make ... argument does not escape
./sample.go:24: a escapes to heap
./sample.go:24: *b escapes to heap

スタックのほうが高速なので、デフォルトではスタックを選択しようとします。 しかし、外部の関数に渡したり返り値で使おうとしたりすると、宣言した関数のスコープよりも変数の寿命が長くなる可能性があるため、ヒープに逃がす(escape)ことがわかります。 ただし、fmt.Printlnのような表示しか行わずデータを変更しない関数でもヒープにされてしまいます。 データのサイズと使われ方次第ですが、参照専用で変更しない場合は、ポインタではなくて実体のコピーを受け取るような関数にしたほうがパフォーマンスが上がることもあります。 他の関数に渡しただけでスタックからヒープになってしまうのは、まだ最適化不足とも言えるので、今後のGoのコンパイラでは改善して欲しいところです。

Go言語のFAQにあるHow do I know whether a variable is allocated on the heap or the stack? (ヒープとスタックのどちらに変数が割り当てられているのか知る方法があるのか?)という項目には、Go言語ではコンパイラが安全側に倒しながら判断するため、どちらに置くかはプログラマーが意識する必要がない、と書かれています。 C/C++では、ヒープの場合はdeletefree()で明示的に削除したり、あるいはスマートポインタで削除されるように仕向ける必要があります。 「どちらか適切なほうに自動で割り当てられる」というGo言語の方針を実現するには、ヒープであってもスタックと同じように不要になったら自動で解放するガベージコレクタが不可欠になります。

Go言語のスライスは他の言語にはあまり見られない特殊なデータです。 実際には裏に配列があり、そこを参照するウインドウ(対象の配列、スタート位置、終了位置の3つの情報を持つ)のようなデータです。 この裏の配列が必要に応じて新しく割り当てられたりしますが、このあたりの話は日本語のウェブサイトの記事も多くあるため、割愛します。

OSの小話 – 仮想メモリが実現する高度な機能

仮想メモリには、フラグメント化されたメモリをフラットに見せかけるアドレス変換以外にも数多くの機能があります。 それらを組み合わせることで、物理メモリのサイズを超える量のメモリを扱うことができますし、メモリの無駄を減らして効率よくアプリケーションに配ることもできます。

たとえば、ページテーブルのエントリには、メモリの該当ページに対する読み書きが行われたときにカーネルに通知するためのフラグや、書き込みされたことを示すフラグが設定できます。 これらのフラグを利用することで、高度なメモリ管理が実現できます。 その一例として、デマンドページングと呼ばれる、「メモリを確保したと見せかけて、はじめてアクセスがあったときに実際に取得しにいく」という仕組みがあります。 デマンドページングでは、たとえばメモリを1GB取得したとしても、即座に物理メモリを1GB取得しに行かず、取得していない部分についてはアクセスがあったときにOSからメモリを受け取るようにします。 メモリを確保しても実際に使うまでにはタイムラグがあるので、デマンドページングによって初期化コストが削れ、使用メモリのピークが異なるプロセス同士でうまくメモリを融通しやすくなります。 デマンドページングは、初期化時だけではなく、優先度の低いメモリ領域をディスクに退避して(スワップ)必要なときに書き戻す際にも使われます。

また、第12回「ファイルシステムと、その上のGo言語の関数たち(3)」コピーオンライトというメモリ節約のテクニックが使われていることを紹介しましたが、これもページテーブルのフラグで実現されています。

仮想メモリには、複数のプロセスでシステムの同じ共有ライブラリをロードしている場合にメモリ消費を抑える仕組みもあります。 それぞれのプロセスの仮想メモリには同じライブラリが個別にロードされているように見えますが、ページテーブルを使って同じアドレスを参照することで、1つ分のメモリしか消費しないようにするのです。 このようなメモリの使われ方は、メモリの使用方法を観察するツールで確認できます。

Linuxのカーネルの書籍を読むと、メモリ管理のセクションには、ディスクの読み書きをメモリに保存しておくページキャッシュ、優先順位を決めてメモリの解放、同一サイズのオブジェクトを効率よく管理する仕組み(スラブアロケータ)、大きいバッファを管理するバディ・システムなど、ここで触れたもの以外にもいろいろな手法が登場します。さらに、macOSでは、メモリの圧縮を動的に行う(おそらく圧縮機構付きスワップ)といったトピックもあります。しかし、アプリケーションのメモリ管理からは少し離れるので、これらの機能については本連載では割愛します。

まとめ

今回は、OSのメモリ管理から、Go言語のプログラム側のメモリの取扱まで一通り紹介してきました。 比較的高速なコードを出力するGo言語にあって、パフォーマンス上の問題として取り上げられやすいのがメモリのアロケーションです。 高速をうたうライブラリはみな、ベンチーマークでメモリのアロケート回数とバイト数も出力し(-benchmemオプションを付与すれば出力される)、いかに回数が少ないかをアピールしています。 メモリ割り当て回数がゼロ(0 allocs/op)はGo言語界においては勲章のひとつのように扱われることすらあります。

「メモリ確保は裏でこれだけ頑張っている高価なオペレーションだ」というのが伝わったなら、今回の記事の役目は果たせたと思います。

次回は最終回になります。


カテゴリートップへ

この連載の記事

『異能vation』公募スタート。今年度はICT分野の”異能なアイデア”そのものも応募可能に!


通称”変な人プロジェクト”が2017年6月30日まで公募受付中。今年は募集部門が増えた!!

2017年05月22日 23時55分更新

文● 編集部

画像は『異能vation』公式サイトより

総務省が実施する『変な人プロジェクト』今年度も公募開始

 2014年(平成26年)度から総務省がスタートし、“国が変な人認定してる”と毎度話題の多い、総務省 独創的な人向け特別枠『異能vation』プログラム。通称『変な人プロジェクト』としても知られる本プログラムが、今年度も5月22日から応募受付を開始したと、業務実施機関である角川アスキー総合研究所が発表した。

 『異能vation』というプログラムは、ICT(情報通信技術)分野において”破壊的価値”を創造する、”奇想天外”で”アンビシャス”な技術課題への挑戦を支援することが目的。選考通過者は300万円を上限とする金銭的支援や、スーパーバイザーからの助言などの人的支援を受けることができる。選考基準においては、その挑戦の成功が重要視されるのではなく、むしろ価値ある失敗に挑戦することを恐れず、技術課題へのアプローチや解決の道筋を示せるチャレンジャーを奨励するというのが本プログラムの文化と言える。プログラムの概要については『異能vation』公式サイト (http://www.inno.go.jp) をお読みいただきたい。

今年からは”こだわりの技術”や”面白いアイデア”だけを応募することも可能に

 本年度のプログラムに関する注目点は、技術課題に挑戦する”人”を支援する『破壊的な挑戦部門』の公募がこれまで通り継続されるとともに、新たに”アイデアそのもの”を公募する『ジェネレーションアワード部門』が設けられているという点。

画像は角川アスキー総合研究所の発表資料より

 新設の『ジェネレーションアワード部門』は、ICT分野における”面白いアイデア”や”実現してほしい技術課題”を募り、プログラムを介してそうした発想と人・企業がつながることにより、新しいイノベーションの芽を創出しようというのが狙いのようだ。こちらの新設部門では、選考を経て『異能ジェネレーションアワード』の表彰が行われるほか、プログラム協力協賛企業から副賞(20万円)および企業特別賞などが提供される予定とのこと。

 『異能ジェネレーションアワード』は、下記に挙げる分野ごとに表彰が行われることになっている。アイデアなら何でもありというわけではさすがになく、あくまでも”ICTに関する分野”に関するアワードであるという点には留意いただきたい。

『異能ジェネレーションアワード』審査分野(ICTに関する)

  1. 情報通信
  2. 宇宙
  3. 医療
  4. 教育
  5. 農業・漁業・林業などの第一次産業と流通
  6. セキュリティ
  7. センシング・データ
  8. 電波とその有効利用
  9. 映像・音声
  10. バイオテクノロジー
  11. 防災
  12. 流通
  13. ロボット・AI(人工知能)
  14. IoT (Internet of Things)
  15. アプリ
  16. その他業務実施機関が思い付きもしない分野

“他薦”という応募方法で誰にでも参加できるのが『異能vation』

 実施される両部門ともに、応募方法は”自薦”のほか、”他薦”という方法ができることも『異能vation』プログラムの特徴だ。被推薦者(技術課題へ挑戦する本人やアイデアを持っている当人のこと)に対して「推薦しておいたよ」と伝えない、というルールを守れば、他薦システムによって誰にでも応募することができる。

 アイデア自体の応募をすることも可能となり、さらに多くの人たちにチャンスがひらけた今年度の『異能vation』プログラム。公募の締切は2017年6月30日(金)となっているので、応募を検討されている方は、お早めに。

■関連サイト



カテゴリートップへ

Go言語と並列処理(2)


Goならわかるシステムプログラミング
第17回

Go言語によるプログラマー視点のシステムプログラミング

今回は、Go言語の並行・並列処理のかなめともいえるgoroutine(ゴルーチン)周りを掘り下げていきます。

goroutineは、前回の記事で軽く触れたように、軽量スレッドと呼ばれるものです。 そこで、まずはこの軽量スレッドと通常のOSのスレッドがどう違うのかを説明します。

そのうえで、goroutineの低レベルな機能を扱うためのruntimeパッケージ、 goroutineのデータ競合を発見するRace Detecter、 さらに高度な非同期処理を書くときに必要になるsyncパッケージおよびsync/atomicパッケージの使い方を紹介します。

スレッドとgoroutineの違い

スレッドとは、プログラムを実行するための「もの」であり、OSによって手配されます。

プログラムから見たスレッドは、「メモリにロードされたプログラムの現在の実行状態を持つ仮想CPU」です。この仮想CPUのそれぞれに、スタックメモリが割り当てられています。

一方、OSやCPUから見たスレッドは、「時間が凍結されたプログラムの実行状態」です。この実行状態には、CPUが演算に使ったり計算結果や状態を保持したりするレジスタと呼ばれるメモリと、スタックメモリが含まれます。

OSの仕事は、凍結状態のプログラムの実行状態を復元して、各スレッドを順番に短時間ずつ処理を再開させることです。 その際の順番や、一回に実行する時間タイムスライスは、スレッドごとに設定されている優先度によって決まります。 実行予定のスレッドはランキューと呼ばれるリストに入っており、なるべく公平に処理が回る用にスケジューリングされます。 複数のプログラムは、このようにして、時間分割してCPUコアにマッピングされて実行されるのです。

スレッドがCPUコアに対してマッピングされるのに対し、goroutineはOSのスレッド(Go製のアプリケーションから見ると1つの仮想CPU)にマッピングされます。 この点が、通常のスレッドとGo言語の軽量スレッドであるgoroutineとの最大の違いです。 両者にはほかにも次のような細かい違いがあります。

  • OSスレッドはIDを持つが、goroutineは指定しなければ実際のどのスレッドにマッピングされるかは決まっておらず、IDなども持たない
  • OSスレッドの1〜2MBと比べると、初期スタックメモリのサイズが小さく(2KB)、起動処理が軽い
  • 優先度を持たない
  • タイムスライスで強制的に処理が切り替わることがないが、コンパイラが「ここで処理を切り替える」という切り替えポイントを埋め込むことで切り替えを実現している
  • (IDで一意にgoroutineで特定できないため)外部から終了のリクエストを送る仕組みがない

GoのランタイムはミニOS

OSが提供するスレッド以外に、プログラミング言語のランタイムでスレッド相当の機能を持つことには、どんなメリットがあるのでしょうか。 Go言語の場合、「機能が少ない代わりにシンプルで起動が早いスレッド」として提供されているgroutineを利用できることで、次のようなメリットが生まれています。

  • 大量のクライアントを効率よくさばくサーバーを実装する(いわゆるC10K)ときに、クライアントごとに1つのgoroutineを割り当てるような実装であっても、リーズナブルなメモリ使用量で処理できる
  • OSのスレッドでブロッキングを行う操作をすると、他のスレッドが処理を開始するにはOSがコンテキストスイッチをして順番を待つ必要があるが、Goの場合はチャネルなどでブロックしたら、残ったタイムスライスでランキューに入った別のgoroutineのタスクも実行できる
  • プログラムのランタイムが、プログラム全体の中でどのgoroutineがブロック中なのかといった情報をきちんと把握しているため、デッドロックを作ってもランタイムが検知してどこでブロックしているのかを一覧で表示できる

goroutineは、OSのスレッドと比べると機能的にはシンプルですが、OSのカーネルが行っていることとGo言語のランタイムが行っていることは、大雑把に次のように対応しています。

用語の対応表
M: Machine 物理CPUコア
P: Process スケジューラ(ランキュー)
G: goroutine プロセス

つまり、Go言語のランタイムは、goroutineをスレッドとみなせば、OSと同じ構造であると考えることができます。

実際、Goのランタイムの内部には、OSがスレッドのスケジューラを持っているのと同様に、goroutineのスケジューラがあります。 具体的には、OSのスレッド(M)ごとに、タスクであるgoroutine(G)のリストがあり、実行中のスレッド上で順番にタスクをどんどん切り替えて実行していきます。 ランキューなどのスレッドが行う作業を束ねるものは、Process(P)と呼ばれます(システムプログラミングにおけるプロセスとは別物です)。 グローバルなタスクのキューもあり、暇になったプロセスはそこからタスクを取得してきます。 また、仕事に偏りがあると、それを平準化する機構も持っています。 また、現代のカーネルはCPUのM個のコア数に対して同時にN個の複数の処理ができるM:Nモデルになっていますが、Go言語のgoroutineもOSスレッドの数Mに対してM:Nモデルになっています。

なお、Goの内部実装についてはネットを探すといくつか情報があります。 さらに詳しいことは、これらの情報を参照してください。

runtimeパッケージのgoroutine関連の機能

軽量スレッドであるgoroutineを使うには、前回説明したように、goを付けて関数呼び出しを行うだけです。 しかし、場合によっては、ベースとなるOSのスレッドに対して何らかの制限を課すといった、より低レベルの操作をしたいこともあります。 runtimeパッケージには、そのようなときに使える低レベルの関数がいくつかあります。

なお、runtimeパッケージにはgoroutineのプロファイリング用の関数もいくつかありますが、集計機能などを自分で実装する必要があります。 goroutineのプロファイルを行うときは、ドキュメントにあるように、runtime/pprofなどの既成のパッケージを利用すべきです。

runtime.LockOSThread() / runtime.UnlockOSThread()

runtime.LockOSThread()を呼ぶことで、現在実行中のOSスレッドでのみgoroutineが実行されるように束縛できます。 さらに、そのスレッドが他のgoroutineによって使用されなくなります。 これらの束縛は、runtime.UnlockOSThread()を呼んだり、ロックしたgoroutineが終了すると解除されます。

この機能が必要になる状況としては、メインスレッドでの実行が強制されているライブラリ(GUIのフレームワークや、OpenGLとその依存ライブラリなど)をGo言語で利用する場合が挙げられます。 「現在実行中のスレッド」が確実にメインスレッドかどうかは、実行中にはわかりませんが、mainパッケージのinit()関数は確実にメインスレッドで実行されるため、それを利用してメインスレッドを固定することができます。

Goのランタイムでは、シグナルを受け取るスレッドを固定するためにこれらの関数を使っています。

runtime.Gosched()

現在実行中のgoroutineを一時中断して、他のgoroutineに処理を回します。 goroutineには、OSスレッドとは異なり、タスクをスリープ状態にしたり復元したりする機能がありません。ランキューの順番が回ってきたら、何ごともなく処理が再開します。

この関数は、ロックを使わずに目的の変数が変更されるのを待つといった用途で使えるかもしれませんが、実際にはあまり使うこともないでしょう。 Go言語のソースコードを見ても、ガベージコレクタが自発的に処理を手放すことでGCの停止時間を短縮させるのに使っているぐらいです。

runtime.GOMAXPROCS(n) / runtime.NumCPU()

ウェブに残る古いGo言語の解説記事では、このruntime.GOMAXPROCS()をよく見かけると思います。 これは、同時に実行するOSスレッド数(I/Oのブロック中のスレッドは除く)を制御する関数です。 Go 1.4までは、デフォルトのPROC数が1となっており、マルチコアを活用するには必ずこの関数を呼ぶ必要がありました。 runtim.NumCPU()でCPU数が分かるので、これをruntime.GOMAXPROCSに渡すのが定石でした。

Go 1.5からは、デフォルトでruntime.GOMAXPROCS()が設定されるようになったので、特別な場合を除いてわざわざ設定する必要はありません。 しかし、最速を狙おうとすると、このデフォルト値の半分に設定するほうがスループットが上がる場合があります。 現代のCPUのいくつかは、余剰のCPUリソースを使って1コアで2つ以上のスレッドを同時に実行する機構(ハイパースレッディングや、SMT(Simultaneous Multi-Threading))を備えています。 そのような機構を利用している場合、1コアで2つのヘビーな計算を同時に実行すると、CPUコアのリソースを食い合ってパフォーマンスが上がらないことがあります。 筆者が執筆で使っているMacBookPro(Core i7の2014モデル)の場合、runtime.NumCPU()は8を返しますが、これも物理コア数が4で、SMT機能による論理コア数がこの返り値となっています。

次の表は、実際にGo言語でCPUヘビーな計算を並列に行わせて(それぞれのgoroutineでは同じ計算をしている)、完了にかかる時間を計測した結果です。 1から4とくらべて、8並列の場合のパフォーマンスが落ちていることが分かります。

CPU不可の高い処理(円周率10万桁計算)の終了時間比較
1 11.834
2 11.971
4 12.294
8 15.181

Go言語のruntime.NumCPU()はSMTを含めた論理コア数を返しますが、物理コア数を返すAPIはありませんので、プログラム側から自動設定はできません。 GOMAXPROCS環境変数によっても設定できるので、実行時にこの環境変数で設定するほうがよいでしょう。 また、goroutineを特定のコアに張り付けてリソースの食い合いが起きないように制御する機能は提供されていませんが、現代のOSのスケジューラは十分に賢いので、4並列で重い計算を行う場合は自動で別のコアに分散して計算してくれます。

Race Detector

Go言語には、データ競合を発見する機能があります。 この機能は、Race Detectorと呼ばれ、go buildgo runコマンドに-raceオプションを追加するだけで使えます。

Race Detectorを有効にしてGoプログラムを実行すると、次のようなメッセージが表示され、 競合が発生した個所と、競合した書き込みを行ったgoroutine、そのgoroutineの生成場所が分かります。

==================
WARNING: DATA RACE
Read at 0x0000011a7118 by goroutine 7:
  main.main.func1()
      /Users/shibu/.../mutex2.go:25 +0x41
 
Previous write at 0x0000011a7118 by goroutine 6:
  main.main.func1()
      /Users/shibu/.../mutex2.go:25 +0x60
 
Goroutine 7 (running) created at:
  main.main()
      /Users/shibu/.../mutex2.go:26 +0x93
 
Goroutine 6 (finished) created at:
  main.main()
      /Users/shibu/.../mutex2.go:26 +0x93
==================

syncパッケージ

前回の記事で紹介したように、チャネルとselectの2つの文法があればgoroutine間の同期には事足ります。 しかし、すでに他の言語で書かれている実績あるコードをGo言語で再実装する場合など、他言語にはないチャネルとselectで書き直すのは大変です。 そのようなときのために、並列処理をサポートするためのsyncパッケージが提供されています。

syncパッケージは、おそらく既存のAPIを参考にして実装されており、 多くのOSなどで並行・並列処理を同期させるための機能として提供されているものがいくつか含まれています。 参考までに、多くのOSでサポートされているPThreadのAPIとsyncパッケージの機能を比較してみます。

ネイティブスレッドのAPIとの比較
ロック pthread_mutex_* sync.Mutex
RWロック pthread_rwlock_* sync.RWMutex
複数スレッドの終了待ち なし sync.WaitGroup
条件変数 pthread_cond_* sync.Cond
一度だけ実行 pthread_once sync.Once
スレッドローカルストレージ pthread_key_*など なし
スピンロック pthread_spin_* なし
オブジェクトプール なし sync.Pool

上記の表を見ると、Goのsyncパッケージに欠けている機能がいくつかあります。

スレッドローカルストレージは、スレッドごとの領域にデータを保存できる機能ですが、利用するにはスレッドを識別できる必要があります。 goroutineはOSのスレッドとは異なり、IDを持たないため、スレッドローカルストレージが使えません。

スピンロックは、ビジーループでロックを獲得するロックです。 goroutineの軽量スレッドは、ブロックしてしまってもコンテキストスイッチが軽いため、スピンロックの重要性があまりなく、この機能が提供されていません。

なお、ロックとRWロックは機能として提供されていますが、ブロックせずにトライできないか試してみるpthread_mutex_try_lock()や、タイムアウト時間の設定ができるpthread_mutex_timedlock()は存在しません。

sync.Mutex / sync.RWMutex

マルチスレッドプログラミングでは、「メモリ保護のためにロックを使う」といった説明をされることがあります。 これはスレッドが同じメモリ空間で動くためですが、実際に保護するのは実行パスであり、メモリを直接保護するわけではありません。 sync.Mutexは、実行パスに入ることが可能なgoroutineを、排他制御によって制限するのに使います。

sync.Mutexを使うと、「メモリを読み込んで書き換える」コードに入るgoroutineが1つに制限されるため、不整合を防ぐことができます。 この、同時に実行されると問題がおきる実行コードの行(1行とは限らないが、プログラミング言語のブロックより小さいこともある)を、クリティカルセクションと呼びます。 マップや配列に対する操作はアトミックではないため、複数のgoroutineからアクセスする場合には保護が必要です。

次のコードでは、IDをインクリメントするコードが同時に1つしか実行されないようにしています。 マルチスレッドプログラミングでは「コードのどの箇所でコンテキストスイッチが発生しても不整合が置きないようにする」のが鉄則です。 id変数をCPUが読み込んだタイミングでコンテキストスイッチが発生すると、同じid値を参照するスレッドが複数できてしまい、同じidが生成されてしまいます。

package main
 
import (
    "fmt"
    "sync"
)
 
var id int
 
func generateId(mutex *sync.Mutex) int {
    // Lock()/Unlock()をペアで呼び出してロックする
    mutex.Lock()
    id++
    mutex.Unlock()
    return id
}
 
func main() {
    // sync.Mutex構造体の変数宣言
    // 次の宣言をしてもポインタ型になるだけで正常に動作します
    // mutex := new(sync.Mutex)
    var mutex sync.Mutex
 
    for i := 0; i < 100; i++ {
        go func() {
            fmt.Printf("id: %d\n", generateId(&mutex))
        }()
    }
}

sync.Mutexは内部に整数を2つ持つ構造体です。 Go言語では、構造体作成時にはかならずメモリが初期化されるため、上記の例のように特別な初期化を行わずに使えます。 ただし、値コピーしてしまうとロックしている状態のまま別のsync.Mutexインスタンスになってしまうため、他の関数に渡すときは必ずポインタで渡すようにします。コードの静的チェックツールのgo vetを実行すると、このsync.Mutexのコピー問題は発見できます。

Go言語には、そのコードブロックを抜けるときに忘れずに後処理を行うdeferがあるので、これと組み合わせて使うのがほとんどでしょう。 メモリ確保と解放、ファイルのオープンとクローズ、ロックの獲得と解放などの対となる動作はなるべく不可分に記述する、連続して書くというのが、間違いを防ぐためのベストプラクティスとして多くの言語で採用されています。

func generateId(mutex *sync.Mutex) int {
    // 多くの場合は次のように連続して書く
    mutex.Lock()
    defer mutex.Unlock()
    id++
    return id
 } 

Go言語Wikiには、Mutexとチャネルの使い分けについて次のようにまとめられています。

  • チャネルが有用な用途:データの所有権を渡す場合、作業を並列化して分散する場合、非同期で結果を受け取る場合
  • Mutexが有用な用途:キャッシュ、状態管理

sync.Mutexにはsync.RWMutexというバリエーションがあります。 この構造体にはsync.Mutexと同じLock()Unlock()に加えて、RLock()RUnlockというメソッドがあります。 Rが付くほうは、読み込み用のロックの取得と解放で、 「読み込みはいくつものgoroutineが並列して行えるが、書き込み時には他のgoroutineの実行を許さない」という方式でのロックが行えます。 Mutexの用途のうち、読み込みと書き込みがほぼ同時に行われるような状態管理の場合はsync.Mutexが、複数のgoroutineで共有されるキャッシュの保護にはsync.RWMutexが適しています。

なお、上記のコードにはバグがあります。 このバグを直す方法はいくつかありますが、一番簡単なのが、次節で説明するsync.WaitGroupを使う方法です。

sync.WaitGroup

sync.Mutex並に使用頻度が高いのがsync.WaitGroupです。 sync.WaitGroupは、多数のgoroutineで実行しているジョブの終了待ちに使います。

package main
 
import (
    "fmt"
    "sync"
)
 
func main() {
    var wg sync.WaitGroup
 
    // ジョブ数をあらかじめ登録
    wg.Add(2)
 
    go func() {
        // 非同期で仕事をする(1)
        fmt.Println("仕事1")
        // Doneで完了を通知
        wg.Done()
    }()
 
    go func() {
        // 非同期で仕事をする(1)
        fmt.Println("仕事2")
        // Doneで完了を通知
        wg.Done()
    }()
 
    // すべての処理が終わるのを待つ
    wg.Wait()
    fmt.Println("終了")
}

Add()メソッドを呼ぶとジョブ数が追加されます。 Add()メソッドは、必ずgoroutineなどを作成する前に呼びましょう。

Done()メソッドを呼ぶと残りジョブ数がデクリメントされていきます。 Wait()メソッドはすべてのジョブが完了するのを待つメソッドです。

チャネルよりもsync.WaitGroupのほうがよいのは、ジョブ数が大量にあったり、可変個だったりする場合です。 100以上のgoroutineのためにチャネルを大量に作成して終了状態を伝達することもできますが、これだけ大量のジョブであれば、数値のカウントだけでスケールするsync.WaitGroupのほうがリーズナブルです。

前節のサンプルコードは、終了待ちをしていないため、main()が終了したらプログラムが完了してしまいます。 sync.WaitGroup構造体を使えば、簡単に全タスクの終了待ちができます。

sync.WaitGroupも変数宣言だけで使えます。 値コピーしてしまうと正しく動作しない点もsync.Mutexと同じです。

sync.Once

sync.Onceは、一度だけ関数を実行したいときに使います。 初期化処理を一度だけ行いたいときに使う場合が多いでしょう。

package main
 
import (
    "fmt"
    "sync"
)
 
func initialize() {
    fmt.Println("初期化処理")
}
 
var once sync.Once
 
func main() {
    // 三回呼び出しても一度しか呼ばれない。
    once.Do(initialize)
    once.Do(initialize)
    once.Do(initialize)
}

Go言語には、init()という名前の関数がパッケージ内にあると、それが初期化関数として呼ばれる機能があります。 sync.Onceではなくinit()を使うほうが、初期化処理を呼び出すコードを書かなくても実行され、コード行数も減るので、シンプルです。 sync.Onceをあえて使うのは、初期化処理を必要なときまで遅延させたい場合でしょう。

sync.Cond

sync.Condは条件変数と呼ばれる排他制御の仕組みです。 これもロックをかけたり解除したりすることでクリティカルセクションを保護します。 sync.Condの用途には次の2つがあります。

  • 先に終わらせなければいけないタスクがあり、それが完了したら待っているすべてのgoroutineに通知する(Broadcast()メソッド)
  • リソースの準備ができ次第、そのリソースを待っているgoroutineに通知をする(Signal()メソッド)

後者の用途の場合、Goであればチャネルで用が済むので、主に使うことになるのは前者の用途でしょう。 Goの標準ライブラリでは、TLSやHTTP/2のライブラリがサーバーとのハンドシェイクが完了したり、サーバーからレスポンスがきたタイミングでワーカースレッドを起こすのに、sync.Condが使われています。

package main
 
import (
    "fmt"
    "sync"
    "time"
)
 
func main() {
    var mutex sync.Mutex
    sync.NewCond(&mutex)
 
 
    for _, name := range []string{"A", "B", "C"} {
        go func(name string) {
            // ロックしてからWaitメソッドを呼ぶ
            mutex.Lock()
            defer mutex.Unlock()
            // Broadcast()が呼ばれるまで待つ
            cond.Wait()
            // 呼ばれた!
            fmt.Println(name)
        }(name)
    }
 
    fmt.Println("よーい")
    time.Sleep(time.Second)
    fmt.Println("どん!")
    // 待っているgoroutineを一斉に起こす
    cond.Broadcast()
    time.Sleep(time.Second)
}

チャネルの場合、待っているすべてのgoroutineに通知するとしたらクローズするしかないため、一度きりの通知にしか使えません。 sync.Condであれば、何度でも使えます。また、通知を受け取るgoroutineの数がゼロであっても複数であっても同じように扱えます。

sync.Pool

sync.Poolは、オブジェクトのキャッシュを実現する構造体です。 一時的な状態を保持する構造体をプールしておいて、goroutine間でシェアできます。 sync.Poolは当然ながらgoroutineセーフです。この構造体は、fmtパッケージの一時的な内部出力バッファなどを保持する目的で導入されました。

sync.Poolの使い方は簡単で、Put()メソッドでキャッシュしたいデータを追加します。 Get()で、キャッシュからデータを取り出します。 出し入れするデータはinterface{}型なので、どのような要素でも入れられます。プールが空のときは、プール作成時に設定した関数で作ったインスタンスが返ります。

package main
 
import (
    "fmt"
    "sync"
)
 
func main() {
    // Poolを作成。Newで新規作成時のコードを実装
    var count int
    pool := sync.Pool{
        New: func() interface{} {
            count++
            return fmt.Sprintf("created: %d", count)
        },
    }
 
    // 追加した要素から受け取れる
    // プールが空だと新規作成
    pool.Put("manualy added: 1")
    pool.Put("manualy added: 2")
    fmt.Println(pool.Get())
    fmt.Println(pool.Get())
    fmt.Println(pool.Get()) // これは新規作成
}

なお、内部では要素はキャッシュでしかなく、(他の言語で言うところの)WeakRefのコンテナとなっています。 そのため、ガベージコレクタが稼働すると、保持しているデータが削除されます。 sync.Poolは、消えては困る重要なデータのコンテナには適しません。

package main
 
import (
    "fmt"
    "runtime"
    "sync"
)
 
func main() {
    var count int
    pool := sync.Pool{
        New: func() interface{} {
            count++
            return fmt.Sprintf("created: %d", count)
        },
    }
 
    // GCを呼ぶと追加された要素が消える
    pool.Put("removed 1")
    pool.Put("removed 2")
    runtime.GC()
    fmt.Println(pool.Get())
}

sync/atomicパッケージ

sync/atomicパッケージは不可分操作と呼ばれる操作を提供しています。

これはCPUレベルで提供されている「1つで複数の操作を同時に行う命令」などを駆使したり、提供されていなければ正しく処理が行われるまでループするという命令を駆使して「確実に実行される」ことを保証している関数として提供されています。 途中でコンテキストスイッチが入って操作が失敗しないことが保証されます。

次の表のように6つのデータ型に対して、5つの操作が提供されています。

不可分操作
int32 AddInt32 CompareAndSwapInt32 LoadInt32 StoreInt32 SwapInt32
int64 AddInt64 CompareAndSwapInt64 LoadInt64 StoreInt64 SwapInt64
uint32 AddUint32 CompareAndSwapUint32 LoadUint32 StoreUint32 SwapUint32
uint64 AddUint64 CompareAndSwapUint64 LoadUint64 StoreUint64 SwapUint64
uintptr AddUintptr CompareAndSwapUintptr LoadUintptr StoreUintptr SwapUintptr
unsafe.Pointer CompareAndSwapPointer LoadPointer StorePointer SwapPointer

最初のsync.Mutexのサンプルをatomicを使って表現すると次のようになります。

var id int64
 
func generateId(mutex *sync.Mutex) int {
    return atomic.AddInt64(&id, 1)
}

sync.Mutexsync.Cond、チャネルなどを使っても、複数のgoroutineがアクセスしてロックされると、コンテキストスイッチが発生します。 こちらのロックフリーな関数を使えばコンテキストスイッチが発生しないため、うまく用途が合えば今回紹介した機能の中では最速です。

これ以外に、任意のデータ型(interface{})に対するLoad()メソッドとStore()メソッドによる、アトミックな変数読み書きを提供するatomic.Value構造体もあります。

まとめと次回予告

スレッドやgoroutineの仕組みと、Goの並行・並列処理をサポートするツールやライブラリを見てきました。 前回紹介したように、Go言語は組み込みで「安全に非同期な待ち合わせをする機能」を提供しています。 しかし、Go言語の非同期サポートの強力さを実感するのは、goroutineがデッドロックしたときに稼働中のgoroutineがどこでブロックしているかを教えてくれたり、競合状態を検出するオプションがあったり、パニック時にきちんとスタックトレースが出てくれる点です。 こうした強力な機能のおかげで、Go言語ではマルチスレッドを駆使したコードのデバッグが極めて簡単です。

次回は並列処理のパターンを取り上げます。

脚注

  1. https://github.com/golang/go/wiki/LockOSThread
  2. 最近、AMDの最新CPUであるRYZENのベンチーマークでも、この問題が話題になりました。「4gamer:Ryzenはなぜ「ゲーム性能だけあと一歩」なのか? テストとAMD担当者インタビューからその特性と将来性を本気で考える」: http://www.4gamer.net/games/300/G030061/20170425122/
  3. https://golang.org/doc/articles/race_detector.html
  4. C++のスマートポインタとデストラクタ、例外処理を持つ言語のtry/finallyブロック、Rubyのブロック構文やPythonのwith構文など、身の回りでたくさん発見できるでしょう。
  5. https://github.com/golang/go/wiki/MutexOrChannel
  6. http://qiita.com/tenntenn/items/7c70e3451ac783999b4f
  7. https://ja.wikipedia.org/wiki/%E4%B8%8D%E5%8F%AF%E5%88%86%E6%93%8D%E4%BD%9C


カテゴリートップへ

この特集の記事

総務省主催『AIネットワーク社会推進フォーラム』レポート


特別企画@プログラミング+
第21回

国内外から約60名の登壇者が集結。多分野からの視点がはげしく交錯した国際シンポジウムの様子をお届け。

2017年05月03日 00時00分更新

文● 窪木 淳子、編集● 杉本 敏則

 人工知能 (AI: Artificial Intelligence) をテーマにした国際シンポジウムが、3月13日(月)と14日(火)に東京で開催された(会場は東京大学 伊藤謝恩ホール)。この2日間にわたる総務省主催の『AIネットワーク社会推進フォーラム』は、国内外から約60名の登壇者を招聘した大規模で密度の濃いものだった。

 講演者・登壇者には、国内からは総務省が開催している『AIネットワーク社会推進会議』メンバーが各分野から集まったほか、海外からは2017年1月にAppleの加盟も話題になった、世界の主要IT企業が名を連ねる研究団体『Partnership on AI』の評議会暫定議長や、OECD(経済協力開発機構)事務次長、Googleの主席研究員、イーロン・マスク氏やスティーブン・ホーキング博士が顧問を務めることでも知られる、AIや核兵器、バイオテクノロジーがもたらすリスク低減などについて研究を行う『FLI (Future of Life Institute)』の共同創設者などが参加。官民問わず多分野のエキスパートたちが文字通り世界中から集結した。その様子をレポートする。

“AIネットワーク”という言葉が意味するところ

 レポートの紹介を始める前に、そもそも、シンポジウムの名に掲げられている“AIネットワーク”という表現の意味だが、この言葉が意図する社会像をイメージするのはまだ難しいように思われる。本シンポジウム開催に関する報道資料によると、これは、近い将来、AIが発達することにより、AIが他のAIや情報システムなどと連携、ネットワーク化されていく社会を指している。その社会では、利便性が飛躍的に増大すると同時に、現在想定される以上のリスクが広範囲に波及してしまうことが考えられる。

 このAIネットワーク化によって、社会・経済にもたらされる影響やリスクを現段階から事前に検討、その課題を整理して、よりよい施策を国際的に展開していこうとするのが、総務省が開催している『AIネットワーク社会推進会議』の目的だ。

 こうした動きは、先進各国で同時に出現していて、国際協調の可能性や必要性を議論するために、今回のシンポジウムも開催されている。日本としては、総務省情報通信政策研究所が、昨年2月から6月まで『AIネットワーク化検討会議』を開催して、基礎的な評価や検討課題の整理を進めてきた。同年4月には、G7香川・高松情報通信大臣会合において、高市総務大臣が『AI開発原則』の策定に向けて、G7やOECD等において国際的な議論を進めるよう提案、参加各国から賛同を得ている。そして同年10月には『AIネットワーク社会推進会議』が立ち上げられ、『AI開発原則』の内容を具体化した『AI開発ガイドライン』の策定に向け、AIの活用の場面を想定してのより具体的な検討が進められているところだ。

『AIネットワーク社会推進会議』議長である須藤修氏による開会の辞

 プログラムの最初は、『AIネットワーク社会推進会議』議長である須藤修氏(東京大学大学院情報学環教授・東京大学総合教育研究センター長)の開会の辞から始まった。

須藤修氏(推進会議長、東京大学大学院情報学環教授・東京大学総合教育研究センター長)

須藤氏 「AIに関する研究開発の動向は、急速に発展している。AIの研究は古くから行われており、かつての第2次AIブームを基盤に、現在の第3次AIブームではクラウドコンピューティングによって大きな計算力をつかんだ。また、インターネットによってグローバルな広がりを見せている。したがってAIを社会のインフラとして考えなくてはならなくなっている。AIのR&D(研究開発)、イノベーション、安心・安全をベースにした利活用についてのコンセンサスを形成、国際的な発展を考えていくことは極めて重要である」

 シンポジウムの主旨が語られた後、議論が国際的に活発化している状況が説明された。各国の主要な動向をまとめると、以下のようになる。

AI(人工知能)にまつわる議論・各国の主要な動向

国際機関・政府関連

団体

日本

 須藤氏は、「人間とAIネットワークは共生していくことになるが、それは人間社会に資するものでなければならない。またこれは、当然ながら国内に留まるものではなく、国境を越えてグローバルに、国際的な協調によって作られる体制のもとで検討、議論されるものとなっていく。この検討と議論に必要なのは、人間の“包摂”、国境や立場を越えた多くの人々、研究開発者、企業、団体、行政、政府、一般市民などの参加により、ネットワーク化されたAIのシステムを発展させていくことだ。誰もが安心して利活用できる状況を、国際的な枠組みで形成していくための体制をこのシンポジウムで議論したい」と述べた。

 また、議論の起点としては、次の点が特に強調された。

イノベーティブな研究開発および公正な競争を確保する観点から、AIネットワーク化のガバナンスは、一般に、技術的特性およびステークホルダー(開発者、ブロバイダー、最終利用者、第三者)間の責任分担を勘案して、非規制的かつ非拘束的なものとすべき。

 そして、「あくまでも非規制的で、非拘束的なガバナンスのうえで、望ましい発展の方法を利用者も含めてフレームワーク作りしていく。そのために、こうした国際会議を繰り返し、コンセンサスを取っていく努力を続けていく」と、開会は宣言された。

特別講演『AIネットワーク化と智連社会』

濱田純一氏(推進会議顧問、東京大学名誉教授/前・東京大学総長)

 プログラムの2番目は、推進会議顧問の濱田純一氏(東京大学名誉教授)による『AIネットワーク化と智連社会』と題した特別講演。東大ロボットプロジェクトの紹介と、『AIネットワーク化検討会議 報告書2016』より、コミュニティの再活性化によって“智の連携と強調”をはかる“智連社会(WINS)”の概念紹介があった。

特別遠隔講演『AIの人間及び社会への影響』

 続く3番目は、Partnership on AI評議員会暫定議長のエリック・ホロヴィッツ氏(Microsoftフェロー)による講演。Amazon、Google(ディープマインド)、Facebook、IBM、MicrosoftがAI研究と開発のために結成した非営利団体Partnership on AIは、今もっとも動向が注目される組織だ。

エリック・ホロヴィッツ氏(Partnership on AI評議員会暫定議長、米国マイクロソフト・コーポレーション技術フェロー、元・米国人工知能学会長)

 Skypeによる遠隔で実施された講演では、まず、Partnership on AIの3つの目標(ベストプラクティスのサポート、理解の促進、議論と参画のためのオープンプラットフォームの創出)などが解説された。また、サイバーセキュリティーの専門家や政策関係者ら40名が、攻撃チーム(赤チーム)と守備チーム(青チーム)に分かれ、有害なAIを想定しての対処法を議論したワークショップの様子などが報告された。

 Partnership on AIの評議員会は定期的に開催され、報告書が出されていく予定で、議論の柱としては、次のようなことが挙がっている。

  1. AI開発における安全性
    • たとえば医療や交通。安全性のためにステップを踏みながら、最適な事例を構築していかなくてはならない。
  2. 公正で説明責任や透明性があるAI
    • 差別やバイアスがないか、また機械学習におけるデータは最新であるかなどを含め、公平で平等なAIを目指していかなくてはならない。
  3. 人間とAIシステムの連携
    • 人間とAIの協調はあくまでスムーズでなくてはならない。
  4. 労働および経済への影響
    • AIのよって雇用が失われる可能性、AIの振興によって生まれる巨富、格差の是正と富の分配は大きなテーマである。
  5. 社会への広範な影響
    • 人々に対する影響も、犯罪性のあるものも含めて、大きなものとなっていくことを考慮しなければならない。
  6. 社会的な善のために
    • AIのプロジェクトやプログラムは創造や熱意に作用するようにものでなければならない。
  7. 特別な取り組み
    • 上記以外の特別なプロジェクト。特にデータの扱いや研究開発側面における課題解決など。

 国際的な協調については、「日本、中国、EUの企業のパートナー加入も、おそらく近々あるだろう。日本のパートナーにはロボティクス分野やIoTにおいてリーダーシップを発揮してもらいたい」と述べた。

基調講演『人工知能の未来に備えて』

エドワード・フェルテン氏(前・ホワイトハウス科学技術政策局CTO補佐官、プリンストン大学教授)

 次の登壇者も海外からで、前・ホワイトハウス科学技術政策局CTO補佐官であるエドワード・フェルテン氏(プリンストン大学教授)。オバマ政権時にホワイトハウスが公開した報告書『人工知能の未来に備えて』『AI、自動化および経済』についての報告がなされた。

 以下、1日目の政策関係の講演から、トピックスをまとめておこう。

講演『AIが提起する公共政策的考察』

 経済協力開発機構(OECD)の事務次長のダグラス・フランツ氏は、ビデオで講演。来日中の科学技術イノベーション局デジタル経済政策課長のアン・カブラン氏が、昨年11月にOECDパリ本部で行われた『技術予測フォーラム2016』を中心に報告をした。



ダグラス・フランツ氏(経済協力開発機構 事務次長) アン・カブラン氏(経済協力開発機構 科学技術イノベーション局デジタル経済政策課長)

 ダグラス・フランツ氏によると、「これまでは、例えば核兵器のリスクに関しては核拡散防止条約が締結され、原子力の平和利用が実施されてきた。AIのメリットを享受し悪用を制限するためには、国際条約が必要になっていくだろう。そのための枠組み作りを進めている」とのこと。国際機関であるOECDとしては、AIについても、核拡散防止条約、あるいは地球温暖化対策としての気候変動枠組条約のような国際条約を想定して動いている様子がうかがえた。

講演『AI・スマートロボットの開発及び利用に関する原則とルール』

ロバート・ブレイ氏(欧州議会法務委員会 事務局課長)

 EUからは、欧州議会法務委員会事務局課長のロバート・ブレイ氏。欧州議会では『ロボティクスに関する民事法的規制について委員会への勧告』(2017年2月)が行われている。これはロボティクス、AIに関するガイドライン策定に向けてのリサーチを提案したもの。欧州議会には、法務委員会において『ロボティクスと人工知能に関するワーキング・グループ』が設置されている。欧州議会としては、「国家単位ではなくEU全体、国際的な標準化の必要性を感じている」という。

イタリア経済財政大臣特別メッセージ『AIの機会に関する共通理解の形成に向けたG7の取組』

 今年のG7議長国であるイタリアからは、経済財政大臣顧問のベネデッタ・アレーゼ・ルチーニ氏が来日。経済財政大臣からの特別メッセージを伝えた。主要国首脳会議(イタリア・タオルミーナ、5月下旬)に併せて、2017年のG7情報通信大臣会合もイタリア・トリノで開催されることになっている。

講演『AIネットワーク時代を迎える国際社会の取組』

ウォンキ・ミン氏(経済協力開発機構 デジタル経済政策委員会議長、大韓民国未来創造科学部企画調整室長)

 OECDのデジタル経済政策委員会議長で、大韓民国未来創造科学部企画調整室長のウォンキ・ミン氏は、OECDの動きに加えて、韓国のAI開発状況と検討状況を報告した。

(次ページ、1日目のAI開発・情報工学研究者による講演/パネルディスカッション、2日目のレポートにつづく)



この特集の記事

『マストドンを企業が自前で立てた理由』ドワンゴ・ニッポン放送・ピクシブの各キーマンが語る!


特別企画@プログラミング+
第20回

5/17(水)東京・飯田橋にて【マストドン会議2】開催。急変拡大するマストドンの現在を尋ねます。

国内でサービスが話題になって1ヵ月絶たぬうちに開催された、第一回マストドン会議。それぞれの思いが極めて高い熱量で交錯した一夜だった。

マストドンの夜明けはたった1ヵ月で終わり、すでに新しいフェーズへと進んでいる

 新サービス、Mastodon(マストドン)に関して、個性的なインスタンス(サーバー)が多数登場するとともに、サービスに対する考察や、インスタンスを立ち上げた人々へのインタビュー記事なども随分目にする機会が増えてきました。当編集部もマストドンに関して異常に濃密な時間を過ごしてきましたが、とは言え盛り上がり始めてからまだ1ヵ月程度、という事実に驚きを隠せないほど、多くの出来事がすでに生じています。

 去る4月28日には【マストドン会議】と題したイベントを緊急開催(会場へお越しの皆さま、中継をご覧くださった皆さま、ありがとうございました!)。世界最大級の登録ユーザー数を誇るインスタンス “mstdn.jp” を個人で立ち上げ、株式会社ドワンゴへの電撃入社を果たしたぬるかる氏をはじめとして、登壇者・参加者を問わず多種多様な人々が集結したイベントとなりました。しかしながら、マストドンを取り巻く環境は、さらに多くの人々や思惑を巻き込みながら、依然として日々刻々と変化・拡大を続けています。では、マストドンにインスタンスを立てる理由・意義は実際のところどこにあるのか。実装や運営における具体的課題とはどういった内容なのか。

 角川アスキー総合研究所では、来たる5月17日(水)に、こうした「マストドンを活用する意義と方法」について、そのユーザー規模や運営体制から言って、最も語るに相応しいと思われる企業インスタンス担当者や責任者をお呼びして、イベント第二弾となる【マストドン会議2】を開催いたします。登壇者には、”friends.nico“を運営する株式会社ドワンゴ、”TUNER“を運営する株式会社ニッポン放送、そして”Pawoo“を運営するピクシブ株式会社から、それぞれのインスタンス構築・運営におけるキーマンにお越しいただきます。詳細は下記をご覧ください。

イベント開催概要

  • タイトル:マストドン会議2 ― Mastodonにインスタンスを立てる理由~そして実装の課題
  • 日時:2017年5月17日(水)19:00~22:00(18:30受付開始、懇親会含む)
  • 会場角川第3本社ビル 3F(東京都千代田区富士見1-8-19)
  • 登壇者(50音順、敬称略)
  • 司会:遠藤 諭(角川アスキー総合研究所)
  • 主催:株式会社角川アスキー総合研究所
  • 参加費:3500円(税込)
    ※懇親会費用を含みます。
  • 募集人数:120名
  • 参加登録コチラから!(Peatixの予約ページに遷移します)
    ==>http://lab-kadokawa25.peatix.com/

イベント同日にインスタンス構築に関する無料講座&公式ニコ生中継も実施

 今回、角川アスキー総合研究所では、マストドン会議2のイベント参加者を対象に『マストドンインスタンスの構築講座』の開催も計画中。先着順でのご案内とさせていただきますが、マストドン会議2の前時間に、インスタンスの構築方法や注意点について、技術者から学んでいただける機会をご提供する予定です。詳細は決まり次第、参加希望者へ直接ご連絡を差し上げます。イベントへのご参加を検討される方は、こちらも併せてご検討ください。

 イベント後には同会場にて懇親会も実施いたします。おかげさまで前回開催時には登壇者/参加者の垣根を超えて、交流の時間を設けることができました。会場参加ならではの機会(念願の”マトンカレー”をご提供できるようにイベント準備中!)と言えますので、ぜひご活用ください。また、イベントを共に盛り上げてくださるスポンサー様も大募集中ですので、ご興味ある方は企業・団体・個人を問わず、弊社お問い合わせフォームからご連絡ください。

 そして「会場参加は難しい……」という方には、当日ニコニコ生放送・公式生放送によるイベント中継を実施いたします。番組を通じて是非イベントへご注目ください。トゥートやツイートをしてくださる場合は、ぜひ #マストドン会議 でお願いします。

ニコニコ生放送 番組情報

【番組名】
マストドン会議2 ― Mastodonにインスタンスを立てる理由~そして実装の課題 生中継

【番組URL】
http://live.nicovideo.jp/watch/lv297159246



カテゴリートップへ

この特集の記事