アプリ版:「スタンプのみでお礼する」機能のリリースについて

 【VC++ 高速演算コードの沢山の変数を、どのように宣言すべきでしょうか?】

 今日は、質問させていただきます。 もしお詳しい方がいらっしゃいましたら、
どうぞよろしくお願いいたします。

 Fortranのコード(歴10年程度)を、VC++2010(歴3ヶ月程度)になおそうとしております。
 ボリュームのある数値解析のコード(3000行程度ございます)を、見やすくするために、
数百行ずつでサブルーチン化したい次第でございます。
   例)int main(){
               ←変数の宣言場所
        definition()
        calculation1()
        calculation2()
        calculation3()
        conclusion()
     }

 しかしmainの最初(上記「変数の宣言場所」)にまとめて宣言しております変数が200個以上ございまして、これらの半分近くを各々のサブルーチンに毎回渡そうといたしますと、引数の量が膨大になってしまいますし、
またサブルーチン内で何か追記する度に引数を追加・削除せねばなりませんので、出来れば避けたい次第でございます。
 そこで「全てPublicで宣言してしまおうかな?」などと考えて検索しておりましたが、「できる限りPublic変数を宣言すべきでない」といったような記事もございましたので、悩んでおります。
(Fortran環境下では、ヘッダファイル内のPublic変数を、全サブルーチンで毎回呼び出しておりましたが、それも良くないのかな、と迷っております。)

 あと、上記のような宣言方法や参照方法が計算速度に影響するものなのかどうか分からないのでございますが、
平均で10日間程度まわし続けるプログラムになりますので、出来る限り「高速化」を優先したい次第でございます。


 変なご質問かもしれませんので大変恐縮でございますが、
是非アドバイスいただけないでしょうか。(書物やサイトをご紹介いただけるのでも結構でございます)

 何卒よろしくお願いいたします。

A 回答 (5件)

C++ なので、単純に考えるならクラス化でしょうか



大量の変数はクラス内の private とし、
変数を操作する関数はクラス内の public とし、
main にてクラスのインスタンス生成/操作/削除を行います。

これにより以下が期待できます。
* 変数を隠蔽するカプセル化により、Public変数宣言でありがちな問題を回避できる
* 同一インスタンスならば、関数はすべての変数を引数や宣言無しに参照・更新できる
* クラス内の変数処理は機械語レベルで最適化されるので、高速化にも有利

以下サンプルです

class ボリュームのある数値解析 {
private:
 int alpha; // 変数の宣言
 int beta;
 ...
 int omega;
public:
 void definition(); // 関数の宣言
 void calculation();
 void conclusion();
}

void ボリュームのある数値解析::definition(){
 alpha = 1, beta = 2, ...; // クラス内のすべての変数が参照・更新可能
}
void ボリュームのある数値解析::calculation(){
 omega = alpha + beta + ...;
}
void ボリュームのある数値解析::conclusion(){
 出力(omega);
}

int main(){
 ボリュームのある数値解析 *obj;
 obj = new ボリュームのある数値解析(); // 生成

 obj->definition(); // 操作
 obj->calculation();
 obj->conclusion();

 delete obj; // 削除
}
    • good
    • 0
この回答へのお礼

 Ogre7077様

 どうもありがとうございます!!
サンプルを書いていただいたお陰で非常に分かりやすかったです。m(_ _)m
>同一インスタンスならば、関数はすべての変数を引数や宣言無しに参照・更新できる
>クラス内の変数処理は機械語レベルで最適化されるので、高速化にも有利
 私では思いつかない内容でしたので、非常に興味がございます。^^
まずNo.1様からアドバイスいただいたようにコードを整理&見直してから、
更にOgre7077様にご提示いただいた内容に変更する事にいたします。
(時間がかかりそうでございますが。。。^^;)

 ご親切にご閲覧・アドバイスいただきまして誠にありがとうございました!!!!m(_ _)m

お礼日時:2014/05/31 13:04

FORTRN、VC++共に今後もサポートされ続けられると思われますし、高速化の点ではFORTRANの方が充実したパッケージ等が揃っておりコンパイラもFORTRANの性能の方が上のようですし、主要部分はこれまで通りFORTRANのままでも良いのではないでしょうか。



入出力連携やディスプレイ表示(グラフ、平面、立体、アニメ、動画...)などの点でVC++が便利なのでしたら、mainはFORTRANのままで入出力部分等をVC++で処理する事も可能だと思われます。

