2013年8月18日

神保町 小学館ビル に行ってみました。

やっぱりほら、流行りモノは押さえないと ( w )

アルバム ビューアー で見る場合はこちら

こんな感じに結構良い天気でした。



小学館ビル前その一。
実は奥側の半分だけしか見えない。



小学館ビル前その二。
微妙な人だかり。 かつ皆携帯カメラで撮影しているので、滞在時間はムダに長い(私は Q10 で撮影しているのでもっと長い…)。



建物の中その一。
これはいろいろ補正した後ですが、ガラスへの映り込みは激しいですな。 まだ立入禁止状態なので、ガラス越しとはいえ中がそれなりに見えます。



建物の中その二。



ブヒョー
神保町の交差点に戻ってきました。
やはりぶっ飛んで天気が良かった、という話。

2013年7月29日

100Mbps と 1Gbps と 10Gbps と…

世の中素晴らしいもので、ついこの間まで Lan で100Mbps 回線を使えれば速い方だったのに、今じゃ1Gbpsは当たり前、お金持ちな方は 10Gbps なんて速度の回線を使ってたりします。
Wanだって負けていませんで、1Mbps がやっとこだったのに100Mbpsなら個人でも、企業なら1Gbps位出ないと…てなもんです。

素晴らしい

素晴らしい…んですが。皆さんなんか忘れちゃいませんかね?? えぇ、通信ってのはbpsだけで決まるもんじゃないんですよ。
ほら、「光速の壁」って奴があるじゃありませんか…。どんなに転送ビットレートが速くなっても、ある1ビットが目的地に到達するのには絶対越えられない最短時間ってのがあるはずですよね?えぇ、だからLatencyとか、TCP/IPだとRound Trip Time(RTT)は改善しないはずで、なのに回線上は沢山ビットが飛び回れるって事は…
送るべきデータも、予め沢山用意しないといけないって事です。


どうも最近、あちこちのお客さんがこの問題に直面しています。今まで気にしなくても良かった事が、通信速度が速くなる事で徐々にそうは行かなくなってきている…。下手をしなくても、古い機械/OSじゃ対応できない事態になってきている…。そういうことが判っていない人がどツボにハマり始めている…。

というわけで、ちょっとその辺の話を書いてみようかと。


以下の説明の一部は間違っている/嘘を含んでいます。例えば通信では octet という単位を使うものであって byte じゃねー、とか。ヘッダサイズ0ってなんだよーとか。

ちゃんとした説明は、真面目な本とか、この辺:

に任せる事にします。この章の説明はあくまでも今回の議題を理屈的に判ればそれで十分、という精度で書いていくことにします。

TCP Window

例えば今読んでいるこの blog とか、Youtube の動画をダウンロードしているとか、インターネット上で情報をやり取りする多くの場合、我々は TCP/IP という通信プロトコルを利用しています。例外はNTPとか実時間動画配信とか…そういう「今すぐ でないと 情報に価値が無くなる場合」ぐらいなものでしょう。

TCP/IP は IP … Internet Protocol を使ってバイト列を送るプロトコルの一種でして、最も重要なのはバイト「列」を送る、という所です。つまり
A, B, C, D...
のようにバイト列が存在したら、
「Aが見えてからでなくてはBが見えるようにならない」
「Bが見える前にCは見えない」
ようするに A の後に B が来て、その後に C が来る…という順序保証された状態を保証してくれる、と言うことです。

このバイト列の順序保証は送信・受信を実際に行うマシンのメモリ容量よりも大きなバイト列に対しても保証されます。つまり、全部のデータを送って、正しい順番に並べ直して、それから「こんなんでました~」と表示する訳にはいかない、ということ。


一方、IP はパケット通信です。データは塊(パケット)に分割され、パケット単位で送信されます。パケットがネットワーク回線のどこをどう通るのか、本当に受信側に届くのか、誰も何も保証してくれません。
なるべく頑張る (best effort)
以上の事は誰も言ってくれない。

いや、仮に運良く到達するとしても。パケットは送った順序通りに届くことは保証されない。もちろん LAN…近距離であれば到達するためのコースが1通りしか無いのでほぼ順序通りに到着するでしょうが、長距離になると複数の経路が考えられて、時々の都合で経路が変化し、後から送ったはずのパケットが先に到着する…なんてことも考えられる。


こんな性質の IP を使って TCP の順序保証をどのように実装するか?

順序を保証する部分は「パケットに番号を振れば良い」というのはすぐわかると思います。実際番号は「パケット」ではなく
通信を開始してから頭から何バイト目のデータを送っているのか
を表すシークエンス番号というものを使いますが、そんなのは些細な部分。


失われた…届かないパケットは?

受け取った側はシークエンス番号の何番までは「全部連続して届きましたよ」ということを送信側に教えることができます。その先のパケットは、そもそも送り側が送っていないのか、送ったけど届いていないだけなのか、受け取った側には見分けはつきませんが。だって届いていませんからね
送信側は、どの部分をいつ送ったのか、いつ届いたと教えてもらったのか、という履歴を元に、本来ならいつぐらいに届くはずなのかを推測します。

「あれ?もう届くはずなのに、届いたって言わないなぁ…もう一度送ってみるか」

本来届いたって教えてもらえるはずの時間が過ぎても届いたといってもらえない場合、同じデータを再度送ります(再送)。再送を使って届かないデータを何度も送ることで、到着する確率を上げます。


でも。

もし、送信側が 1024kbyte のデータを持っていて、受信側のメモリが 1kbyte しか無かったら? 送信側が勝手気ままに 1024kbyte 分を送っても 1023kbyte 分はほぼ確実に再送対象になっちゃいますよね? だって受信側は 1kbyte しかメモリがないので、シークエンス番号を見て、最初の 1kbyte の外にあるデータは捨ててしまいます。
TCPの順序保証機能を実装するためには、最初の 1kbyte 以外の部分を受け取るゆとりはないからです。

最初の1kbyteの部分を順番通りのバイト列に戻して、アプリケーションとかにそのバイト列を渡してから、次の1kbyteのことが考えられるようになる。
そこに 1024kbyte 分のパケットを送りつけられても、最初の 1kbyte 分以外は全部棄てるしかありませんよね?

そこで、TCPには TCP Window という考えがあります。ようするに送信側、受信側で
「オラーこんだけ一気に送れるだよ?」
「オラーこんだけしか受け取れねーだよ?」
というサイズを教えあうのです。で、小さい方を TCP Window とする。

送信側は、受信側が「受け取ったー」と言ってきたシークエンス番号(通信を開始して以来、先頭から何バイト目かを表す番号)にこの TCP Window の大きさを足した分までの範囲だけを送ることにする。
受信側が送ってきたシークエンス番号よりも前の部分はもう受信側も受け取ったので送り直す必要はない。
シークエンス番号にTCP Windowの大きさを足した分を超えた先の部分のデータは、受信側は受け取れない可能性があるので、あえて送らない。

こうして送らなくてはいけない全データ、ではなくデータのごく一部に集中することで、少ないリソースで再送機能を実装して、確実に順序保証ができる形でデータをやり取りするわけです。


このTCP Window の「大きさ」のことを TCP Window size と言います。


Round Trip Time

パケットをA地点からB地点まで運ぶのにどれぐらいかかるでしょう…??
え?そんなの簡単??予め2つ時計を用意しておいて、一定時刻にAからパケットを送ってBで受け取った時刻を記録すればいい??

いえ、それは簡単ではありません。A,B両地点の時計を完璧に同期させるのは不可能です。光の速度で数msecかかるということは時計だって同期させると数msecの誤差が出るということ。

ましてやパケットはどのような経路を通って伝わるのか分かりませんし、本当の本当にA<->B地点間の通信ケーブルの長さが何メートルなのかも判りません…。

