筆者の個人的な経験によるデバッグの仕方(大阪弁モード) [Top]

本ページは、Satoshi Takahara先生の、”ホー ムページ大阪弁化計画”によって、大阪弁化されたものです。変換による、原文との微妙な相違や、不具合はチェックしていま せん。

この大阪弁化ホームページは実質上使用不能になってしまっていたのですが、 最近新たに[大阪弁化ページ ](大阪弁以外にも多数の方言などに変換できます、上記Satoshi Takahara先生のページのパクリだそうです)を発見しました(本ページは、この新たなページでの再変換は行なっておりま せん)

目次
デバッグの仕方
標準となるプログラムを用意しておくこと
考えが煮詰まったらちーとの間うっちゃっておく
その他、デバッグのための(経験的な)常套手段
常套手段(まとめ)
どないな時でもデバッグはエライである
予想されるバグの例
並列化作業でのバグの例
ここでは各論(各サブルーチンの説明)に入る前に、きょうびやらかしたバグの 話しをしてみたいと思うで。バグがあったのはこのルーチンや。クリック

このルーチンは希土類計算を目的として、非局所f部分に対応するルーチン をs、p、d非局所に対応しとった元のプログラムに付加したもので、この改 良過程で生じたバグや。全エネルギーの計算結果とストレスの計算結果が一 致せんとうい問題が生じたんや。十分にエネルギーカットオフを取れば、全 エネルギーの最小なトコでストレスの値はゼロになるはずやけどアンタ、実際計算 してみると、全エネルギー最小なトコで、ストレスの値が有限で大きな値を もってしもたんや。

このルーチンの問題を以下に説明しまんねん。文番号1400の変数Lに関する ループの外側で、CWLなる配列に値を代入していまんねん。ここで、CWL(1 0)は扱う原子がs、p、d非局所かs、p、d、f非局所かで値が異なりま す。それを次のループ(文番号1922、1923)の内側のIF文で条件設 定していまんねん。

実はこれが間違いで文番号1923のループは個々の原子に関するループで、 このループ内ではZFCなる配列変数の初期化を行なっとるだけや。そし てCWLが実際に使用されるのは次の文番号1420、1410のループや。 トコロが、CWLの値は前の文番号1923のループで定義されていまんねん。

扱う系が原子1種類だけの場合は問題ないのやが、s、p、d非局所な原 子とs、p、d、f非局所な原子からなる系を扱う場合には正しい計算を行な わなくなるんや。最初、Eu(bcc構造)の計算を行なりよった時は、全エネルギー とストレスの値の関係は正しいものやった。トコロがEuS(岩塩構造)では正 しい関係にならず、先に述べたような不一致が出てきたんや。これは、このルー チンのまんまだと、最初の原子(Eu、希土類、CWL(10)=3.0D0)の段階で、次の 原子Sの設定(CWL(10)=1.0D0)が行なわれてしまっていまんねん。

このためs、p、d非局所とs、p、d、f非局所の2種類以上の原子から なる系を扱う場合はバグが存在することがわかるんや。ここでバグを修正した 正しいルーチンを示しまんねん。クリック
この修正された(必ずしも速度的なチューンアップや、プログラムの構造的 な正しさ美しさを考慮していまへん。)ルーチンをみればわかるように、CW L(10)のIF文による条件別設定を文番号1410のループ内で行なうよ うにしたんや。これでEuS岩塩構造でも全エネルギーとストレスの関係は正し く出るようになったんや。(但し、希土類金属としての計算が正しくできるよ うになりよったわけではおまへん。実は、希土類金属の擬ポテンシャルによる計算は大 変難しく、まだ検討せなならへんことが多数残っていまんねん。)

デバッグの仕方

バンド計算プログラムを作っとると、いろいろな局面でバグに遭遇し、何 日も何週間も、場合によっては何カ月も悩まなければならへんことがあるんや。 ここでは、そのような場合(特に、何カ月もデバッグに費やさなければならな い時)での対処の仕方について述べていきたいと思うで。

(1)標準となるプログラムを用意しておくこと。
これは既存のプログラムを拡張、改良する場合には当然必要なことや。つ まり改良、拡張中にバグが見つかり、デバッグ中の場合、最低この段階まで戻 れば正しい結果を与えるオリジナルプログラムを用意しておくことが必要や。 このオリジナルプログラムが必ず正しい結果を与えるのならば、改良、拡張中 のプログラムの実行結果のチェックがいつでも可能で、万が一、不一致が生じ てもプログラムの変更箇所が容易にわかり、デバッグの助けになるんや。

