[リストへもどる]
一括表示

投稿時間:2003/03/28(Fri) 11:42
投稿者名:ひろぽん
Eメール:
URL :
タイトル:
クラスは遅い?
ファイルを読み込んで、変数に格納するプロシージャがあるのですが、
変数があまりにも多く、以外と時間もかかるため、変数の定義方法を変えて
高速格納化を目指してます。

今MDI環境で作成してますが、変数は全てクラスで定義していて、2次元
および3次元の配列はクラスではうまく表現できないため、それらの配列変数は
コレクションを用いてます。
昔、SDIで作成したときは、対象のプロシージャを含むモジュールの先頭部分で
Public変数(表現は悪いですが、クラスを用いずという意味)として定義してました。
この両者、つまり、「クラス(MDI)」と「Public変数(SDI)」で読込み
時間(格納時間)を比較した結果、後者の「Public変数」の方が3〜4倍程度
早い事が確認できてます。

(質問1)
遅くなった原因ですが、これはクラスを用いているせいでしょうか。それとも
MDIだからでしょうか。それともコレクションを使ったせいでしょうか。
分かるようでしたら、是非教えてください。

以上のような事から「クラス(MDI)」→「Public変数(MDI)」に変更しようか
どうか非常に迷ってます。せっかく、MDIにするためにクラスを導入したのに、
また「Public変数」に戻るのもしゃくですが、高速化が最大目標だから、
そうしたいと思ってます。

(質問2)
また「Public変数(MDI)」にする場合には、「Public変数(SDI)」で使っていた
変数を、子フォーム毎の変数を識別するため、下記の例のように配列化しなければ
ならないと思うのですが、本手法は一般的でしょうか?
     Dim X As Long
           ↓
     Dim X() As Long
          …
     WindowNo = WindowNo + 1  '子フォームを開く毎に加算
     Redim X(WindowNo) As Long

以上の件、よろしくご教授下さい。

投稿時間:2003/03/28(Fri) 12:45
投稿者名:Lantern
Eメール:
URL :
タイトル:
Re: クラスは遅い?
コレクションを使用していると遅いです。
また、クラスの参照を頻繁に行うコードだと遅いです。

簡単なクラスの使い方例です。参考になればと思います。

'MDI等
Private clsChild() As Class1
Private Sub Task1()

    ReDim clsChild(2) As Class1
    
    Set clsChild(0) = New Class1
    Set clsChild(1) = New Class1
    Set clsChild(2) = New Class1
    
    Call LongTask(clsChild(0))
    Call LongTask(clsChild(1))
    Call LongTask(clsChild(2))
    
End Sub
Private Sub LongTask(ByRef cChild As Class1)

    Dim udtChild As typChild
    
    'udtChildにファイル読み込み
    
    cChild.uChild = udtChild

End Sub

'クラス
Option Explicit
Private udtChild As typChild
Friend Property Let uChild(ByRef newVal As typChild)
    udtChild = newVal
End Property
Friend Property Get uChild() As typChild
    uChild = udtChild
End Property
Public Property Let ChildA(ByVal newVal As Integer)
    udtChild.uChildA% = newVal%
End Property
Public Property Get ChildA() As Integer
    ChildA = udtChild.uChildA%
End Property
Public Property Let ChildB(ByVal newVal As Long)
    udtChild.uChildB& = newVal&
End Property
Public Property Get ChildB() As Long
    ChildB = udtChild.uChildB&
End Property
Public Property Let ChildC(ByRef newVal() As Double)
    udtChild.uCHildC#() = newVal#()
End Property
Public Property Get ChildC() As Double()
    ChildC = udtChild.uCHildC#()
End Property

'モジュール
Public Type typChild
    uChildA As Integer
    uChildB As Long
    uCHildC() As Double
End Type