だから別の手を使います。

A地点からB地点へとパケットを飛ばします。B地点ではそれを受けたら「受け取ったー」というACKを返すようにします。で、パケットを送ってからACKがかえってくるまでにかかる時間を計測するのです。これなら時計は一種類ですし距離は2倍に伸びますから、計測しやすく誤差も出にくい。
この「行って・来い」一周(Round Trip)にかかる時間のことを Round Trip Time(RTT)と言います。

TCPの場合、受信側はパケットを受信し、なおかつそれが今まで受信してきた部分の続きの場合、「ここまで受信したよ」というシーケンス番号を返してくれます。これを ACK と言います。traceroute などはこの データパケットを送ってから ACK パケットが返ってくるまでの時間を計測して RTT を算出します。

類似の方法に ICMP echo と ICMP echo reply パケットを使う方法もあります。これは ping が使う方法ですね。

ここでは TCP/ACK の方で RTT を測る、としましょう。


TCP Window size が同じサイズで通信速度が違うと何が起こる?

この章では TCP Window size が同じサイズだと仮定しましょう。 RFC793(RFC793和訳) の時代に戻って 65,535 byte と仮定します。

先出の通り、TCP Window size は「受信側が受け取ったとしたシーケンス番号以降、TCP Window size までのデータを送信して良い」サイズです。

65,535byte は 1byte = 8bit なので 524,280bit です。本当はこれはペイロードなので TCP header とか IP header とか色々くっつくのですが、面倒なのでそれらのサイズは全部 0 だとして、代わりに諸々計算する時にどんどん「切り上げ」することにしましょう。


100Mbps

100Mbps の世界では 524280bit を送信するのに 5.2428msec かかります。つまり、5.2428msec …大雑把に 6msec ですか…以内に ACK が帰ってこないと、送信側はこれ以降のデータを送れなくなります。

6msecの RTT というのは…大雑把に言うと東京・大阪間を直結した場合がそれぐらいです。
Google 先生によると光の速さは 299,792,458m/sec で、東京・大阪間の距離がだいたい 515km だそうですので、片道
515*1000/299,792,458(sec) = 1.7178(msec)
往復で3.5mseecぐらい。電気信号でLANの中を走り回ったり、信号を変換したり、1packet のデータ分をビットに変換したりしてると、だいたい 6msec のRTTは妥当だ、と言うことになります。

東京・名古屋だと 5msec ぐらい。東京・仙台間や、大阪広島間も同じぐらいの距離なので直結線であれば時間も同じぐらいです。

なので、100Mbpsの回線を東京・大阪間でフルパワーで使えると仮定するなら、古い TCP の実装でも Window Size を最大にすれば、全力で通信できる、ということができます。

しかし仙台・大阪間のように、明らかに6msec以上かかる距離になると、通信は間欠泉のように「データを送れないタイミング」ができてしまう。

例えば仙台・大阪間が10msecかかるとします。最初の6msecかけてTCP Windowに格納されていたデータが全部送られるわけですが、最初のパケットに対する ACK はまだ返って来ません。結果、6msec経ったポイントからしばらく沈黙が発生します…最初のパケットに対するACKが返ってくる4msec後までは。

4msecしてACKが返ってくると、受信が確認された分だけ TCP Window に空きができますから、その分だけ新しいデータを相手に送れるようになります。その間にもACKが次々と返って来ますので、TCP Window に空きができ、その分データが送れるようになります。
データが送れるようになればその分また新しいパケットを送るようになるわけで…その状態が6msec続きます。で、またACK待ちになり…

つまり 6msec データを送っては 4msec 沈黙、6msec データを送っては 4msec 沈黙、を繰り返すようになるのです。


ただし、このような状態は WAN …中でも日本を半分近く縦断するような WAN 通信でない限り起こりません。LANと呼べるレベルならほとんど心配は無いはずです。逆に言うと海外との通信に於いてはほぼ確実にこれは起こってしまいます。

1Gbps

1Gbps の世界では 524280bit を送信するのに 0.52428msec かかります。つまり、大雑把に 0.6msec ですか…以内に ACK が帰ってこないと、送信側はこれ以降のデータを送れなくなります。

RTTが0.6msecかかるには、ちょっと大きめの工場の端から端までぐらいの 規模の LAN が必要でしょう。同じビル程度であれば 0.15msec とか 0.2msec 程度になります。

つまり、1Gbpsの世界で WAN を貼ろうとすると、 RFC793(RFC793和訳) の世界だと 100Mbps で海外と通信しようとした時に生じる問題が、となり町との通信でも発生する、と言うことです。

もちろん、東京・大阪間などひどい状態に…なにしろ RTT 自体は変わらないのです。6msec中最初の0.6msec でデータを送り終わり、5.4msec 沈黙を守った後、また0.6msecデータを送り…を繰り返すことになります。
つまり実質 100Mbps でしか通信できない…。原因はもちろん、TCP Windowサイズの小ささと RTT の大きさが通信幅にマッチしていないことにあります。


10Gbps

10Gbps の世界では 524280bit を送信するのに 0.052428msec かかります。つまり、大雑把に 0.06msec ですか…以内に ACK が帰ってこないと、送信側はこれ以降のデータを送れなくなります。

これは正直、スイッチを一台挟んで隣のラック上のマシンと通信してもクリアするのは難しい。Fibre で直結すれば 7kmぐらいまではどうにかなるでしょうが…

このレベルになると、ケーブル上をデータが流れていくのにかかる時間よりも、スイッチやルーターで信号処理をしたり、リダイレクションを掛けたりするのにかかる時間の方が長くなります。


新しい問題は何もありません
全ては新しい問題です

これでお分かりいただけたでしょう。

10Gbpsの世界では隣同士のマシンで起こる事象は、100Mbpsの時代には長距離通信において起こっていた現象でしかありません。別に新しい問題ではないのです。新しい問題ではないのですが…

同時にほとんどの人が意識したこともなく、意識することもなかった問題が噴出する、ということでもあります。ほとんどの人が気にして来なかった、太古から存在する問題が襲ってくる。


解決策は簡単です。TCP Window size を大きくしてやればいいのです。RTTが大きいといっても人間が感知できるほどの大きさではありません。通信格闘ゲームでもやってれば別ですが。なので TCP Window size の default 値を大きくすれば…いい…ん…で…す…が……


実はこれが容易なこっちゃありません。

あぁ、もちろん、64bit 環境においてはアホのように簡単な問題です。問題はこれが「2000年1桁台」初期の頃のマシンや、その頃以前のソフトウェア…特にOSと混ざった場合…32bit CPU とかその程度の環境で発症します。


話を応用性が効くようにするために 10Gbps で東京・大阪間を通信する場合を考えましょう。

RTTは相変わらず 6msec です。常時通信し続けるのに必要な TCP Window size は 60Mbit …大雑把に 8Mbyte 弱です。最近の TCP には Scaling Factor という機能があって、16bit の変数を 2n倍しろ、とすることができます。この n を 0 から 14 まで指定することができるので Window size は (1024-16)Mbyte = 1008Mbyte まで指定することができます。


しかし。プログラムからすればこれから開始する TCP session が「どれぐらい遠く」としゃべるためのものなのか、知るすべはありません。というかプログラムを動かしている「持ち主」だって知らない。インターネットの通信は、サーバがどこにあるのかが「シームレス」だというのも特徴の一つですから。

と言うことは、仮に最長が東京・大阪間で、それ以外はLANの中に収まるとしても、TCP Window size は「全ての session に対して」8Mbyte 用意しなくちゃいけない、と言う事。例えば私が今これを書いている Windows7 マシンは、netstat で ESTABLISHED 状態のセッションを数えただけで18個あります。全部に 8Mbyte を与えると 144Mbyte * 送受信それぞれ = 288Mbyte 、kernel の作業領域から持っていかれることになります。