(この時、改良、拡張は一気に行なうのではなく、適当な段階毎にコンパイル、 実行し常に計算結果のチェックを行ないながら仕事を進めていくのがええかと 思うで。こうしておけば、結果の不一致があっても、最初のオリジナルでは なく直前まで正しく動いとったバージョンと比較できるので、変更箇所の量が オリジナルと比べてずっと少なくて済み、間違っとる部分を発見することが どエライ楽になるんや。〔その変わりバージョン管理をしっかりやらねばなりま せん。〕)

もし正しい結果を与えるオリジナルプログラムがない場合、実行結果が明ら かにおかしかったりする場合はまだええのやが、どうも微妙にとか、何とな くおかいし場合に、よって頼るべきもの(ゴチャゴチャゆうとる場合やあれへん,要は、この段階での計算なら正し いと自信が持てるもの)がないので、デバッグがどエライ困難なものとなるんや。

(2)考えが煮詰まったらちーとの間うっちゃっておく。
デバッグちうものはどエライ困難な作業であり、プログラムのコーディング は、数日、数週間でできても、デバッグは何カ月もかかってしまうことがあり まんねん。何カ月もかけてバグがとれればええのやが、場合によってはにっちも さっちもいかなくなることもあるんや。ほとほと考えが煮詰まってしまって万 策尽き、もし、他にも重要な仕事があったり、逆にそれほど時間的に切迫して いなければ、思い切ってデバッグ作業を一時中断し、別の仕事を行なうか、家 に帰って寝てしまうかして、気分転換を計るちう手があるんや。
これはあくまで、経験的な話しやけどアンタ、2週間とか1カ月ほどデバッグ作業 をせんでいて、久しぶりに問題のプログラムを眺めとると、ふとバグに気 付くことがあるんや(必ず気付くわけではおまへんし、何の根拠もない)。

他にも、”間際効果”ちうものがあり、学会発表や、D論の発表が近付く と、不思議とバグが見つかり、一気に仕事が進むことがあるんや。(この間際 効果なるものにも、その存在を証明する根拠はどこにもないや。)

(3)その他、デバッグのための(経験的な)常套手段
いくつかあるんやが、思いつくまんま以下に挙げていきまんねん。

(イ)取り敢えずデバッガーを使ってみるちうわけや。
対象となる計算規模がさして大きくなく、アヤマチもすぐに出てくる場合は、 この方法はどエライ有効であると思われまんねん。但し、デバッガーの使い方を熟知 しとることが必須や。また計算規模が大きい場合、デバッガーが動かない 場合があるんや。更に、コンパイラーの種類にもよるんやが、UNIX系のコンパ イラではデバッグオプション(普通 -g)を付けると最適化を行ないまへん (最適化モード用のデバッグオプションのあるコンパイラ〔例DEC-FORTRAN〕 もあるにはあるんや。)。従って、アヤマチの出てくる箇所まで到達するのに大 変な時間が掛かってしまうことがあるんや。

従って、どのような場合にもデバッガーが万能であるとは言えまへん。因み に筆者はめったにデバッガーは使いまへん(使い方も良くわかっておらへんのも 理由の一つ)。

(ロ)計算の途中結果を出力するちうわけや。
これは、一番確実で正統的な方法かもしれまへん。とにかく、アヤマチ箇所に 到達するまで、各ルーチン毎に計算結果を片っ端から出力するちう手や。 この場合、前述の確実に正しい結果を与えるプログラムによる途中結果との比 較ができればアヤマチ箇所を見つけることは比較的容易な場合があるんや。但し、 気を付けへんと、大規模な繰り返しDOループ中に下手にWRITE文を書い ておくと、エライ量の出力が生じまんねん。もしこれをファイルに書き込むように しておくと、たちまちディスクが溢れてしまい、システムが止まってしまう可 能性すらあるんや。またバッチジョブ形式でジョブを投入してると、例え6番 に書き出して(WRITE (6,100) A(N)等)いても、ジョブ用のバッファが溢れて しまいジョブが異常終了してしまいまんねん(場合によっては、これもシステムを 止める原因になるんや)。

