物理的見地からみた計算機構:古代COBOL文明(予備的考察)

古代言語COBOL

COBOLポケットリファレンス

COBOLポケットリファレンス

近年日本では主にメインフレーム上での主流の開発言語であるCOBOLについての書籍の刊行が相次いでいるように思います。具体的には上掲の本の著者が年1冊ペースで近刊を上梓していますが*1 *2、これがどのような需要を反映しているのか筆者にはわかりません。
最近メインフレームが業務の中核にある古典的な企業、というものを垣間見る機会に恵まれたために、筆者はこの世界に興味を抱き、少し調べてみることにしました。

メインフレームの典型的想定

メインフレーム実践ハンドブック z/OS(MVS),MSP,VOS3のしくみと使い方

メインフレーム実践ハンドブック z/OS(MVS),MSP,VOS3のしくみと使い方

COBOL*3というのは計算機言語であって、それ単体としては単に複雑で直交性を欠いた構文を持つ、古めかしく機能の少ない言語といえるでしょう。しかしその機能と開発様態、典型的なプログラムの構造はいわばその「下部構造」*4たるメインフレームのハードウェアの特性を抜きにして考えることが困難です*5。現代のPC、Linuxサーバとは全く異なる発想がそこにあります。
簡単にメインフレームの基本的諸条件を列挙します(思いつくままに書いているので乱雑ですがご容赦ください)。

  • メモリは高価であり、節約して使う
  • CPU時間は高価であり、節約して使う
  • I/Oは廉価である
  • ディスクやテープは廉価である
  • 計算機は人間を待ってはならない
  • 人間は計算機を待たなければならない
  • プロセッサは主記憶とのみやりとりする
  • 一種のDMA機構であるI/OチャネルがあらゆるI/Oを行う
  • 入出力デバイスは文字指向である
  • 計算速度と比較してI/Oは高速であるか、少なくとも非同期的
  • RISC以降に一般的になったデータのアライメント(アラインメント)は考慮されない*6
  • メモリ上のデータにはバイト単位で可読性があり、最低限の処理で高速大量に印刷してそのまま人間が理解できる*7
  • ディスクへのデータの書き出しとテープへのデータの書き出しは実質的に同じであり、ディスクは基本的にはオペレータが編成を操作するために立って歩き回る必要のないテープの山として扱われる
  • あらゆるハードウェアの差異はOSが吸収する
  • OSの機能は貧弱であり、データの交換はディスク、もしくはテープ上のファイルを経由する
  • 対外通信は非効率であるためサブシステム、通信制御装置に集約する

これらの条件は典型的なメインフレーム上の開発や運用のあらゆる側面を規定しています。

計算量

メインフレーム上のあらゆる操作はO(n)であると想定されます。これは明らかに言い過ぎですが、バッチ処理の時間がデータ件数に比例すると仮定され、トランザクション処理の時間が取引件数に比例すると仮定されることが死活的に重要です。
O(n)でない操作の典型はソートであり、これについては後述します。

経済的インセンティブ

ほとんどのメインフレームはレンタル契約で運用されており、CPU時間に対して課金されている筈です。これはメインフレーム上で経済的価値の低い処理を行うことに対する強い負のインセンティブとして機能します。前提となるのは上述の、線形計算量の想定です*8
これは、取引件数が経済的価値に対して線形な業種でメインフレームが生存しがちであることを説明します。
また、これはメインフレーム上でのプログラム管理が最低限のものに留まる*9理由も説明します。

メインフレームで忌避されるアルゴリズムとデータ構造