私が仕事で面倒を見ているマシンになると、常時なんだかんだで 800session ぐらい貼り続けていますから、12800Mbyte = 12.5Gbyte 必要なわけで…

32bit の Linux Kernel の場合、Kernel 自身が動作できるメモリの大きさは 892Mbyte ぐらいです。この内 288Mbyte を通信で消費されただけでも動作への影響は著しくでかい。12.5Gbyte 頂戴なんて言われたら絶対無理。

ましてやこれは東京・大阪間の 6msec の RTT の場合です。世界中を股にかけるとなると 300msec ぐらいは RTT があり得る。このさらに 50倍…600Gbyte…


昔の 1Gbyte ぐらいまでしかメモリが搭載できないマシンに、Windows2003 とか RHEL5 とか載っけて、無理やり動かしているマシンなんて疾うの昔にまともに動けるような状態じゃない、と言うことです。
64bit CPU に 64bit CPU 用OSを用意して、クライアントマシンでも 16Gbyte …サーバなら 1Tbyte ぐらいは搭載しないと、まともな通信なんかできなくなりつつあるってことです。

もちろん、世界中を股にかけた通信なんて、まだ 10Gbps 出ません。100Mbps だって出るかどうか。でも 1/100 にしても 6Gbyte は当たり前の世界なんですよ。

判りますか? HWだけ新しくして、ふっるい distribution …例えば RHEL 5 とか…を使って
「TCO下げてます」
なんて大間違いなんですよ。全然 TCO 下がってないんです。単純に不便になってるだけ。しかも通信していない理由なんか統計情報からは全然判りませんから、問題があるようには見えないかもしれませんが、それは問題を発見するための物差しが間違っているだけなんです。


また、新しく回線を敷設する場合は、通信速度だけではなく、RTT も確認するべきです。もし、本来直結線で 6msec の RTT の回線が 12msec かかるようなサービスがあったら…そりゃ値段を叩いて安くさせるべきです。だってその分メモリを浪費して、サーバの動作効率が落ちるんですから。IO cache とかどれぐらいヒット率が下がってどれぐらい無駄に計算時間が浪費されると思ってるんですか…

RTTは短い場合はルーティング候補がほとんどありませんのでいいのですが、RTTが不必要に長い場合、複数のルーティングの中から適宜選んでいる…という場合があり得ます。この場合、性能を出し続けるためには 一番長いRTT にパラメータを合わせなくちゃいけません。たとえば、60%が 13msec、20% が 20msec、10% が 25msec、5%が 30msec、残り 5% が 40msec 以上…と言われたなら、例えば
「合計95%までの確率で通信速度が全力であることを保持したい」
ならばRTTの短い方95%の中で最も長い RTT … 30msec で 値を決めなくちゃいけません。


え? TCP Window size を RTT に合わせて長くしなおせばいいんじゃないかって?? 今の所、RWINなどは、最初に Window Size を大きく negociation しておいて、実際には少ししか使わない、と言う方法で Window size の調整をしています。
そうではなく TCP Window sizeを最初は小さく、後で option か何かで大きく同意し直す、という方法は、RFCを書く所から始めなくてはいけないレベルの話です。仮に今日、RFCを書いたとしても実装が普及してあなたが使えるようになるのは 10年後ぐらいの話ですよ…。


結論

ようするにこういうことです。


  • TCP Window size こそが真の通信速度の律速条件となる世界がやってきた。
  • 昔WANで気にしなくちゃいけないことは、今やLANでも気にしなくちゃいけない。
  • 古いOSや、32bit HW だともう速い速度にはついていけない。いい加減諦めて買い換えろ。特に Windows を 2003 とか XP とか使ってる奴ら、大概にしろっ!!

2012年6月17日

MECEと「7±2」と「その他禁止!」の話

世の中、分析を行う上で使う価値のある「考え方のツール」というのはいくつもあります。MECEもそのひとつ。網羅性を尽くしたい時によく使われますね。あまりにも広まっているので、珍しくもなんともないのですが…

珍しくない割には、ちゃんと使いこなしている人が余りいません。
いや、言い過ぎですね。今の会社で、私の周りには、あまりいません。

で、何故なのかなぁ、と調べていくと…うむ。これは酷い。お前ら何考えてるんだ、という事が浮かび上がりました。それが「7±2」と「その他禁止!」のルールを知らない、と言う事。

いや、「7±2」は聞いたことがある、と言う人も多いんですよ? 多いんですが…覚えているだけという感じ。意味無いじゃん。

というわけで、今回は私が知っている「MECEを使う上での注意事項」を2つ紹介しようと思います。



まずは MECE についてのおさらいから。

MECEは「Mutually Exclusive and Collectively Exhaustive」の略です。見ての通り実際には2つの条件「Mutually Exclusive」と「Collectively Exhaustive」を両方満たせ、と言っています。


Mutually Exclusive というのは「相互に排他的な項目」…もっと簡単に「ダブリ無し」とか「無駄なし」と言い換えられています。
「Aが成り立つ場合と、Bが成り立つ場合と、Cが成り立つ場合と…」
と列挙していった時に、それぞれが互いに重なり合っていると場合分けが大変になるので、なるべく
「Aが成り立つなら、BやCは成り立たない」
ように条件を決めてやろう、というわけです。


Collectively Exhaustive は「完全な全体集合」ということです。「漏れなし」と言い換えられています。
後から「考慮対象外」が出てきて困ったことになってしまうことがないように、最初から「これで全部」であることを考慮する。特に分類項目なんかを考える時に重要なルールです。


MECEはどういうときに使うかというと…まぁ、「条件分け」と「各条件に当てはまる事象として何が言えるか/既知の情報として何があるか」を分類して、考慮漏れ・配慮漏れを見つけたり、まだ誰も攻略していないブルーオーシャンを見つけたりするのに使います。


例えば客に何かレポートを送らなくちゃいけない場合、「言わなくちゃいけないことは何か?」を考える時に、最初に MECE で条件分けをして、それぞれの箱のなかに何が入るかを並べていき、穴がないことを確認する、なんていう風に使う事が多いです、最近は。


「それで全部考え尽くしたよね?」
と議論とか考察の最後に確認しなくちゃいけない場合は、最初から MECE を考慮しておくと非常に便利です。

MECE …ちゃんと使いこなすと便利だというのは、誰でも直感的に判ると思うんですが…実はこいつには制約事項があります。

その一つ目が「7±2」。

もっと簡単に言いましょう。
書け!!

えぇ、この単純な条件をロクに実施しない奴がほとんどなんですよ。


MECEは「ME」の条件が「分類数を減らす」事に寄与し、「CE」の条件が「網羅性」に寄与します。別の言い方をすると、MECEを使った場合の真の価値は「CE」の方にある。CE の条件を満たす時に ME の条件を満たしていないと無限地獄に陥るから、MEの条件が付与されているのに過ぎません。


一方で。7±2」の条件は「人間が一度に把握できる概念数の上限」を示しています。チャンク、と呼ばれていますが、ようするに「一塊にできる」概念を持ってきた時に、人間は 5~9個しか「塊」を覚えていられないのです。

更に言うなら。

MEの条件だけなら、9個に近い所まで人間は覚えられます。これは、「互いに排他である」事を理解するには、一度に2つの「塊」だけを比較すれば良いため、記憶にあまり大きな負荷がかからないから。

しかし、CEの条件の場合、人間が覚えられる塊は5個に近い処にまで落ちていきます。「全体網羅性」は「全部の塊を一気に把握しないと判らない」ため、CEの確認は記憶に非常に大きな負荷がかかる作業になります。