"FORTRAN C++ 連携 OR インターフェイス"
"FORTRAN C++ 連携 OR インターフェイス common"
等とサーチされたら色々な例が見つかります。

common blockを宣言し、FORTRAN, VC++の両プログラムから沢山の変数は共通にアクセス出来るのではないでしょうか。
FORTRAN内で使われていた沢山の変数は、宣言部分のみを書換えれば実行部分では殆どそのままでアクセス出来るのではと思われます。
それからご存じとは思いますが、二次元や三次元のデータアクセス等に当ってはFORTRANとVC++ではabc(i,j,k)をどの順番で(i,j,kのどれを一番内側のループにして)早く変化させるか、メモリ上でどの順番にデータが並んでいるか<==>キャッシュラインヒット率を上げるか等で、単純な書換えでは性能が反って悪化する場合もあるのではと思われます。

既に調べられているとは思いますが、次等いろいろと検討が必要のようです。

http://www.moonmile.net/blog/archives/3359
FortranのCOMMONブロックをC++から扱う

http://www.xlsoft.com/jp/products/intel/compiler …
インテル@Fortran プログラムからの C プロシージャーの呼び出し

http://docs.oracle.com/cd/E19957-01/806-4843/Cp1 …
C と Fortran のインタフェース

システム全体の事が解らずにピント外れの回答でしたら申しわけありません。
    • good
    • 0
この回答へのお礼

ninoue 様

 どうもありがとうございます!!
>システム全体の事が解らずにピント外れの回答でしたら申しわけありません。
 いえ非常に貴重なご意見でございます。ご親切にどうもありがとうございます。m(_ _)m
>入出力連携やディスプレイ表示(グラフ、平面、立体、アニメ、動画...)などの点でVC++が便利なのでしたら、mainはFORTRANのままで入出力部分等をVC++で処理する事も可能だと思われます
 はい、計算結果を一定の時間ステップ毎に可視化する必要がございました為、VS上でそのようなソフトを作ってみたかった次第でございます。
>common blockを宣言し、FORTRAN, VC++の両プログラムから沢山の変数は共通にアクセス出来るのではないでしょうか。
 なんとそのような事が可能なのでございましたか(・o・)存じませんでした。
となりますと、こちらも選択肢に入りますね。迷います^^
 自分で検索&勉強させていただきます!!この度はご親切に誠にありがとうございました!!!m(_ _)m

お礼日時:2014/05/31 13:05

高速化に関してだけですが、



経験上、FORTRANが一番速いコードになることが多く、Cが注意深く書くとFORTRANと同程度、C++ではC++らしく書くとそれなりに遅くなります。

コードによって最適化が全然効かなかったりします。コンパイラの最適化性能にもよりますが、今時のコンパイラはどれでもそれなりに最適化してくれますので、基本的にはコード次第です。

>上記のような宣言方法や参照方法が計算速度に影響するものなのかどうか分からないのでございますが

関数をインライン展開すればほとんど影響しないと思います。それも時間がかかる多重ループの中だけ考慮すれば十分です。FORTRANは明示的インライン展開する機能はないはずですが、コンパイラのオプションに大概ありますのでそれを利用します。

高速化の基本は、プロファイラで時間のかかる部分を特定して、そこを中心に対策することです。数値計算の場合は僅かな部分がほとんどの計算時間を消費してる場合がほとんどです。そこをベクトル化や並列化ができるコードにすればかなり速くなります。(時間のかかる部分は多重ループになっていること多く一般的にベクトル化や並列化しやすい)。
それ以外のところはどのような(非効率?な)書き方をしてもほとんど影響しないということです。

ベクトル化や並列化ができれば、それをGPUで実行させるとさらに高速化が期待できます。

>平均で10日間程度まわし続けるプログラム
最適化のやりがいがありますね。GPUでうまくいけば一桁程度は速くなります。
    • good
    • 0
この回答へのお礼

 ki073様

 ご親切にどうもありがとうございます!!
