リストHOME  リストOpen Source

第13回VBAプログラム豆知識

概要 ~ ちょっとした高速化その2 ~

 今回は、前回に引き続き、VBAプログラムの高速化について、お話したいと思います。
内容は主に、以下の2点について、お話ししたいと思います。



 コンパイル 

 以前、別のお話で、コンパイルについて、簡単に説明したと思いますが、復習もかねて、再度説明したいと思います。コンパイルとは、『人間が記述したプログラム文(VBAやC言語)を、コンピュータが理解できる実行可能なマシン語に変換する処理』のことです。


 VBAのコンパイラは少し特殊で、インタープリタ型のコンパイラと呼ばれています。『インタープリタ型って?』と、なると思うのですが、ここでいう、インタープリターとは、言葉通り、翻訳の意味で使用されています。プログラムを実行時に、プログラム文を翻訳し、実行可能なマシン語に変換しつつ、同時に実行も行うコンパイラのことです。


 これに対して、C言語などの他のプログラミング言語のコンパイラは、先に全てのプログラム文を、コンパイルして、実行可能なマシン語のデータに変換してから、実行します。ですので、インタープリタ型の方が、翻訳処理を伴うため、どうしても実行時の処理速度が遅くなってしまいます。


 エクセルVBAの場合、プログラムの作成や変更後、1回目は、この翻訳を伴うコンパイル実行となり、以降、プログラムが変更されるまでは、1回目のコンパイル情報が保持され、2回目以降は、それを利用した実行となります。ですので、1回目の実行速度より、2回目以降の実行速度は、速くなります。


 この翻訳されたコンパイル情報は、エクセルを保存することで、一緒に保存される仕様のようで、それにより次回、起動時も、コンパイル済みの状態で、VBAプログラムを実行することが可能になります。


 以上が、VBAのコンパイルについてなのですが、実は、VBAには、このコンパイルのみを先に実行する機能があります。これを利用すると1回目の実行から、コンパイル済みの状態で実行することが出来ます。また、このコンパイルを実行すると、VBAプログラムの文法チェックにもなりますので、プログラム作成が一段楽したところで、コンパイルを実行することをお勧めします。


 コンパイル方法 

 メニューの【デバッグ】-【VBAProcjetのコンパイル】を選択することで、プログラムの実行前に、コンパイルすることが出来ます。

VBAプログラムのコンパイル


 関数呼び出しについて 

 関数呼び出しのフローについて、以下のプログラムを使って簡単に説明します。このプログラムは、単純にFunction関数から、CalledFunction関数を呼び出す内容となっています。



'呼び出される関数
Private Function CalledFunction(ByVal a as Long,ByVal b as String) as Long
    Dim i as Long

    For i=1 To 100
        Debug.Print b

    Next i
    
End Function

'サンプル関数
Public Sub Function()
	
	'関数の呼び出し
	CalledFunction 100,"Hello!"
	
End Sub



 関数の呼び出し時の、おおまかな処理のフローを纏めると以下のようになります。


  1. 関数を呼び出すときには、まず、関数で使用される引数(a,b)を、右側の引数からb、aの順に、スタック上に積みます。
  2. また、プログラム上では、明記されていませんが、関数の呼び出し直前には、関数の戻り先のアドレスも、スタック上に積まれます。
  3. プログラムは、関数の呼び出しに伴い、CalledFunction関数が配置されているアドレスにジャンプします。
  4. CalledFunction関数の処理が終了すると、スタックに積まれた、引数a、bをスタックから除外(解放)します。このとき、スタックはLIFO構造(後入れ先出し)なので、呼び出し前に、積んだb,aの順ではなく、a,bの順でスタックから除外されます。
  5. 関数の戻り先にジャンプします。


 上記のフローを、処理に着目して纏めて書き直すと、以下のようになります。



 これにより、関数を呼び出すと、それに伴い、少なからず呼び出しに必要な処理が発生することが分かります。処理が発生するということは、その分、時間がかかります。極端なことを言うと『関数を呼び出すプログラムよりも、関数を呼び出さないプログラムの方が速い』ということです。


 関数を使わなければ、速いプログラムが書けるということになるのですが、そうなると関数の呼び出しの代わりに、同じ処理を何箇所も記述しなければいけません。ですので、修正が発生した場合には、該当する全ての箇所を変更しなければいけません。それは、生産性も、メンテナンス性も非常に悪くなることを意味しています。


 パフォーマンスが求められる場面(ループ回数の多い、繰り返し処理etc.)では、関数を使用しないということも、一つの改善手段として、考えてみても良いと思いますが、先に説明したように、マイナスの側面も非常に大きくなります。状況に応じて、優先度を考え、実装しましょう。


 『プログラムって、意外と冗長的にダラダラ、上から下に流れように書いたほうが、速いんですよね。』


 ちなみに、C言語では、以下のように関数宣言の先頭に、inline修飾詞を付与すると、その関数を呼び出した位置に、関数呼び出しではなく、関数内の処理がインライン展開されてコンパイルされます。VBAにも欲しいところです。



//C言語
//inline修飾詞をつけた関数
inline long SampleFunction(long a)
{
    //ここに書かれたコードが呼び出しもとの位置に
    //インライン展開されます。

}



第12回  <  >  第14回



所感

 関数呼び出しの際に、引数をスタックに積んだり、解放したりするのですが、VBAでは、この辺りを明示的に、プログラミングすることはありません。アセンブリ言語などの低水準のプログラミング言語では、push命令、pop命令などが用意されており、明示的に、自分でスタックの積み下ろしを、コーディングしてやらなければなりません。この辺りを知っていると、関数呼び出しに、少なからず時間がかかるということにも、気付き易いのですが…


 アセンブリ言語自体、触れる機会がほとんどないと思いますし、とっつきにくい言語ですので、それを知らなくてもいいように、と思って、今回のテーマに取り上げました。

※アセンブリ言語は、CPUの命令を直接呼びだし、その単純な命令の組合せにより、プログラミング実装する言語です。


連絡先

 ご意見・ご要望等ありましたら、画面最下部のメールアドレスまでご連絡ください。




エクセルバックアップ・ページのフッター
管理者のメールアドレス