投稿時間:2003/03/29(Sat) 14:59
投稿者名:ひろぽん
Eメール:
URL :
タイトル:
Re^4: クラスは遅い?
> コレクションを使用していると遅いです。
> また、クラスの参照を頻繁に行うコードだと遅いです。
>
> 簡単なクラスの使い方例です。参考になればと思います。
>
> 'MDI等
> Private clsChild() As Class1
> Private Sub Task1()
>
>     ReDim clsChild(2) As Class1
>    
>     Set clsChild(0) = New Class1
>     Set clsChild(1) = New Class1
>     Set clsChild(2) = New Class1
>    
>     Call LongTask(clsChild(0))
>     Call LongTask(clsChild(1))
>     Call LongTask(clsChild(2))
>    
> End Sub
> Private Sub LongTask(ByRef cChild As Class1)
>
>     Dim udtChild As typChild
>    
>     'udtChildにファイル読み込み
>    
>     cChild.uChild = udtChild
>
> End Sub
>
> 'クラス
> Option Explicit
> Private udtChild As typChild
> Friend Property Let uChild(ByRef newVal As typChild)
>     udtChild = newVal
> End Property
> Friend Property Get uChild() As typChild
>     uChild = udtChild
> End Property
> Public Property Let ChildA(ByVal newVal As Integer)
>     udtChild.uChildA% = newVal%
> End Property
> Public Property Get ChildA() As Integer
>     ChildA = udtChild.uChildA%
> End Property
> Public Property Let ChildB(ByVal newVal As Long)
>     udtChild.uChildB& = newVal&
> End Property
> Public Property Get ChildB() As Long
>     ChildB = udtChild.uChildB&
> End Property
> Public Property Let ChildC(ByRef newVal() As Double)
>     udtChild.uCHildC#() = newVal#()
> End Property
> Public Property Get ChildC() As Double()
>     ChildC = udtChild.uCHildC#()
> End Property
>
> 'モジュール
> Public Type typChild
>     uChildA As Integer
>     uChildB As Long
>     uCHildC() As Double
> End Type

ありがとうございます。
上記コードを試してみたのですが、uChildC()の使い方が分かりません。
今、上記コードの
'udtChildにファイル読み込み
の部分に、
    udtChild.uChildA = 5
    udtChild.uChildB = 10
    udtChild.uCHildC(0) = 15#
と書いてみると、「udtChild.uChildC(0) = 15#」の部分で、
「実行時エラー9、インデックスが有効範囲にありません」
とエラーが出ます。
私には原因が分かりませんでした。
なぜエラーが起こるか教えてください。
よろしくお願いします。

投稿時間:2003/03/30(Sun) 12:49
投稿者名:Lantern
Eメール:
URL :
タイトル:
Re^5: クラスは遅い?
#配列ですからReDimしないとエラーになります

'MDIフォーム
> Private Sub LongTask(ByRef cChild As Class1)
>
>     Dim udtChild As typChild
>    
>     'udtChildにファイル読み込み
>    
>     cChild.uChild = udtChild
>
> End Sub
Private Sub LongTask(ByRef cChild As Class1)

    Dim udtChild As typChild
    Dim d() As Double
    
    With udtChild
        ReDim .uCHildC(10, 10) As Double
        .uChildA = 5
        .uChildB = 10&
        .uChildC#(0, 0) = 15#
    End With
    
    cChild.uChild = udtChild

    DeBug.Print cChild.ChildA
    DeBug.Print cChild.ChildB
    d#() = cChild.ChildC
    DeBug.Print d#(0, 0)

End Sub

#子フォームに入れたい変数を親フォームでファイルから読み込み
#プロパティを使用して子フォームに格納するという作り方
#
#子フォームに入れる変数をユーザー定義型にまとめて
#Friend属性のプロパティで渡します

投稿時間:2003/03/31(Mon) 09:11
投稿者名:ひろぽん
Eメール:
URL :
タイトル:
ありがとうございます。
> #配列ですからReDimしないとエラーになります
>
> 'MDIフォーム
> > Private Sub LongTask(ByRef cChild As Class1)
> >
> >     Dim udtChild As typChild
> >    
> >     'udtChildにファイル読み込み
> >    
> >     cChild.uChild = udtChild
> >
> > End Sub
> Private Sub LongTask(ByRef cChild As Class1)
>
>     Dim udtChild As typChild
>     Dim d() As Double
>    
>     With udtChild
>         ReDim .uCHildC(10, 10) As Double
>         .uChildA = 5
>         .uChildB = 10&
>         .uChildC#(0, 0) = 15#
>     End With
>    
>     cChild.uChild = udtChild
>
>     DeBug.Print cChild.ChildA
>     DeBug.Print cChild.ChildB
>     d#() = cChild.ChildC
>     DeBug.Print d#(0, 0)
>
> End Sub
>
> #子フォームに入れたい変数を親フォームでファイルから読み込み
> #プロパティを使用して子フォームに格納するという作り方
> #
> #子フォームに入れる変数をユーザー定義型にまとめて
> #Friend属性のプロパティで渡します


ありがとうございます。
おかげさまで、多次元配列の定義のやり方を、コレクションから完全なクラスに変更する事が
可能となり、また子フォーム毎にファイルからの読み取り変数を持たせる事ができるようになりました。
"Friend"は初めて見るものだったので、ちょっととまどいましたが、なんとなく分かりました。
要は、クラス間での受け渡しを可能とするスコープみたいなものですね。勉強になりました。