このため、全てを記憶だけに頼っていると(すでに何度も使っていて慣れている分類パターンならばともかく、新規の分類パターンの場合)、MEの条件は満たしているがCEの条件を満たしていない分類法を使ってしまったり、全条件パターンについて網羅的に調べきれずに失念してしまう物が出てきたりと、ミスが続発します。

えぇ、
MECEは書いてなんぼ
なんでございますよ、奥様。

MECEは記憶力だけで
使うもんじゃない
んでございますよ、お嬢様。


なのに、ノートに書かない、そこらへんの紙にすらろくに書かない、ホワイトボードも何も使わない奴らがゴロゴロゴロゴロ…挙句に、穴だらけの考察を持ってきて
「これでどうですかねぇ」
とか、寝言は寝て言えと。


そもそもお前ら、俺がノートを6冊以上消費しているのに、まだ半分使ってないとか、なにやっとんのかと(ただし、私はノートの紙の片面しか基本的に使わないようにしているので、私が6冊使う間に3冊しか使ってなくてもそれはOK。2冊しか使ってない、とか言うのはありえなくはない状態)。
そんなに「何も書いてない」状態自体、MECEを使いこなせていない証拠だろうが。


えぇ、実際。私はほとんどの考察をノートに書いていまして。というか書かないとダメで。覚えていられませんで。というか私の記憶力は 5±2ぐらいしかないんじゃないかという嫌な予感が、特に英語を叩きこまれた辺りからずーっとしているんですが(多分、無茶な環境変化を食らったせいで記憶制御ユニットが一部壊れたんじゃないかと…)。



閑話休題。


とにかく。MECEを使いこなしたければ「書け」。これがポイント。そして殆どの人が「実行に移していない」ポイントの1つ目でもあり、その度に穴だらけの考察が出来上がる理由でもあります。


具体的に「どう」書くか、は人それぞれです。
私は表を作って埋めていくタイプが好きです。人によってはマインドマップで書くのが好きだという人もいます(これでMEの条件を満たしていると判る理由が私にはわからない)。何がどこにあるんだかよく判らない具合に列挙するだけ、と言う人も居ます(これでもちゃんとMECEの条件を満たす辺りがすごい)。
まぁ、やり方はいろいろです。そこをとやかくいうつもりは全くありません。重要なのは「覚えきれないに決まってるんだから、記憶力に頼るな」と言う事。



さて、MECE を使いこなす上での2つ目の制約条件。それが「その他禁止!」です。


例えば…人間を次のように分類する場合を考えてみましょう。

  1. 女性
  2. オカマ
  3. その他

女性とオカマは排他です。オカマの性別は(一応)男性ですから。オカマは男性全体ではありませんが、男性の部分集合です。また、「その他」は「女性でもオカマでもない」ものを表すので、この3つは Mutually Exclusive なのは間違いありません。


「その他」は「それまで列挙してきた条件を満たさないもの全て」という意味なので、Collectively Exhaustive でもあります。女性でもオカマでもない人間は全て「その他」に分類できます。どこにも属さない人間は発生しません。


つまり「女性」「オカマ」「その他」と言う分類は、確かに MECE を満たしているのです。しかし…この分類、役に立つでしょうか??


実は殆どの場合、役に立ちません。それは分類の精度がバラバラだから…ではなく、「CE」の条件を満たしているのが『その他』という単語に起因しているからです。実はこの分類、「その他」に何が当てはまるのか、理解できている保証がありません。そのような保証がなくてもMECEの条件を満たしてしまう、それが『その他』という単語の恐ろしいところなのです。


実際、この条件から「その他」にはどのような人が割り当てられるか、想像できるでしょうか? ノーマルな男性? あぁ、それは確かに「一部」そうでしょうね。その他には? 「フタナリ」…んー、女性とオカマの定義によっては若干ブレますが、それもアリでしょう。他には?…


そう。これ、いつまででも「他には?」って聞いていられるんです。そういう曖昧模糊としたラベリングでは、「そこに対応するものを思いつけ」と言われてもいつまでも終わらない。逆に「これはどこに分類されるんだ?」となった時に「その他」がどれぐらいの大きさになるのかも予測がつかない。もし、大半が「その他」に入ってしまったら、多分その分類は意味を成さないでしょう。




ところが、この「その他」もこれがまた、失敗した考察にはよく出てくる。そして「その他」が実は重要な要素を持っているはずなのに、完全に失念されていたりする。


なので「その他禁止!」となるわけです。意味もわかってないものを使うな、と。でないと、何のために MECE を使って考えているのか、わからなくなりますからね。

というわけで、MECEを使う上での条件「7±2」と「その他禁止!」というお話でした。

どっと払い(なんでや)

2012年4月9日

I just couldn't stand it....

いやー、アメリカに出張になった時からず――っと我慢してたんだけど。Easterで大半のお店が閉まってる中、ToysRusで見つけちゃったら…我慢できなかった。
Kindle Fire I got .. photo

まぁ、なんか後ろから全力で背中を突き飛ばしてくれた人もいたみたいだけれど…$200だったので買ってしまいましたよ、あぁ…

とりあえず、日本語の Web Page も読めたし、なかなか面白い。
同時に、「お急ぎ配送サービス」とかが1ヶ月無料…とかのサービスも付いてきていて、あぁ、こりゃアメリカ合衆国だけにするわけだわさ、と納得。つーか、本の虫な人とか、Amazonのヘビーユーザーなら、これは安い買い物だろう。

2012年2月10日

Sudo format string vulnerability

前置き

Sudo format string vulnerability ( CVE-2012-0809 )  というバグが、sudo コマンドには存在する。CVEが出ていることからも判るように、結構深刻なバグで、セキュリティ・ホールとしての性質を伴う。sudoのページによれば
運が良くても sudo はクラッシュし、
運が悪いと攻撃者に root 権限を掌握される問題が含まれているかもしれない
との事だ。

で、このバグ、何がどうなっているのかについては、『てきとうなメモ: [Linux][Security] sudo-1.8のバグ』が詳しい。とても詳しいので、そちらだけ見ておけばいいやと思っていたのだが、何箇所かから、
お前も書け
という圧力を食らったので、書くことにする。ただし、新しい情報がなにかあるって言うわけじゃない。

影響範囲

このバグは sudo の 1.8.0 で導入され 1.8.3-p2 で修正されました。なので、影響は 1.8.0 - 1.8.3-p1 と言うことになります。