>経験上、FORTRANが一番速いコードになることが多く、Cが注意深く書くとFORTRANと同程度、C++ではC++らしく書くとそれなりに遅くなります。
Cを注意深く書くスキルを身につけるよう頑張ります(`_´)ゞ
GPUも今勉強しております^^
 プログラミングについては過去、誰かに教わったという経験がございませんで、初めからぶっつけでサンプルコードを引用したり、
自分で検証をひたすら繰り返してまいりました。そのため、今回いただけたようなアドバイスは非常に貴重であり、勉強になります。
 どうもありがとうございました!!!m(_ _)m

お礼日時:2014/05/31 13:04

#1さんの言うことはもっとですが,とにかく


> Fortranのコード(歴10年程度)を、VC++2010(歴3ヶ月程度)になおそうとしております。
ということであれば,プログラム構造をいじることなく,そのまま変換することを目指すほうがよいように思う。
そして,誤りなく変換されて動作が安定したら,それからモジュール分割とかいろいろと考えればいいのではないだろうか?
高速化を考えているのならfortranのままのほうがいいかもしれない。とにかく高速化のネックになっているところを特定することからはじめるべきでしょう。
    • good
    • 0
この回答へのお礼

 f272様
 どうもありがとうございます!!
>高速化を考えているのならfortranのままのほうがいいかもしれない。
 はい、説明が不足しておりまして大変失礼いたしました。定期的に画像出力するなどVSを使う必要が出てまいりました為、
(VisualStudio上でのコーディングが好きだという理由もあるのでございますが^^;)、VC++を選択したくなった次第でございます。
高速化のネックにつきましても見直してまいります!!(`_´)ゞ
 どうもありがとうございました!!!m(_ _)m

お礼日時:2014/05/31 13:02

基本的には、「各所で使い回すような変数が200個以上あるようなプログラムを分割するのはやめた方がいいです」ということになります。



あと、「行数が多いので、わかりやすくするために、一定の行数で分割する」という方針も、正しくないことの方が多いです。

分割は、「処理」とか「機能」の単位で行うべきかと思います。

という基本を押さえておいた上で。

まず、Publicな変数を多用すべきでないというのは、本来、「どこでその変数をいじっているかわららなくなるから、ある変数がおかしくなったときに、どこでおかしなことをしているのか、追いかけるのが大変だから」です。

そういう意味では、200個の変数があって、その多くが(半数近くが)あちこちで使い回されるということは、十分、「どこでその変数をいじっているのかわからない」状態です。

なので、この状態で、「Public をやめて、すべて引数で」と力んでみても、あまり効果はないです。

さらに、

> またサブルーチン内で何か追記する度に引数を追加・削除せねばなりませんので、
> 出来れば避けたい次第でございます。

というのも、本来は、あるはずのないことです。


モジュール分割には、「強度」と「結合度」という概念があります。
http://www.kogures.com/hitoshi/webtext/kj2-module/

モジュールを分割しているのに、たくさんの引数を渡す必要があるというのは、本来間違いです。
その関数の中でしか使わない情報を、関数の外に出さなくてもいいように、モジュールの分割をするのが正しいです。
普通は、main で大量の変数を宣言するというのは、間違った設計を示す兆候です。

その点を、考えてみるといいかと思います。



ただ、計算内容によっては、本当にたくさんの変数があちこちで必要になることもあるようです。
その場合、大量の変数が、すべて無関係に存在しているということはないと思います。

ある程度のまとまりのある変数は、「構造体」を使ってひとまとめにすると(そして、その構造体を引数に使うと)見かけ上、引数を少なくすることもできます。

また、データの持ち方を変更する場合でも、構造体レベルで変更すれば、関数の見かけは変わらないことが多いです(きちんと、機能的な分割ができている場合)
    • good
    • 0
この回答へのお礼

 asano_nagi 様

 どうもありがとうございます!m(_ _)m
ひと通り試しておりご返信が遅くなりました。大変失礼いたしました。
 すべてじっくり読ませていただきましたが、全て「なるほど」でございます。
>モジュール分割には、「強度」と「結合度」という概念があります。
存じませんでした。^^; あと自分のコードを見直しましたが、殆どの変数がどこでも書き換えられる事が出来るようになっております。
今回いただいたアドバイスを活かせるように
まず、分割してあるサブルーチン群を全てメインに戻してみます。
→(Fortranのコードと計算結果を比較し一致することを確認いたします。)
→「処理」とか「機能」の単位で行える部分のみ分割し、
→>関数の中でしか使わない情報を、関数の外に出さなくてもいいように、モジュールの分割をする
を行ってみます。

 この度はご親切に誠にありがとうございました!!m(_ _)m

お礼日時:2014/05/31 13:01

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!