tagCANDY CGI VBレスキュー(花ちゃん)の Visual Basic 6.0用 掲示板 [ツリー表示へ]   [Home]
一括表示(VB6.0)
タイトル容量が増えると動作速度が遅くなる
記事No13906
投稿日: 2009/07/31(Fri) 08:33
投稿者ごれ
今下記のプログラムにしていて、CSVファイルの容量が増えると処理速度がかなり遅くなって(2分くらい)しまいます。処理速度を速くするいい方法はないでしょうか?
よろしくお願いします。

Public Sub make_csv_Data(Number As Integer)
    Dim fName As String
    Dim fData As String
    Dim data1 As String
    Dim data2 As String
    Dim data3 As String
    Dim write_data As Variant

    On Error GoTo Error_Handler     'エラー処理
      
    fName = ".\\data\\data_" & CStr(Number) & ".csv"
    fData = Number  
    Open fName For Input Access Read As fData
    write_data = "Point:" & memDate(Number).sName(Number) & vbCrLf
    write_data = write_data & "Title1,Title3,Title2" & vbCrLf
    Do Until EOF(fData)
        Lock fData
          Input #fData, data1, data2, data3
        Unlock fData
        write_data = write_data & data1 & "," & data3 & "," & data2 & vbCrLf
    Loop

Error_Handler:
    Close fData
    
    fName = ".\\data" & CStr(Number) & "_download" & ".csv"
    fData = FreeFile
    Open fName For Output Access Write As fData
    Print #fData, write_data
    Close fData
    
End Sub

[ツリー表示へ]
タイトルRe: 容量が増えると動作速度が遅くなる
記事No13907
投稿日: 2009/07/31(Fri) 10:42
投稿者オショウ
> 今下記のプログラムにしていて、CSVファイルの容量が増えると処理速度がかなり遅くなって(2分くらい)しまいます。処理速度を速くするいい方法はないでしょうか?

  容量って、最初がどの程度で遅くなると言うにはどの程度
  なんでしょうか?
  もしくは、1行あたり、何文字で何行あると言うことでし
  ょうか?
  客観的にも比較できる内容が無いので何とも・・・

  で、高速化ですが、HDD上にあるCSVファイルですネ
  VB6の場合、ほぼ無いです・・・

  ただ、上記コードの場合、何故、Variant使っているんですか?
  若干かもしれませんが、これが速度低下の一因の可能性はある
  かと。

※ CPUやHDDの速度、メモリの空き容量なども速度に影響します
  ので、プログラム的にここが悪い!なんてことは、上記のコードで
  は、なかなか言えない・・・

※ メモリをどっさり搭載しているなら、CSV作成時点でRAMDISKにで
  も保存するとか・・・

以上。参考まで

[ツリー表示へ]
タイトルRe^2: 容量が増えると動作速度が遅くなる
記事No13908
投稿日: 2009/07/31(Fri) 11:35
投稿者ごれ
>   容量って、最初がどの程度で遅くなると言うにはどの程度
>   なんでしょうか?
>   もしくは、1行あたり、何文字で何行あると言うことでし
>   ょうか?
>   客観的にも比較できる内容が無いので何とも・・・
最初は1KBで400KBを超えるとおそくなります
1行あたり半角28文字です。
最大2万行くらいになります

>   ただ、上記コードの場合、何故、Variant使っているんですか?
>   若干かもしれませんが、これが速度低下の一因の可能性はある
>   かと。
Variantを使っている理由は特にありません。

一度試してみたのですが
write_data = write_data & data1 & "," & data3 & "," & data2 & vbCrLf
をコメントアウトすればかなり早くなりましたが
この部分は必要なんです(汗)

[ツリー表示へ]
タイトルRe^3: 容量が増えると動作速度が遅くなる
記事No13909
投稿日: 2009/07/31(Fri) 12:16
投稿者neptune
こんにちは
> 最初は1KBで400KBを超えるとおそくなります
> 1行あたり半角28文字です。
> 最大2万行くらいになります
数10MB程度までなら
binaryで一気読みして
http://hanatyan.sakura.ne.jp/vbhlp/Binary.htm
その結果をwrite_dataと結合するのが一番だと思いますよ。