で、商用 Linux distribution の中で、影響があるのはなさそうです。
Free distribution の中も含めると、Fedora16, OpenSuSE 12.1, Rawhide の3つが影響を受けているようです。
(http://rpm.pbone.net/index.php3 でどのdistributionがどのrpmを使っているのか、分かります)

ただし、それ以外のOSでも sudo は使っているはずです。それらが安全かどうかは判りません。一応、sudo のページには対応している各種OS用の置き換えバイナリは用意されているようです。

という訳で、ほぼ影響はない、と言うことなので、以下は笑い話として。他人の失敗は蜜の味 (^w^)。

攻撃方法

攻撃方法は次のとおりです。
  1.  ln -s /sbin/sudo /tmp/%s
    のようにして、sudu コマンドを「%s」というファイル名にすり替える
  2. /tmp/%s を実行する

%s は printf() 系処理が format 文として利用しているものであれば、大抵のもので問題が生じるようです。どれでどのような症状が出るのかは、CPUなどにある程度依存します。


こうすると、argv[0] 文字列の中に %s という文字列が含まれるようになります。


技術的概略

このバグは、すごく簡単に言うと printf() 系関数を2段階かけた事に起因したバグです。printf() 系関数を多段に使うことは、セキュリティを考慮したコーディングとしては非常にまずいもので、
絶対やるな
と言っても構わないぐらい、危険な行為です。

修正後も2度使っていますが、まぁ、これならしょうがないかな、という所にまで治っています。

実はもう一つ、例外処理が足りていない、と言う問題もあります。sudoだから発生する確率はあまり高くありませんが…仮想メモリシステムを使い切った状態だと危険ですね。

詳細

sudo-1.8.3-p1 の ./src/sudo.c の最後に次のような関数があります。デバッグログを出力するためのコードですが、ここに悪さの元が含まれています:

void
sudo_debug(int level, const char *fmt, ...)
{
    va_list ap;
    char *fmt2;

    if (level > debug_level)
 return;

    /* Backet fmt with program name and a newline to make it a single write */
    easprintf(&fmt2, "%s: %s\n", getprogname(), fmt);
    va_start(ap, fmt);
    vfprintf(stderr, fmt2, ap);
    va_end(ap);
    efree(fmt2);
}

通常、argv[0] は "sudo" という文字列へのポインタです。で、getprogname() は argv[0]を返すようになっています。
そこで、
     fmt = "%s"
   fmtの次の引数 = "hello"
のような場合について考えてみましょう。


    easprintf(&fmt2, "%s: %s\n", getprogname(), fmt);


この行を通った段階で fmt2 は


    fmt2 = "sudo: %s\n";



になります。で、その後:


    vfprintf(stderr, fmt2, ap);


という行を通ると、stderr には


    "sudo: hello\n";


という文字列が送りつけられることになります。
何も問題はありませんよね??

では、攻撃方法にあるように sudo コマンドに対してリンクを貼って './%s' という名前で起動したらどうなるでしょう?

argv[0] = "./%s"

になります。



    easprintf(&fmt2, "%s: %s\n", getprogname(), fmt);


この行を通った段階で fmt2 は


    fmt2 = "./%s: %s\n";



になります。で、その後:


    vfprintf(stderr, fmt2, ap);


という行を通ると、stderr にはまず、第1引数である "hello" が出るので:


    "./hello: 



…おや、困りました。後半の %s のための文字列へのポインタが引数として渡されていません。しかも vfprintf() はそんな事を知りません。しょうがないので、stack 上にある値をポインタと解釈してしまいます。

運がよいと、「Segmentation Violation」が発生します。で sudo はクラッシュします。あぁ、core を吐かないように設定しているといいんですが…。運が悪いと、coreファイルを持っていかれますが、そこには /etc/shadow の値が書いてあるかもしれません。
まぁ、意図的にやっている奴らは
確実に core 取得できるように
設定してから実行するよね。
はっはっはっ

まぁ、運良く大した情報が保存されていない core ファイルが出来上がることを祈るしかありませんな。


ちなみに。"%s" は上記のとおりですが、"%n" という楽しい引数がございましてな…これ、引数で渡したアドレスにそこまで書いた文字数を「書きこむ」んですわ… はっはっは …

判ったと思いますが、万が一これらのバージョンを使っていたら
血の涙を流してでも upgrade しろ!!

対策

1.8.3-p2 はこう変更されました。
void
sudo_debug(int level, const char *fmt, ...)
{
    va_list ap;
    char *buf;

    if (level > debug_level)
 return;

    /* Bracket fmt with program name and a newline to make it a single write */
    va_start(ap, fmt);
    evasprintf(&buf, fmt, ap);
    va_end(ap);
    fprintf(stderr, "%s: %s\n", getprogname(), buf);
    efree(buf);
}

えぇ、見ての通り printf は相変わらず2度使われています。

ただ、1度目は与えられたフォーマットと引数から、とりあえず buf という文字列を作るために、2度目はargv[0] と buf を fprintf() で出力するために使われます。

fmt は sudo のプログラム内で指定するフォーマット文ですし、fprintf() で使われるフォーマット文もプログラム内で指定するフォーマット文ですので、取り敢えず引数に %s などの危険な記号が含まれていても安全、とは言えるでしょう。


ただね。このコード、 buf == NULL だった場合を考慮していないんですわ。一応、glibc の場合 NULL を渡されると "(null)" という文字列を出しますけどね、これは必須ではない。たとえばSolarisは core dump するそうです。

環境によっては fprintf() の例外処理の定義が甘い所につけ込んだコードになる危険性は残されています。

2011年12月29日

sched_clock() overflow after 208.5 days in Linux Kernel

えーっと、久しぶりに Linux Kernel にダメダメなバグが発見されて、よりにもよってうちの製品も影響を受けたので、ここに詳細を書くことにした。
つーか。新しい Kernel を使うなら皆で使おうよ。なんだよその「1つだけ」影響を受けて残りは「影響も受けないぐらい古い」ってのは…


概要
大雑把に 208.5日連続運転した Linux Kernel が突如として reboot する。

実機でなおかつ Time Stamp Counter を内包している必要があるので、Pentium4以降のプロセッサ(が、それはようするに今ある Intel 系CPU全部)か、その互換CPUである必要がある。32bit モード、64bit モードの区別はない。
逆に VMware や Xen など、仮想マシン上で動いている kernel に影響はない。これはそもそもバグを内包したルーチンを、仮想マシンで動いていると検出した Linux Kernel は使わなくなるからだ。

一切の予防的挙動は取られていないので、file systemなども含めて、non-volatile メディアに対する影響は甚大かつ破壊的。回避策はパッチの当たった Kernel をインストールしてrebootするか、200日以内に計画的に reboot する事のみ。


影響範囲
Linux Kernel 2.6.28 で導入されたパッチにバグがあり、3.1.5 で修正された。この間にリリースされた distribution は多分全部影響を受ける。

なお、具体的には
2.6系列:  2.6.32.50でfix
3.0系列:   3.0.13でfix
3.1系列:   3.1.5でfix
らしい。

商用 distribution で影響を受けているもの。

まずは SuSE11 SP1。fix パッチはこうなっている:
http://kernel.opensuse.org/cgit/kernel-source/commit/?id=413af9e6f17a1a1519cae0bfd1c1fe4409bb42a3

RHEL6も 2.6.32 base らいいのでこれも影響を受ける。
(2012/02/14追記: RHEL6 は kernel-2.6.32-220.4.2.el6 で修正が入ったらしい。http://rhn.redhat.com/errata/RHBA-2012-0124.html
ただし、ひどい事に「2012年1月24日に登録され、fix ができたのは2012-02-13という事になっている。3ヶ月近く何をしていたのか…)

多分Fedoraとかも影響をうけるはずだが、マシンを上げていないので判らない(何その手抜き)。
Fedora マシンを上げたら
「3.1.1→3.16 に kernel を更新」
という update が出ていた。 Fedora16使いは速攻で上げることをお勧めする。
また、Fedora15使いは速攻で Fedora16 にして、update を最新に持っていくことをおすすめする。


3.1.5のバグ修正後のコードはこう:
http://lxr.linux.no/linux+v3.1.5/arch/x86/include/asm/timer.h#L58
(LXRとか、こうやってソースコード丸ごと指定できるからありがたい)



なお、VMware とかの仮想環境では、このバグっているルーチンはそもそも利用されない。なのでこれは実機上で動かしている Linux Kernel でのみ、発生する。なので、VMで実験を…とかは一切効かない。


技術的詳細
最近の Intel processor やその互換CPUには Time Stamp Counter (TSC) という 64bit レジスタがある。

こいつは Intel の場合だと CPU 駆動クロックでカウントアップしていて、1tick ごとに+1していく。例えば 2GHz のCPUの場合、1秒で2Gづつ増えていくのだ。

また、AMDの場合だとCPUに投入されるクロックで 1tickごとに+1していく。大抵の場合は 800MHz なので1秒で800Mづつ増える。

判ると思うが、ほぼ nano 秒オーダーの時計として使えるので、リアルタイム制御をしたい場合、TSCはとてもありがたい。しかし、最近の電力消費量制御機構は、CPU駆動クロック周波数を変更してしまう。すると TSC の値は何がなんだか判らなくなる…
そこで、Linux Kernel 内部には cycles_2_ns() という関数があって、TSCの値をナノ秒に修正してくれる事になっている。

しかし、不幸はここに内包されていた


すごく簡単に言うと、TSCの値を必要な Scale Factor 倍すれば nano sec になる。しかし、TSCの値は nano sec に近い。当然 Scale Factor は 1.0 に近い、微妙な浮動小数点になってしまう。

そうだ! Scale Factor を分数で表そう!!
SC/1024 で表現して、
SCを整数にしておけば、
SCを掛け算した後、
右に10bit シフトすれば済むよっ!!!

…この段階で何か嫌な予感がしなかったんだろうか、こいつらは…


とにかく、元の式は、TSCの値を 64bit 変数に代入し、それに 32bit のSC ( コード上は per_cpu() という関数の戻り値) を掛けて、10bit 右にシフトする、というものになった。


さて。ちょっとここで視点を変えてほしい。
「TSCに SC を掛けて、その後 1024 で割ると nano sec になる」
って事は、

TSCにSCを掛けると
大雑把に pico sec の精度の数字ができる

って事だ。
210 = 1024 ≒ 1000 = 103

なので、メートル法の縮尺単位が1つ変化する度に10bit消費される。
(ここは試験に出ます)

すると、pico sec レベルでの数字を扱うと、1秒を扱うには 40bit 必要、ということだ。しかるに、この値は 64bit の変数に代入されいてる。って事は「1秒以上」の表現には 24bit しか有効桁数がないって事だ。

224 / ( 24 * 60 * 60 ) = 194.180741日

実際には、1024の方が若干大きいので、その端数が載る。具体的には 64bit変数を最後に 10bit 右にシフトするので、有効桁数は 54bitになる。これが nano sec で表された時刻なのだから、

254 / ( 24 * 60 * 60 * 1000 * 1000 * 1000 ) 
= 208.499983日

めでたく、今回 overflow を起こした日数そのものが出てくる。64bitは、無限にでかいに等しい桁数ではないのだ。

かくして、cycles_2_ns() は、208.5日経つと、桁あふれを起こして 0 に近い値を出力する。sched_clock() はこの値を利用しているため、ここもぶち飛ぶ。

スケジューラーはここの値に強く依存しているので、突如として 54bit の数字が 0 になると、カーネルのあちこちがぶっ飛んでしまい、reboot に至る。


感想
497日問題の反省
全く活かされておらずっっ!!!


------

あれから2年…どうやら新しい 208.5日問題が発見されたらしい…

今回は私なんぞよりよほどわかりやすく説明してくれる人が現れた。お陰でリンクを貼るだけで済む。ありがたや、ありがたや… (-人-)

今度のは CPU のバグとペアになっていて、reboot すると freeze する、という実に悪質な…実運用に入っているサーバのメンテナンスとかで再起動しようとしたら freeze した…なんて事、その場に立ち会ったら心臓がショックで止まりかねないだろう…。

2011年12月11日

lunar eclipse

On year 2011, Dec. 10th around 21:30 - 00:00, we had Lunar Eclipse here at Japan.

Here's some of the photos I took.

Camera: RICOH CX1
Mode: ... ah .. I kept on changing it in the dark. So I'm not sure.
























And .. Battery run out ..... gosh ...

2011年11月24日

ホームゲート

都営大江戸線の青山一丁目駅。

ホームゲートが設置されているのに、まだ稼動していない。
ゲートは全開でその先にレールが見えている。

実運用が始まるとこんな光景はもう見られない。珍しいので撮影。

2011年9月23日

< 2.6.17 な Linux に存在する flock の double free バグ

Linux Kernel 2.6.17(正しくは 2.6.16.x で入ったのかもしれないが…) で新しく入った修正の一つに、flock(2) システムコール起因でカーネルがメモリをダブルバインドする、というものがある。

これを修正するパッチ自体は、2つ。
http://git.kernel.org/?p=linux/kernel/git/torvalds/linux-2.6.git;a=commitdiff;h=993dfa8776308dcfd311cf77a3bbed4aa11e9868
http://git.kernel.org/?p=linux/kernel/git/torvalds/linux-2.6.git;a=commitdiff;h=9cedc194a7735e5d74ad26d3825247dc65a4d98e
最初のパッチが問題の本質を直して、2つ目のパッチはその修正の際に、異常系処理が甘くてエラーが還らなくなっていたのを直した、というものだ。

あぁ、あれね、と判っている人はこのバグが高負荷時に結構酷い確率で頻発することも知っているでしょう。
RHEL4U4でもまだ直っていないので、是非 RedHat にギャーギャー言ってください。
*1

当然、ここでは「で? それはどういうバグなの?」という質問してくれる、ヨイ子のみんなのためにある。つーてもいきなり答はタイトルに書いてありますが。


*1) morikawaさん、情報ありがとうございます _o_。
つーかこれは修正し忘れですね。大昔、これを書き始めた時の情報を更新し忘れてました。