投稿時間:2003/03/28(Fri) 12:55
投稿者名:Say
Eメール:
URL :
タイトル:
Re: クラスは遅い?
>> (質問1)
> 遅くなった原因ですが、これはクラスを用いているせいでしょうか。それとも
> MDIだからでしょうか。それともコレクションを使ったせいでしょうか。
> 分かるようでしたら、是非教えてください。

低速化の主たる要因が何かは解析しないでわかることではありません。
高速化が最優先課題なら、より速いハードを使うのが一番手っ取り早いかと・・・。
ソフトの範囲で、ということなら、可能ならVC++あたりに移植すれば、たいてい速くなります。
コーディングやロジックの範囲で、ということなら、まずループの中を洗ってみては?

> (質問2)
> また「Public変数(MDI)」にする場合には、「Public変数(SDI)」で使っていた
> 変数を、子フォーム毎の変数を識別するため、下記の例のように配列化しなければ
> ならないと思うのですが、本手法は一般的でしょうか?
>      Dim X As Long
>            ↓
>      Dim X() As Long
>           …
>      WindowNo = WindowNo + 1  '子フォームを開く毎に加算
>      Redim X(WindowNo) As Long
少なくとも、私は見たことありません。
素直に
'Form1
Public X As Long

'Form2
Public X As Long


Form1.X = 10
Form2.X = 20
とかではまずいのですか?

投稿時間:2003/03/28(Fri) 14:33
投稿者名:ひろぽん
Eメール:
URL :
タイトル:
Re^2: クラスは遅い?
> >> (質問1)
> 低速化の主たる要因が何かは解析しないでわかることではありません。
> 高速化が最優先課題なら、より速いハードを使うのが一番手っ取り早いかと・・・。
> ソフトの範囲で、ということなら、可能ならVC++あたりに移植すれば、たいてい速くなります。
> コーディングやロジックの範囲で、ということなら、まずループの中を洗ってみては?

他の方からのご回答で、どうもコレクションが癌みたいなので、配列にしてみようと思います。

> > (質問2)
> 少なくとも、私は見たことありません。
> 素直に
> 'Form1
> Public X As Long
>
> 'Form2
> Public X As Long
>
> …
> Form1.X = 10
> Form2.X = 20
> とかではまずいのですか?

子フォームに、例えば
  Public X As Long
としておいて、
親フォームから、複数の子フォームの中のある子フォームのXを参照した場合は、
どうすればいいでしょうか?

投稿時間:2003/03/28(Fri) 13:09
投稿者名:よねKEN
Eメール:
URL :http://www5b.biglobe.ne.jp/~yone-ken/
タイトル:
Re: クラスは遅い?
> ファイルを読み込んで、変数に格納するプロシージャがあるのですが、
> 変数があまりにも多く、以外と時間もかかるため、変数の定義方法を変えて
> 高速格納化を目指してます。

その部分のコードを提示して下さい。
また、どんなデータを読み込んで、どんなマシン環境で、
どの程度の時間がかかっているのでしょうか?

> 今MDI環境で作成してますが、変数は全てクラスで定義していて、2次元
> および3次元の配列はクラスではうまく表現できないため、それらの配列変数は
> コレクションを用いてます。

クラス内部で保持するデータ構造と、外部からそれにアクセスするインタフェースとは
まったく別なので、内部のデータ構造にコレクションを用いるか、配列を用いるか?は、
クラスを利用する側のプログラムには関係ありません。
#というか利用する側になるべく影響がでないようにクラスを設計しないといけません。

Private 内部_3次元配列(100,100,100) As ほげほげ

Public Property Get 外部_3次元配列(ByVal x As Long, ByVal y As Long, ByVal z As Long) As ほげほげ
      外部_3次元配列 = 内部_3次元配列(x,y,z)
End Property

Public Property Let 外部_3次元配列(ByVal x As Long, ByVal y As Long, ByVal z As Long, ByVal value As ほげほげ)
      内部_3次元配列(x,y,z) = value
End Property

のようにすれば普通に3次元配列を使えますが、こういうことではないのでしょうか?

> 昔、SDIで作成したときは、対象のプロシージャを含むモジュールの先頭部分で
> Public変数(表現は悪いですが、クラスを用いずという意味)として定義してました。

どういうやり方のことを指しているのかよくわかりません。