CPU時間による課金、と考えると、例えばピークロードの時に性能を稼ぎ後でその代償を払うような最適化は「ならし計算量」*10で見てどうなのか、という問題が生じます。もともと、I/O性能の高さと、チャネルがDMAであるためデータ転送がCPU時間に影響しないことを考えれば、メモリの浪費にならない範囲で最初からsparseなデータ構造を採用することがおそらく最適でしょう。denseなデータ構造をCPUで展開してsparseにすることは避けなければなりません。
CPU intensiveなアルゴリズムはともかく避ける必要があるし、O(n)でないもの、非決定的なものも避ける必要があります。これは現代的なプログラミングにおいて見慣れた可変長文字列、表引き、抽象データ型、暗号、ハッシュ関数連想配列正規表現、リスト構造、ツリー構造などはメインフレームのプログラムでは避けるべきであることを意味します*11。従って、典型的なCOBOLプログラムはそもそも再帰的な処理をほとんど含まず、固定長データ構造と順編成ファイル*12を使うよう条件付けられていることになります。
検索頻度の高いデータについてはインデックスの作成は引き合う選択になりますが、ISAM*13とかVSAM*14といったインデックス付きのレコード構造*15はOSが*16これをサポートし*17COBOLプログラムの中でこれらのインデックス生成が明示的に書かれることはありません。
ソート処理については、多くの場合メインフレームベンダーが自社のハードウェアに最適化したユーティリティーを提供しているようです。これらはバッチジョブの中で、ユーザーの書いたCOBOLプログラムと協調して呼び出されることになるでしょう。*18

アーキテクチャの淘汰圧と計算の効用の線形化

メインフレームのI/Oの高速化はチャネルという一種のDMA機構によって実現されていますが、結局、これはCPUの計算をチャネルにオフロードしていることに他なりません。チャネルの先のディスク装置が十分な規模を持っているなら、チャネルは休む暇もなくデータをコピーするわけです。では、なぜチャネルの計算負担は課金対象にならないのでしょうか?
こう考えると、CPU課金にはもはや限られたCPUパワーの経済的配分という意味はなく、経済的価値のある(経済的価値に対して「示量的」「1次同次」な)計算からメインフレームベンダーが相応の利益配分を得るために重要であることがわかります。これは、経済的価値のある計算を安全に遂行するためのサポート体制(保守人員の教育負担や高速なディスク装置の維持費、開発費などを含む)が、その計算の経済的価値に基づいて配分されることを意味します。
また、CPU課金の正当性(スループットとの比例関係)を維持するためには、CPU性能に見合ったI/Oの増強が不可避であったことも明らかです。つまりCPU課金という取引形態がメインフレームの進化を方向付けたことになります。

バッチ処理:射影としてのラン(実行)、認識過程としてのジョブ、信頼性

メインフレームにおけるバッチ処理について検討しましょう。
経理処理は転記や総和などを経て生の数字を要約していく処理であり、報告書の作成も同様です。一般的に、入力ファイルを読み取り処理して出力ファイルを生成する過程は情報の単純化、射影と考えることができます*19。これはガベージイン、ガベージアウト、として知られていることで、入力ファイルに無い情報が出力ファイルに付加されていたならば、それは処理プログラムが情報を生成していることになります。
典型的なメインフレームのジョブでは多数のプログラムが中間ファイルを生成してその中間ファイルを更に処理して新たな中間ファイルを生成することになります*20メインフレームOSにおけるプロセス間通信の手段は限定されており、基本的には情報の要約の各段階に対応した中間ファイルが生成される、と考えてよいでしょう。
さて、ある段階でプログラムの誤りによるデータの破損や、何らかの事情による中間ファイルの欠如が判明したとします。このとき、メインフレームバッチ処理では入力データの変更が締切られていさえすれば、問題のあった箇所以降をリラン(再実行)することで処理を修復することができます。つまり、チェックポイントの生成は、メインフレームのジョブの設計においてごく自然に行われていることになります*21。並列シスプレックス(クラスタリング)やパリティECC等の例外検出は、このようなジョブの実行形態を前提として信頼性の向上に寄与するものとなります。
もちろん現代のメインフレームにおいて、メモリ上に巨大なオブジェクトを抱えて延々とその状態を変更することは技術的には可能ですが、CPU課金と、安価なI/Oが前提にあるとき、この選択は非合理になります。
CPU課金が大量の情報の書き出しを促すことになっているわけですが、これはメインフレームではオープン系よりも同じ計算を行うのに大量の物理的作用を伴うということでもあります。これを統計的観点から整理すると*22、メモリ上で行われる計算の正しさよりも、ディスク上に書き出される計算の正しさの確度がより高い、ということになります。

トランザクション処理:入力の安定化と遅延