なぜコレをここに書くのかと言うと、理由は2つ。

1つ目は Linux Kernel の内部構造が double free に弱い(コレ自体はしょうがない)ため、類似のバグがあると、このバグを踏んだかなり後になってから いきなり kernel がぶっ倒れる、という事が多々あるからだ。この症状から真の原因を直接求めるのははっきり言って不可能に近い。が、なぜ不可能なのかをよく判っていない人に説明するのもかなり困難だ。なのでここに書いておけば、再びそういう目にあったときもここを見れば判る。

2つ目は、このバグがタイミング依存性の高い、かなりいやらしい構造をしたバグだから、だ。同期系のプログラムはかなり丁寧に動作を追わないと、いつ足元を掬われるか判らない。そのためには、いろいろな「実在したバグのケース」を記録しておくのが一番だ。というか、ややこしすぎて厳密に覚えていないと意味がないのに、すぐ忘れるからここに書くわけ。


とりあえず、パッチは上記にあるが、ソースコードはこんな感じだ。

2.6.16: http://lxr.linux.no/linux+v2.6.16/fs/locks.c の flock_lock_file()
2.6.17: http://lxr.linux.no/linux+v2.6.17/fs/locks.c の flock_lock_file()

で、この flock_lock_file() を呼び出しているのは sys_flock() 関数… flock() システムコールの入り口だ:
2.6.16: http://lxr.linux.no/linux+v2.6.16/fs/locks.c#L1495
2.6.17: http://lxr.linux.no/linux+v2.6.17/fs/locks.c#L1535

sys_flock() の中で flock_lock_file_wait() という関数が呼ばれている:
     http://lxr.linux.no/linux+v2.6.17/fs/locks.c#L1569

flock_lock_file_wait() はここだ:
     http://lxr.linux.no/linux+v2.6.17/fs/locks.c#L1496
で、flock_lock_file_wait() の中のここで flock_lock_file() が呼ばれている:
     http://lxr.linux.no/linux+v2.6.17/fs/locks.c#L1501

これが、全体の流れだ。

で、メインの処理は flock_lock_file() が受け持っている。

まず、flock_lock_file() の中で kernel 全体を lock している。つーてもシングルプロセッサの場合は何もしていなくて、SMPの場合だけ、lock が必要な場所で lock をかけている。これが lock_kernel() と unlock_kernel()。もし、kernel_lock がすでに取られていたら、SMPの場合はこのロックが解除されるのを待つことになる。