> (質問1)
> 遅くなった原因ですが、これはクラスを用いているせいでしょうか。それとも
> MDIだからでしょうか。それともコレクションを使ったせいでしょうか。
> 分かるようでしたら、是非教えてください。

クラスの作り方、使い方に問題がありそうな気もしますが、
配列を使うべきところでコレクションを使っているのであれば、
遅くなっているのはおそらくコレクションにデータを設定しているところでしょう。

コレクションは便利ですが、あまり大量データを扱うのには向いていません。
コレクションは配列に比べてデータの設定が極めて遅いので、
データ量が多くなると使い物にならなくなります。
大量データを高速に扱う必要がある場合は、配列を使うべきです。

後、クラスからインスタンスを生成するのも非常に時間のかかる処理です。
無駄なインスタンスの生成を控えるようにした方がいいでしょう。

> 以上のような事から「クラス(MDI)」→「Public変数(MDI)」に変更しようか
> どうか非常に迷ってます。せっかく、MDIにするためにクラスを導入したのに、

クラスとPublic変数は比較対照となるようなものではありません。
クラスはデータ型ですし、Publicは変数のスコープで、まったく別のものです。
根本の部分で、クラスとは何か?どう作ってどう使うべきか?を
誤解されているように思えます。
オブジェクト指向とは?というあたりからの勉強をお勧めします。

投稿時間:2003/03/28(Fri) 14:28
投稿者名:ひろぽん
Eメール:
URL :
タイトル:
Re^2: クラスは遅い?
> その部分のコードを提示して下さい。
> また、どんなデータを読み込んで、どんなマシン環境で、
> どの程度の時間がかかっているのでしょうか?

コードはちょっと長いので、抜粋した形で載せます。マシンはPentium4 1.5GHzで
8.3MBのファイルを読み込んで下記コードのように変数に格納していった場合で15,6秒程度です。
以前作成したクラスを使っていないSDIでは、5秒くらいでした。
変数は、全部で50個程度で、そのうち1次元配列は10個、2次元配列(コレクションで定義)は13個、
3次元配列は3個でした。

'子Formでクラスを定義
  '普通の変数の定義例
    Private m_NumNode As Integer

    Public Property Get NumNode() As Integer
        NumNode = m_NumNode
    End Property

    Public Property Let NumNode(ByVal NewValue As Integer)
        m_NumNode = NewValue
    End Property

  'コレクションを使った2次元配列定義例
    Private Function MakeKey_2D(ByVal pintIndex1 As Integer, _
                                ByVal pintIndex2 As Integer) As String
        MakeKey_2D = CStr(pintIndex1) & "-" & CStr(pintIndex2)
    End Function

    Public Property Get Lsys(ByVal pintIndex As Integer) As Integer
        If (UBound(m_Lsys) < pintIndex) Or (LBound(m_Lsys) > pintIndex) Then
            Lsys = ""
        Else
            Lsys = m_Lsys(pintIndex)
        End If
    End Property

    Public Property Let Lsys(ByVal pintIndex As Integer, ByVal NewValue As Integer)
        If (UBound(m_Lsys) < pintIndex) Then
            ReDim Preserve m_Lsys(pintIndex)
        End If
        m_Lsys(pintIndex) = NewValue
    End Property

'親Formでのファイル読込みルーチン
Private Sub Read_File()
    Dim StrBuffer As String
    Dim Fso       As New FileSystemObject
    Dim FsoTS     As TextStream

    Set FsoTS = Fso.OpenTextFile(RiplsOutFile, ForAppending)

    With FrmChild '子フレームに定義したクラスを使う

    Do Until FsoTS.AtEndOfStream
        StrBuffer = FsoTS.ReadLine
        For i = 1 To 10
            .Lsys(i) = Val(Mid(StrBuffer, 4 * i - 3, 4))
        Next i
            …
        If (.Lsys(3) > 0) Then
            FsoTS.SkipLine
            StrBuffer = FsoTS.ReadLine
            .NumNode = Val(Mid(StrBuffer, 8, 7))
        End If
             …
        For i = 1 To .NumNode
            StrBuffer = FsoTS.ReadLine
            .StnRate(i, .StepNo) = Val(Mid(StrBuffer, 11, 12))
            .StsXY(i, .StepNo) = Val(Mid(StrBuffer, 59, 12))
        Next i
             …
    Loop
    
    End With
        
> Private 内部_3次元配列(100,100,100) As ほげほげ
        …
> のようにすれば普通に3次元配列を使えますが、こういうことではないのでしょうか?