トランザクション処理についても検討しましょう。
前節で少し触れた並列シスプレックスで、クラスタの切り替わりのタイミングにかぶったトランザクションは犠牲とならざるを得ません。過渡的な不整合状態が固定することを防ぐためには、ストレージの共有や、入力の照合、待ち合わせ、多数決、端末へのトランザクション電文不受理の通知などが一体となって実施される必要があり、これらの処理は相応の安定化期間を要します。これは、特に冗長化を行っていない計算機がRAMの状態を書き換えることに比べて入力から応答までの待ち時間が増すことを意味します。もちろん、トランザクションの開始時のログイン記録も忘れてはいけません。
前節のように基礎的な原理に訴えれば、これは端末がキーボードからの文字入力を行う際のチャタリング除去や、音声のサンプリングを行う場合のローパスフィルタと同じで、反応速度と引き換えに確実な入力を得ていることになります。

中間的結論

メインフレームに対して支払われているコストは過渡状態を排除し再現性、確実性を向上させるために、計算の内部状態に対してオープン系のシステムよりも大量の物理的作用を伴わせることの代価である。信号の増幅であるから電力を要し、また大量の物理的装置の製造コストも必要である。

*1:asin:4798038857

*2:asin:4877833072

*3:本稿では特に断らない限りメインフレーム上のCOBOL85の標準的な実装を想定し、その他の環境、およびCOBOL2002については必要があれば都度明示します

*4:社会的現実をその物質的条件である「下部構造」と、その非物質的な特徴である「上部構造」に分けて考え、前者から後者への因果関係を重視することはカール・マルクスに由来し、これは人文科学、社会科学の分野では古めかしくはあっても完全に捨て去ることのできない基本的な考え方になっています。なお本稿は計算機科学の文脈においてマルクスに代表される「労働価値説」が持つ意味を検討するものともいえます

*5:PC上で現在もCOBOLアプリケーションを使っている事例は稀でしょうし、オープン系のCOBOLへ移行する場合、いずれはJavaで書き直される可能性が高いでしょう

*6:現代のメインフレームは実際にはPower系などのRISC CPUで動作しており、たとえOSでミスアラインメント例外を捕捉するとしても性能的なペナルティが大きい筈ですが、COBOLのPICTURE句で宣言した記憶域がメモリ上で宣言通りになっているかどうかについて、筆者は知りません。少なくとも順編成ファイルとして書き出された時点ではPICTURE句の通りになっていなければ互換性が保てない筈ですが

*7:上述のアラインメント問題や印刷時のデータ型の扱いを考えるとこの想定は厳密には正しくない可能性があります

*8:つまり、メインフレームの価値は時給で計られ、その算定根拠となる工数はCPU時間

*9:たとえばSubversionやGitのようなソースコードの版管理はCPU時間の浪費となるため、メインフレーム上で行うべきではありません

*10:償却計算量、とも http://en.wikipedia.org/wiki/Amortized_analysis

*11:これらすべてを避けてなお計算機に意味のある仕事をさせることができることは驚くに値します

*12:http://ja.wikipedia.org/wiki/%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E7%B7%A8%E6%88%90%E6%B3%95

*13:http://ja.wikipedia.org/wiki/Indexed_Sequential_Access_Method

*14:http://ja.wikipedia.org/wiki/Virtual_storage_access_method

*15:これらは磁気テープ上では意味をなさないか、そもそも実装することができない

*16:正確にはCOBOLランタイムやユーティリティー群が

*17:IMSやAIM、DB/2などのデータベースサブシステムもCOBOLから見た扱いは基本的に同じです

*18:なおこのようなジョブをメインフレームからオープン系に移植した場合にはソートユーティリティーを用意する必要があり、そういった商品がいくつかあります http://www.nec.co.jp/pfsoft/sortkit/ http://jp.fujitsu.com/group/fmw/services/powersort/

*19:http://ja.wikipedia.org/wiki/%E9%A0%98%E5%9F%9F%E7%90%86%E8%AB%96

*20:昔のPC用コンパイラで多数の翻訳段階があり、中間ファイルが生成されていたことを思い出してください。メモリ容量が増えるに従い、こういった実装は姿を消しました

*21:おそらくOS側にも同様の工夫があるのでしょうが、ここでは検討しません

*22:つまりバッチ処理量子論の「測定」とのアナロジーで考えると