先に 2.6.17 …つまりバグが修正された後のコードを見て欲しい。lock_kernel() が呼ばれているのはここだ:
        http://lxr.linux.no/linux+v2.6.17/fs/locks.c#L739
一方、unlock_kernel() が呼ばれているのはここ:
        http://lxr.linux.no/linux+v2.6.17/fs/locks.c#L788

ようするに入り口の所すぐで kernel_lock を取得し、この関数から出ていくまで一度も解放しない。これが正しいやり方だ。この間のどこかで kernel_lock を解放してしまうと、仮にそのあとすぐ kernel_lock を再取得したとしても、その間に別CPUが処理に入り込んでくる危険性がある。

で、一方で 2.6.16 は
2.6.17と同様、関数の入り口でkernel_lock を取得している。
         http://lxr.linux.no/linux+v2.6.16/fs/locks.c#L722
で、関数の出口で kernel_lock を解放している:
         http://lxr.linux.no/linux+v2.6.16/fs/locks.c#L768

が、その途中で一度 kernel_lock を解放し:
        http://lxr.linux.no/linux+v2.6.16/fs/locks.c#L737
再度取得し直している。
        http://lxr.linux.no/linux+v2.6.16/fs/locks.c#L749

問題は、この L737 出の開放の後、L749 での再ロックまでの間に、別のCPUが flock_lock_file() 関数に入ってくる危険性がある、ということだ。最初の git パッチ にはこうある:

sys_flock() currently has a race which can result in a double free in the
multi-thread case.

Thread 1 Thread 2

sys_flock(file, LOCK_EX)
sys_flock(file, LOCK_UN)

If Thread 2 removes the lock from inode->i_lock before Thread 1 tests for
list_empty(&lock->fl_link) at the end of sys_flock, then both threads will
end up calling locks_free_lock for the same lock.
ちょっと見辛いし判りづらいな:
SMP環境において

Thread1: sys_flock(file, LOCK_EX)
Thread2: sys_flock(file, LOCK_UN)

がほぼ同時に呼ばれたとする。ただし、最初に kernel_lock を取得したのは Thread1 の方だ。で、Thread1 が最初の unlock_kernel() を呼んだ。

この瞬間に Thread2 は kernel_lock を取得できる。で、ここ:
        http://lxr.linux.no/linux+v2.6.16/fs/locks.c#L734
この場所で、inode->i_lock を解放する。
で、ここを通り抜けた後、Thread2 が unlock_kernel() を呼ぶと、Thread1 が flock_lock_file() の後半部を通り抜け、flock_lock_file_wait() へと戻り、そこから sys_flock() へと帰る。

問題は、Thread1 が sys_flock() へと帰った後。ここ:
http://lxr.linux.no/linux+v2.6.16/fs/locks.c#L1532
で、lock_free_lock() を呼んでしまうことだ。結果として、同じロックを2重解放してしまう。

lock_free_lock() のコードはここだ:
2.6.16:    http://lxr.linux.no/linux+v2.6.16/fs/locks.c#L157
2.6.17:    http://lxr.linux.no/linux+v2.6.17/fs/locks.c#L169

ここで問題なのはただ1点、kmem_cache_free() を呼び出すところだ。これを同じ構造体に対して行なってしまう。結果として、2重解放が行われる。



二重解放されたメモリは、やがて再利用される。二箇所の、全然関係ない所で。
それぞれを利用している kernel thread や、ルーチンはまさか他の人も同じ部分を利用している人がいるなんて考えてコーディングされていない。というか、それを疑ったら何もできなくなっちゃう。

で、ある日。自分が昔設定した値に従って処理を実行しようとしたのに、実はその値は別の人が別の目的で書いた値で…結果としてそのせいで、kernel 全体がクラッシュする、という状態に陥るのです。

2011年9月16日

Brave to stay

This brave Mantis has been on "outside" of the window of 29th Floor elevator hall since noon.
I shoot this photo on my way back to home, 17:48, and (s)he was still there!

Hanging upside down on the glass must be tremendously tough job. And since (s)he have a wing to fly, it's not hard for h(im|er) to give up and fly a little bit aside for better environment. What a brave.

...

Well ... it might simply be that this insect was stupid as to stay though ... staying on tough situation for nothing ...

2011年6月11日

リサイクルループに着目しよう

節電が叫ばれている。一般家庭も企業も15%ぐらいは節電して欲しいそうだ。

で、老人が「ペカペカ光るもの」は「電気を食うに違いない」とボケを垂れ流している。が、正直、それではたいして節電にならない。本当に電気を食うものってそんな所にはないからだ。

というか、「ペカペカと光る」とか「キンキンに冷える」とか、そういう擬音付きキーワードな代物は、別に昨日今日目の敵にされたわけじゃない。なのでアチラコチラに工夫がしてあって、実はかなり電力を食わないようになっているのだ。だからそんな所を今更攻めたって、たいして節電になどならぬ。

じゃぁ、どこにエネルギーは消費されているか?
エコロジーだともてはやされたもの。CO2削減にはこれ、ともてはやされたものにこそエネルギー消費は隠れている。
というわけでリサイクルを槍玉にあげてみよう。

リサイクルって言うのは「モノ」の形状を変えて、同じ物体をなんども変形させながら使い回しする、っていうのが基本発想だ。ペットボトルを粉砕して溶かしてもう一度ペットボトルにするとか。紙をパルプに戻して漂白してまた紙にするとか。「モノ」を無駄に破棄しない、と言う意味では素晴らしい。

でもちょっとまってくれ。そもそも、その「モノ」は何のために消費されているのだ? 我々は「ペットボトルを買いたい」わけじゃない。我々は「紙が欲しい」わけじゃない。
ペットボトルなら、その中にあるジュースが欲しいのだ。紙が欲しいのではなく、紙に印刷されている情報が欲しいのだったり、紙に包まれている中身が欲しかったりするのだ。まさに輸送のためのケースがペットボトルであり、紙なわけだ。だからこそリサイクル出来るぐらい損傷少なくペットボトルや紙を回収できる。

しかし、リサイクル・ループはエネルギー的に見れば決してただじゃない。輸送にはガソリンが、加工には電気が使われているはずだ。

じゃぁ、ペットボトルを使わない、紙を使わないで目的を達することは出来ないだろうか?
非炭酸系の飲み物をペットボトルから紙容器に切り替えるとか。
新聞を紙から電子版に変えるとか。

本来の目的からすれば別に欲しいわけじゃないモノを使わないようにする事で、リサイクル・ループを流れるモノの絶対量を減らす。しかも「本来の目的」は別にセーブしなくてよい事になる。


今回の節電は、目先の電力消費量を変えるのではなく、リサイクル・ループを流れる物流を減らしつつ、無駄にモノを廃棄しない、という形を取らないと苦しいと思う。目先のペカペカしたものを排除するような発想では、長期的に見ると必要なエネルギーが減らないままだ。

2010年12月5日

【DS】A列車で行こうDS 「青葉経済開発区」「標準」


というわけで、新しい地図「青葉経済開発区」…と言っても、演習問題でやった最後の地図だが。これの「標準」モード。

目標は複数。

  • 総資産1000億円
  • 2年連続黒字
  • 子会社数10社
以上を10年以内で。

とはいうものの、これだけだと簡単だ。そこで追加の条件。

  • 株式公開しない
  • 借金しない
  • 地下鉄駅は作らない(陸橋上に駅は作るし、一部は地下鉄にする)
  • 鉱脈は掘らない
  • 資材置き場は全部地面の下(だって地上に用意するのもったいないじゃん)