実は、2次元,3次元配列は、Step数(上記コードではDo Loopの数、ファイル毎に違う)や、
節点数(Step毎に違う)や、要素数(これもStep毎に違う)で定義される配列であるため、
動的配列で定義しています。
大きめに初期配列数を取れば、ご助言のとおりの方法でいけると思うのですが、動的配列により
メモリを無駄遣いしないようにと思ってしてしまいました。
とは言いましても、それぞれMin=10,Max=2000程度なので、X(2000,2000,2000)として、
コレクションを使わずに上記方法でクラスで定義した方が賢明なのかもしれません。

> どういうやり方のことを指しているのかよくわかりません。

ファイルを読み込むためのコマンドボタンのあるFormのGeneralの所に、
Option Explicit
Public NumDie As Integer
        …
Public StsXY() As Double
という風に定義しているという意味でした。

> コレクションは便利ですが、あまり大量データを扱うのには向いていません。
        …
> 大量データを高速に扱う必要がある場合は、配列を使うべきです。

上記のように、メモリの無駄はある程度覚悟して、コレクションから配列に変更してみます。
速度の方が優先ですから。

> オブジェクト指向とは?というあたりからの勉強をお勧めします。

たしかに、クラスは始めて2,3週間なので、人のコードをまねしながらしてるだけでした。
一から勉強してみたいと思います。

投稿時間:2003/03/28(Fri) 16:51
投稿者名:Lantern
Eメール:
URL :
タイトル:
Re^3: クラスは遅い?
クラスだからといって動的配列の取り扱いが複雑になるわけではありませんよ。
プロパティで配列受け渡しできますし。

'子フォーム
Private sAry() as String
Private lCol as Long
Private lRow as Long
'2次元行数
Public Property Let ArrayCol(ByVal newVal as Long)
    lCol& = newVal&
End Property

'2次元列数
Public Property Let ArrayRow(ByVal newVal as Long)
    lRow& = newVal&
End Property

'2次元配列受け取り
Public Property Get StringArray() as String()
    StringArray = sAry$()
End Property

'2次元配列の確保
Public Function AllocateArray() as Boolean
    ReDim sAry(lCol&, lRow&) as String
End Function

-----------------------------------------------
それと、Read_Fileという関数ですが、この場合子フォーム共通でしょうから
クラスに持たせて、メソッドとしてコールすればもっと楽にコードできますよ。
子フォームに
Public Function Read_File(ByVal strLoadFile as String) as Boolean
    'ファイル読んで子フォームの変数(従来のSDIの様に)に格納
End Function

親フォームからは
    Call FormChild.Read_File(strLoadFile)
としてやればよいかと。

----------------------------------------------------------------
FormはFormsコレクションに格納されていますので
For Each frmF In Forms
    frmF.X = ****
Next frmF
でできます。


誤字・書式ミス色々あるかもですが、参考になればと思います。

投稿時間:2003/03/29(Sat) 08:35
投稿者名:ひろぽん
Eメール:
URL :
タイトル:
Re^4: クラスは遅い?
> クラスだからといって動的配列の取り扱いが複雑になるわけではありませんよ。
> プロパティで配列受け渡しできますし。
>
> '子フォーム
> Private sAry() as String
> Private lCol as Long
> Private lRow as Long
> '2次元行数
> Public Property Let ArrayCol(ByVal newVal as Long)
>     lCol& = newVal&
> End Property
>
> '2次元列数
> Public Property Let ArrayRow(ByVal newVal as Long)
>     lRow& = newVal&
> End Property
>
> '2次元配列受け取り
> Public Property Get StringArray() as String()
>     StringArray = sAry$()
> End Property
>
> '2次元配列の確保
> Public Function AllocateArray() as Boolean
>     ReDim sAry(lCol&, lRow&) as String
> End Function
>
ご回答、ありがとうございます。私はクラス初心者なもので、せっかくご呈示していただいた
コードですが、非常に高度な手法をお使いになっているため、理解するにが大変です。
実際、本クラスを用いて2次元配列にデータを格納する場合は、どうすればいいのでしょうか。


> ----------------------------------------------------------------
> FormはFormsコレクションに格納されていますので
> For Each frmF In Forms
>     frmF.X = ****
> Next frmF
> でできます。
>
これは子フォームにクラスで定義したXに値を代入する(開いている子フォーム全て)
という意味でよろしいでしょうか。

クラスはおろか、Fortran -> VBに転身して間もないもので、理解不足が甚だしく
ご迷惑をおかけしますが、何とぞよろしくご教授いただけますようよろしくお願いします。