計算途中のデータを書き出すときは、くれぐれもこのようなことにならへん ように細心の用心(特に所属組織の共通〔共有〕の計算機システムを使ってい る時)が必要や。データの出力はなあんもぜええんぶひとつのこらず出す必要がなく、例あげたろか,たとえばやなあ大きなD Oループ内の変数の場合、可能ならその和をとり、それ同士を比較するちう 手があるんや。

(ハ)コンパイル時のオプションを変えてみるちうわけや。
コンパイラの最適化が厳し過ぎるため、正しくない結果を与えることがあり まんねん。途中で異常終了する、計算結果が明らかにおかしい場合に、コンパイラー の最適化レベルを下げてもっかい計算してみると正しい結果を与えることがありま す。この場合、コンパイラーのバグなのか、プログラム側のバグなのか判然と せん場合もあるんやが、他機種では最適化しても正しい結果を与える場合は そのコンパイラーのバグを疑ってみる必要があるんや。

このようなコンパイラーのバグと思われる場合、アヤマチの起こる箇所付近に WRITE文(時にはコメントやらなんやら)をソーニュー(うひひひ...おっとカンニンや)するだけで正しく動いてしまうこ とがあるんや。このような場合、プログラム上の問題が残っとる場合もあり 得まっけど、コンパイラーのバグも疑ってよいかと思うで。このような時には、 コンパイラーの製造元(普通、大型機やワークステーションの世界ではコンパ イラーとマシンの製造元は一致しまんねん)になるべく早く問題を指摘することを 勧めまんねん。相手側が適確に動いてくれるかどうかは当該するメーカーに強く依 存しまんねん。

ただ、この手のコンパイラーのバグに引っかかるプログラムは、プログラム 側にも何らかの問題(プログラムの内容にどエライトリッキーな部分がある、例 えばサブルーチンの呼び出す側と呼び出される側の引数〔変数〕の型を〔高速 化のため〕わざと一致させへん場合があるんや。ホンマはやってはいけへんこと やけどアンタ、それでも大抵コンパイラーは通ってしまい、正常な結果を与えてしま いまんねん。)を抱えとる場合がようけあるんや。一概にコンパイラーだけを悪者 にすることは(もっともっともっともっともっともっともっともっともっと重大な問題を見落す可能性があり)エライ危険なことにな るんや。

(ニ)異なるシステム同士で比較するちうわけや。
異なる計算機上で、問題のプログラムを動かし、アヤマチの出方を比較すると いう手があるんや。これはどのような場合でもうまく行くちうわけではあり まへん。かなり特殊な状況の場合と思っておくんなはれ。

デバッグの場合、時にはアヤマチの箇所が判然とせず、アヤマチの原因も皆目見 当もつかない場合があるんや。この時、異なるシステムで問題のプログラムを 動かし、計算途中の結果やアヤマチ文句の違いからアヤマチの原因を見つけ 出せることが(たまに)あるんや。せやけど、これは互いのシステムをある程度 以上熟知しており、この手の計算やデバッグの経験が相当ないとこの手の手法 は使えへんと思うで。

(ホ)どなたはんかに相談するちうわけや。またはどなたはんかとアヤマチについて議論してみるちうわけや。
意外とバグちうのは、担当しとる本人には気付きにくいものやが、他 人がみるとすぐに指摘できてしまう時があるんや。また、共同研究者や、同じ 研究室の同僚(テーマは違っても大抵バンド屋はんと思われまんねん)とプログラ ム上の問題について話し合っとる内に、ふと解決策が頭に浮かんでくること があるんや。場合によっては、まるっきしの門外漢の人との議論からアヤマチの原因が わかることもあるんや(あるでっしゃろ)。

(ヘ)バグも仕様の内(「マーフィーの法則」アーサー・ブロック著、アスキー 出版局刊より)としてしまうわ。
これはエライ危険な解決策や。明らかに、バグが致命的でなく、計算上もさ して重要な影響を与えることはないと判断できるなら、バグをそのまんまにして おくちう手もあるんや。特に、単なる出力フォーマット上の問題のような、 明らかに計算そのものにまるっきし影響を与えへんようなバグは、一時シカトしてほうっ ておくことも可能や。

但し、バグが計算に関わっとる場合は、安易にそのまんまにしておくことは 決して勧められまへん。余程その計算に熟知し、この手のバンド計算に熟練し ていなければ、鷹をくくることはどエライ危険や。今、問題がなくても後々大 問題になる可能性が大きいや。