最初の資本金は600億円で、この一部が土地だの電車だのになった状態でスタートする。2年連続で黒字になれば、株式公開できる。すると資産が一気に増える。ここに借金分を追加すれば総資産1000億円は実は簡単だ。子会社だって黒字・赤字を無視すれば10社ぐらい簡単に作れる。運がよければ黒字になって、総資産を増やしてくれるだろう。

が、株式公開も借金もしないとなると、一気に条件が難しくなる…はず。


この土地は丘の上と下に別に町があるので、それぞれについて小さなループを作ってやる。とりあえず、一方づつ片付けつつ、もう一方もある程度発展してもらうには、この小さなループが一番。ただし、あとでループを伸ばせるように、伸張部分相当の土地は買っておく。

丘の上のループと丘の下のループそれぞれ上下の水平線路の間隔が10マスになるように取る。すると駅をうまく配置すると、町が発展したときに複数の駅が同時に発展する事ができる。並列な線路それぞれの駅の影響範囲をオーバーラップさせればいいわけだ。で、それを丘の上と下でも実装するために、丘の上と下の間、丁度10マスの所に水平に土地を買っておく。

で、とりあえず子会社を10個作って稼がなくちゃいけない。そこで上の範囲指定。離れ半島の根元、一番左側だ。
ここに工場地帯を作る。目的は画面外の「隣町」への資材の輸出。
一番最初は線路を引いて、隣町から資材を輸入。資材が1つでも発生したら、速攻で工場を建て始める。で、工場ができたらこれまた速攻で輸出に切り替える。これで期限内に黒字になるに違いない…と思ってやってみたのだが…
実際には、工場を建てるコストが結構高いので、10年以内に黒字になりませんでした。つまり計画倒れ。

もう一つ、打った対策がこれ。上のループ側に工場があるように見えなかったので、下の工場から道路を引っ張って上のループのそばに1つバス停を設置、その真下に資材置き場をたくさん用意して、資材不足にならないようにした。

というわけで、以上の敷設が終わった直後の地図がこれ。
線路とか、ほとんどまだ撤去できていません。撤去費用がもったいないんだもの。その間に地下鉄の技術を開発する。

さて。しばらくして人口が増えてきたら、路線を延ばします。伸ばし方はこう:
下のループを解除してうねうねと南の端まで。南の端で線路が切れてる?!いいえ。では見てみましょう。
見てのとおり、南の端は地下に潜っています。
で、出てくる側はここ。とりあえず、地下を通って一気に北上します。

さらに時間が過ぎて、人口が増えてきたら、さらに鉄道を延ばします。こんな感じ:
この段階で、南の端から潜った電車は、画面右端の一番北側…右の方に「山」があるんですがその根元から湧き出しています。B1だとまっすぐ伸ばすのが難しいのでB2まで沈んで、一気に北東へ、で必要な高さまで再び上昇。

さて。会社運営は初年度を除いて毎年ギリギリ黒字でして。ですので2年連続黒字は簡単。子会社も工場を建てたり、マンションAを建てたので10社は楽勝。唯一の問題は資産1000億円なんですが。

10年目の8月。ギリギリ、なんとか資産1000億円クリア。
見ての通り、どうにか全部あわせて月20億円づつ収益が上るようになったので、それを子会社に再投資…を続ける。あ、あと福利厚生は毎年ガッチリプランに入れて、社員のやる気を高めます。
1月から5月までは一切投資せず、6月から12月に一気に子会社を作ると、だいたい収入の40%を税金用にプールできるので。8月だから残り7ヶ月ですか。結構ヤバかったかも。

さて、条件をクリアしたら、あとはひたすら発展させていくのみ。

見ての通り、10マスごとに東西に平行な線路を引いていくわけですが、一気に伸ばすと人口密度が薄くなってしまうので、とりあえずは上下方向に伸ばすようにしました。左右方向はとりあえず上下方向が一通りそろったら。

で、左右方向の左端から半島に、半島から南の島へと線路を延ばそうかと。あ、半島の右にある島は開発対象にしません。面積が狭すぎて駅が建てられねぇ。

で、この辺まで伸ばすと、資材不足が頻発します。工場から遠すぎて、資材を必要とする場所と、工場との間に別の資材不足が発生すると、資材が届かなくなる。そこでカーソルのある場所にも工場を建てることにしました。
もちろん、画面ギリギリに作っているのは、これらの工場群の第一目標はあくまでも隣町への資材の輸出だから。資材が必要ないときは輸出量を多く、必要になったら少なく調整する。

というか、これは発展させていく過程で判ったのですが、巨大な1つの循環路は、線路を延ばすたびに貨物列車が一周する時間が長くなります。で、そうすると工場から資材を回収する頻度が下がり、輸出量を多くしなくちゃいけない。でも我々の町側も実はあちこちで資材を必要としていて、貨物列車の数が足りていない(資材不足が発生しまくる)という問題が…。


そうこうしているうちに、新たなる問題が。
判りますか? 駅が建設できないんです。
…さっっ、先に言ってくれぇ(T.T)
って言うか、少なすぎるだろう。80って。

とりあえず、駅を増やせない以上、どこかの駅を引っぺがして新しく配置するしかない。
幸か不幸か、今の駅の配置はほぼ全ての土地を2つ以上の駅が共有するようになっている。なので、どこか…そう。南側の土地を集中的に開発してしまい、その場所については急激な人口増加を必要としない状態にしてしまえば駅密度は半分…つまり土地の発展に影響される駅の数が1つになるようにできる。に違いない。
で、とりあえず、駅を引っぺがせるようになるまでの間に、残りの線路を引いておく。

半島に入る線路は、北側が半島に入るほう、南側が半島から出る。

半島に入ったらまず、最初に作った工場のそばを通り、ここで資材を拾う。で、その直後に地下に潜って、島の北側へ。島をグネグネと移動したら、今度は半島の南端へ橋を渡る。で今度はグネグネと半島を北上する。海岸が東西方向に近い部分は一部の引き戻しを地下で行うけれど基本は地上を通るように線路を引く。で、最後に半島を抜けて、大陸側のループに戻る。

ここまでで61年目。
なのだが、ここからが長かった…

結局ループが長すぎて、地図全体で4箇所も工場地帯を作っているのに、資材配給がどんどん滞るようになっていく。とりあえず、全体開発完了の目標として、総人口100万人を目指したのだが、資材が届かないのではいかんとも発展しがたい。当然子会社の上限とかが先に来るわけで…作っては売り、を繰り返すと社員のやる気がすごい勢いで落ちていくし…。
結局、我慢比べ大会のような状態でジリジリと開発を進めていって、総人口が100万人を超えたのは 190年後の事に。全体はこんな分布:

会社の状態はこんな感じ:
やはり、もう社員の不満はどうしようもなく。

会社沿革はこう:

なによりも残念なのは、人口を100万人にしても会社沿革に載らない事。いや、総資産10兆円オーバーも、100兆円オーバーも載らないんですけどね。

結局地図全体はこうなりました:

最終的な反省点。
  • 巨大循環路は退屈だ。
    なにしろ時間を掛ければ絶対成長する事が判りきっている。一方で、資材不足とかが頻発しすぎる。循環路自体はともかく、全部を1つの循環路にするのは考え直したほうがよさそうだ。
  • 隣町との交易専用の駅は、駅舎の無駄。
    隣町へ輸出するために工場のそばに駅を作るのはいいのだが、距離を最適化しすぎると、輸出にしか使えない駅になってしまう。結果として、その工場群から「自町用の資材」を拾うための駅をもう1つ作る羽目になり、これが駅の無駄遣いになる。
    1つの駅で両方の目的をクリアし、ついでに町の発展分(21x21マス)の計算にも入れられるよう、配置するべき
というわけで。以上の反省点を踏まえて、「いばらの道」を攻略する事にしよう。