> Variantを使っている理由は特にありません。
ならstring型を使った方が良いです。
Variantはデータ型の中で一番遅いと言われてますしね。

[ツリー表示へ]
タイトルRe^4: 容量が増えると動作速度が遅くなる
記事No13910
投稿日: 2009/07/31(Fri) 12:21
投稿者ごれ
オショウさん、neptuneさん
ありがとうございます。
ちょっと試してみます

[ツリー表示へ]
タイトルRe^3: 容量が増えると動作速度が遅くなる
記事No13912
投稿日: 2009/07/31(Fri) 13:22
投稿者よねKEN
> 一度試してみたのですが
> write_data = write_data & data1 & "," & data3 & "," & data2 & vbCrLf
> をコメントアウトすればかなり早くなりましたが

文字列の連結は非常に重い処理ですので、
(1)Midステートメント使う記述方法に変える
(2)Join関数を使って結合する
などの工夫をするという手があります。

(1)については、ここのサイト内検索で「Midステートメント」で検索するか、
「文字列連結 Midステートメント」といったキーワードでWeb検索してみてください。

[ツリー表示へ]
タイトルRe^3: 容量が増えると動作速度が遅くなる
記事No13914
投稿日: 2009/07/31(Fri) 14:00
投稿者YuO
> 一度試してみたのですが
> write_data = write_data & data1 & "," & data3 & "," & data2 & vbCrLf
> をコメントアウトすればかなり早くなりましたが
> この部分は必要なんです(汗)

このwrite_dataはファイルへの書き込み以外でも使うのですか。
使わないのであれば,このwrite_dataへ文字列を連結している部分を,
そのままファイルへの出力にしてやればよいのではないでしょうか。

[ツリー表示へ]
タイトルRe^3: 容量が増えると動作速度が遅くなる
記事No13916
投稿日: 2009/07/31(Fri) 17:23
投稿者ごれ
みなさんありがとうございます

色々試行錯誤してみましたが


下の通りデータを並べ替えているので一気にバイナリで読んで書き込むこともできないので
あきらめました。
write_data = write_data & "Title1,Title3,Title2" & vbCrLf
    Do Until EOF(fData)
        Lock fData
          Input #fData, data1, data2, data3
        Unlock fData
        write_data = write_data & data1 & "," & data3 & "," & data2 & vbCrLf
    Loop

文字列の連結に一番時間がかかっているようなので連結をつなぎ変えるのが一番ベストなのでしょうか?
みなさんのご意見お願いいたします。

[ツリー表示へ]
タイトルRe^4: 容量が増えると動作速度が遅くなる
記事No13919
投稿日: 2009/07/31(Fri) 18:39
投稿者neptune
> 文字列の連結に一番時間がかかっているようなので連結をつなぎ変えるのが一番ベストなのでしょうか?
その点に付いては、よねKENさんが既にアドバイスされていますね。

データの並べ替えについても一気読み、メモリ上で並べ替えが速いような気はします。
・・・多分速い。
で、最後にjoinするとか?

そんなに複雑でもないようなのでやって見た方が間違いないです。

いづれにしても、よねKENさんのアドバイスにあるように
string型の再定義は重いですので、それを減らす工夫が必要ですね。

[ツリー表示へ]
タイトルRe^4: 容量が増えると動作速度が遅くなる
記事No13920
投稿日: 2009/07/31(Fri) 19:01
投稿者オショウ
> 下の通りデータを並べ替えているので一気にバイナリで読んで書き込むこともできないので
> あきらめました。

  あきらめられるんですか・・・
  なら、プログラム的には突っ込みませんが、VB6なら限界?

  .NETならMemoryStreamクラスあるんで楽勝なんですが・・・

※ こういうのって、本来、プログラマーのだいご味なんですが
  諦めたら、全然楽しくない!

以上。

[ツリー表示へ]
タイトルRe^4: 容量が増えると動作速度が遅くなる
記事No13921
投稿日: 2009/07/31(Fri) 22:05
投稿者魔界の仮面弁士
> みなさんのご意見お願いいたします。