これからバンド計算に手を出そうとする人は、バグがあれば徹底的に原因を つきとめ、問題を解決するような習慣を最初からつけておくことを強く勧めま す。

以上を簡単にまとめると
1、正しい結果を与える標準となるべきプログラムを必ず用意するちうわけや。
2、取り敢えずデバッガーを使ってみるちうわけや。(アナライザもあれば役立つかも)
3、他に迷惑をかけへん範囲で途中結果を出力するちうわけや。(1、と比較)
4、計算条件(系のサイズや原子の種類やらなんやら)を変えてみるちうわけや。
5、コンパイラーのオプションを変えて実行(例、最適化のレベルを下げる)。
6、異なるシステム(OS、機種やらなんやら)同士で実行、比較してみるちうわけや。
7、デバッグ作業の履歴をきちんと記録しておく。
8、デバッグに伴うプログラムの変更箇所には必ず注釈をつけるちうわけや。
9、バグが見つかりまへん時は、いろいろな方法を試してみるちうわけや。
10、信頼できる人々(1人だけでなくてよい)に相談(議論)するちうわけや。
11、バグは大抵単純(時に見つけ難い)で深刻な影響を与えるものがほとんど。
12、先入観(思い込み)を棄てるちうわけや。(虚心坦懐に)
13、デバッグ作業を難儀がりまへん。(急がば回れ)
14、可能な限り妥協してはいけまへん。(結局問題を先送りするだけ。)
15、いよいよ煮詰まったら、ちーとの間ほうっておいてみるちうわけや。(気分転換)
16、末期段階、バグも仕様の内と開きなおる(ホンマは絶対やっちゃいけへん)。

やはり、16に関して、明らかに間違っとる計算結果をもってして、研究 を進めて(間違いを承知していながら、それを正しいデータと偽って研究発表 等を行なうこと)いくことは研究者の態度としては論外といえるものや。良 い研究者はこのようなことをしてはいけまへん。

(4)どないな時でもデバッグはエライであるちうわけや。
特に、バグの中で問題なのは、何が何だか分りまへんバグ(原因不明で止まり、 どうして止まるか分りまへん場合、系のサイズを大きくしたりと、ある条件にす ると原因不明で止まる場合やらなんやら)、正しい計算結果と比べて微妙(シカトできな い程度)に値が異なるバグ(これが一番厄介だとわいは思っていまんねん。)やらなんやらで す。

何がなんだか分りまへんバグの場合、先に述べた(イ)から(ホ)までの手段 や1から15までの事柄を踏まえて、とにかく原因を突き止めることや。む しろ途中で止まるバグや、計算結果がまるっきしでたらめ(大き過ぎたり、小さ過ぎ る)な場合は、ある程度以上の経験を積むと対処できるようになっていきまんねん。 それまでは、いろいろなデバッグ手段をためし、いろいろな条件設定の下での 計算結果の比較検討を行ない、わかりまへんことはマニュアルや参考文献を探し、 それでもわかりまへん時は、良く知っていそうな人(指導教官、所属研究室の助 手、先輩、同僚やらなんやらで頼りになりそうな人で、できればあまり忙しそうにして おらへん時)に質問するなり、助けを求めるなりして、経験を積んで貰うしかな いと思うで。

むしろ、微妙に値がちゃう場合は、原因がはっきりしていれば対処できまっけど、 大抵こういうような局面では原因がさっぱり分りまへんことが多いや。但し、 この手のバグは大抵単純(故に見つけ難い)ながら、計算結果に深刻な影響を 与えとる場合が多いや。これらの例をいくつか挙げると、

  1. 単純な係数の間違いちうわけや。(例4*を2*とした、や単位系の間違いやらなんやら)
  2. 計算すべき式が、もともと間違っとった。(論文の式は鵜飲みするな)
  3. (配列)変数等の初期化の間違いちうわけや。(例、ゼロにしたつもりでそうなっておらへん。)
  4. (配列)変数の定義の間違いちうわけや。(例、型の不一致)
  5. 配列変数で、定義した外側の領域にアクセスしようとしたちうわけや。
  6. サブルーチンへの(配列)変数の受け渡しの間違いちうわけや。(変数の数や型の不一致)
  7. (配列)変数名のスペルミス。
  8. 入力データの間違いちうわけや。
  9. 計算制御変数の間違いちうわけや。(例、時間刻み幅のミスや結晶対称性の間違い)
  10. その他
やらなんやらやらなんやらや。

特に、サブルーチンで受け渡す側と受け取る側の変数の数や型が合わなかっ たり、変数名のスペルミスはコンパイラーがアヤマチを出さない、出せへん場合 があるんや。また入力データの間違いや、計算条件の設定ミスもコンパイル時 にはアヤマチが出まへん。(サブルーチンの変数受け渡しの問題は、コンパイラー の種類によって、標準ではアヤマチの出ないものが多いやけどアンタ、大抵はオプショ ンでアヤマチを出せるようにはなっていまんねん。オプションについてはmanコマン ドか、マニュアルを参照しておくんなはれ。)

配列変数で、定義した外側の領域にアクセスしたちうのは、ゴチャゴチャゆうとる場合やあれへん,要は配列A がA(1000)と定義してあったとして、1001番目以降のAについて演 算、代入等の操作をしようとしたことを意味していまんねん。(動的なメモリーの 割付けが行なわれ、それに配列Aが対応するようにできていれば〔ホンマにでき るかは、いまのトコわいにはわかりまへん。〕問題ないかもしれまへん。)

この場合、コンパイルの時点ではアヤマチは出ない場合がほとんどや。大抵 は実行時に記憶領域を壊したとか、セグメントが壊れたとか、記憶領域障害と かエンドユーザーにはさっぱりわけの分りまへんアヤマチ文句が出て、プロ グラムが止まってしまいまんねん。これは、定義した以外の配列の領域を使用した ため、メモリー(記憶領域)上のデータがおかしくなってしまうためで、この 場合、アヤマチも配列の操作を間違えたトコとはまるっきし関係ないトコで止まっ てしまいまんねん。このため、アヤマチ箇所が見つけ難くなっていまんねん。

このアヤマチを見つけるためには、デバッガーを使うことが有効かもしれませ ん(コンパイル時にデバッグオプションが必要、またデバッガーの操作も熟知 しとることが必須)。またコンパイルオプション(詳細は各システムのマニュ アルを調べるか、そのシステム〔UNIXを想定〕上でman f77としてみるちうわけや。)で 実行時の配列領域違反しとる部分を指摘するようにさせることができるもの があるんや(大抵できると思うで)。ただ、この方法はこのような場合には まるっきし無力であることもようけ、どエライデバッグに苦労することがあるんや。

この逆の場合として、考えとった数より、ずと少ない数の配列しか使ってい ないバグもあるんや。”やさしいバンド計算プログラムの作り方”のトコで、 変数、配列定義用のインクルード文PACVPPの説明をしたかと思うで。 そこに、KNGとKNG1ちう変数が定義されていまんねん。この変数の大小関係は必 ずKNG > KNG1や(大体KNG=8*KNG1)。

よりどエライ昔、計算ルーチン内のDOループ変数で、KNGとすべきトコをKNG1とし て計算しとったことがおました。一文字ちゃうだけやけどアンタ、このバグでエライ苦 労したんや。KNG > KNG1で、関係する配列等はちゃんとKNGまで定義してあっ たさかい、記憶領域障害等のアヤマチもでず、計算そのものも何のアヤマチも示さず、 正常に終了しておったんや。せやけどダンさん、どうも計算結果が微妙に(表現がややこしい) おかしいと思おったんや。当初は計算機誤差やろうと鷹をくくっとったのやが、 どうも気になって、いろいろなテスト計算をしていく内に、何らかのバグと考 えるようになったんや。せやけどダンさん、このバグの追求は困難を極め、バグ取りを試 みて、疲れてほうっておいて、またバグ取りを試み、また疲れて他の仕事をす るちうのを繰り返して、何カ月もかかって、プログラムリストを何度目かの 点検をしとる時に、ふとDO文の制御変数が1からKNGではなく、KNG1になっ ていて、実際このループではKNGではおまへんかと気付いて、バグがようやっと取れ た経験があるんや。バグとしてはたった一文字の違いで、どエライ単純やったが、 どエライデバッグに苦労した例や。

また並列化作業の上で犯したバグの が”FACOM VPP300、500での並列化”編にあるんや。

[アタマ][総目次 ][最初に戻る][FACOM VPP300、500での並列化 ][きょうびやらかしたバグ、シッパイ ]
[原文]