ループ内で行われている
 write_data = write_data & data1 & "," & data3 & "," & data2 & vbCrLf
の部分を
 write_data = write_data & (data1 & "," & data3 & "," & data2 & vbCrLf)
にするだけで、処理効率がアップしますよ。(理由は後述します)


しかし、もっと効率の良い方法があります。
それは既に No.13914 で YuO さんが回答している方法なのですが、
出力ファイルと入力ファイルを同時に開いて、順次書きこんでいくという物です。

つまり、現在の

    fData = FreeFile()
  Open 入力ファイル For Input Access Read As #fData
  Do Until EOF(fData)
    Input #fData, data1, data2, data3
        write_data = write_data & data1 & "," & data3 & "," & data2 & vbCrLf
    Loop
    Close #fData
    fData = FreeFile()
    Open fName For Output Access Write As #fData
    Print #fData, write_data
    Close #fData

のような処理を、

    fIn = FreeFile()
  Open 入力ファイル For Input Access Read As #fIn
    fOut = FreeFile()
    Open 出力ファイル For Output Access Write As #fOut
  Do Until EOF(fIn)
    Input #fIn, data1, data2, data3
    Print #fOut, data1 & "," & data3 & "," & data2
    Loop
    Close #fIn, #fOut

という形式にするという物です。これにより、相当の高速化が期待できます。



> 下の通りデータを並べ替えているので一気にバイナリで読んで書き込むこともできないので
> あきらめました。

文字列連結を行うと、そのたびに
  新たな文字列領域の確保 → 確保された領域へのコピー → 以前の領域を破棄
という作業が繰り返し行われる事になります。

短い文字列同士の連結であれば、確保サイズが小さいため、さほど時間はかからないのですが、
長い文字列に対して連結する場合には、この確保と解放にかかる時間が問題となります。


そこで、「長い文字列」に対して文字列を連結させる回数を減らす工夫が必要です。


ひとつ実験。下記を試してみてください。

Private Sub Command1_Click()
    Dim T As Single, L As Long, S As String
    T = Timer
    For L = 1 To 10000
        S = S & CStr(L) & "TEST"
    Next
    Debug.Print Format(Timer - T, "#,0.000秒")
End Sub

Private Sub Command2_Click()
    Dim T As Single, L As Long, S As String
    T = Timer
    For L = 1 To 10000
        S = S & (CStr(L) & "TEST")
    Next
    Debug.Print Format(Timer - T, "#,0.000秒")
End Sub


いずれも同じ処理を行っています。違うのは、文字列連結部分を
  S = S &  CStr(L) & "TEST"       'Command1
    S = S & (CStr(L) & "TEST")      'Command2
にしているだけです。

両者は当然、同じ文字列を生成しますが、後者の方が高速に処理されます。
実際、これを当方環境で試してみると、平均時間で
      ループ 10000 回        20000 回       30000 回
  Command1 …… 約0.6秒 …… 約3.8秒 …… 約10.8秒
  Command2 …… 約0.3秒 …… 約1.8秒 …… 約5.3秒
という速度差になりました。処理時間がほぼ半分になっていますね。


これは Command1 の方の処理が
                          '『S = S & CStr(L) & "TEST"』
  X = S & CStr(L)      ' S が大きいので、処理に時間がかかる
  S = X & "TEST"        ' X が大きいので、処理に時間がかかる

に相当する処理が行われているのに対し、Command2 の方では
                          '『S = S & (CStr(L) & "TEST")』
  X = CStr(L) & "TEST" ' 短い文字列なので、処理時間が短い
  S = S & X             ' S が大きいので、処理に時間がかかる

という処理になるためです。そのため、処理時間を約半分で済ませることができます。


そしてこれが先の
 'write_data = write_data & data1 & "," & data3 & "," & data2 & vbCrLf
 write_data = write_data & (data1 & "," & data3 & "," & data2 & vbCrLf)
という修正案を提示した理由となります。

[ツリー表示へ]