C# と VB.NET の質問掲示板

ASP.NET、C++/CLI、Java 何でもどうぞ

ログ内検索
  • キーワードを複数指定する場合は 半角スペース で区切ってください。
  • 検索条件は、(AND)=[A かつ B] (OR)=[A または B] となっています。
  • [返信]をクリックすると返信ページへ移動します。
キーワード/ 検索条件 /
検索範囲/ 強調表示/ ON (自動リンクOFF)
結果表示件数/ 記事No検索/ ON
大文字と小文字を区別する

全過去ログを検索

<< 0 | 1 | 2 | 3 >>
■83299  Re[18]: 大量のテキストファイルをマルチスレッドで高速に読み込む方法
□投稿者/ 金尾 -(2017/03/16(Thu) 16:54:58)
    fs.Read(bs, 0, file_size)
    を同時に実行するのが問題ですので
    fs.Read(bs, 0, file_size)
    が完了するまで、他のスレッドを停止させるような方法があれば
    教えてください


記事No.83253 のレス /過去ログ142より / 関連記事表示
削除チェック/

■83303  Re[18]: 大量のテキストファイルをマルチスレッドで高速に読み込む方法
□投稿者/ とっちゃん -(2017/03/16(Thu) 18:01:21)
    No83299 (金尾 さん) に返信


    No83298 より
    > シングルスレッドにしないと、それぞれのファイルの読み込みがぶつかり合って
    > 逆に遅くなってしまいます。

    えーっと。。。これは計測済みの結果ですか?試してみましたか?
    SSDが対象で、一括読み込みですよね?

    シングルスレッドにしないと、Cloneしてる間にデータが壊れてたり
    Data_in が実行された時点で bs1 が意図したファイルの内容ではないとか
    そういうことではありませんか?

    まずは、
    > bs をそのループの中のローカル変数としてメモリを確保。
    と書いていますが、各ファイルごとにメモリを用意して、
    Cloneをしないように書き換えてみるところから始めることをお勧めします。
記事No.83253 のレス /過去ログ142より / 関連記事表示
削除チェック/

■83302  Re[19]: 大量のテキストファイルをマルチスレッドで高速に読み込む方法
□投稿者/ なちゃ -(2017/03/16(Thu) 17:57:32)
    今の構造だと、素直に配列を必要分最初に作成した方がいいと思いますよ、今なら10個だけですし。
    ※ファイルサイズが固定の場合ですが

    でもって、タスクは実際には10もいらないはずです。多分一つでも効果があります。
    これは、一つでもタスクを使うと、読み込みとデータ処理が並列に動作できるようになるからです。

    あと複数タスクの待機は、Task.WaitAll使いましょう。
記事No.83253 のレス /過去ログ142より / 関連記事表示
削除チェック/

■83305  Re[20]: 大量のテキストファイルをマルチスレッドで高速に読み込む方法
□投稿者/ 金尾 -(2017/03/16(Thu) 18:06:03)
    No83302 (なちゃ さん) に返信

    ストレージからのデータ読み込み時間>>データの処理時間
    の時にはタスクは1つでも十分なのですが、
    ストレージからのデータ読み込み時間<<データの処理時間
    という条件の時には、タスクは複数あった方が高速処理が可能となります。

    そして大きな問題は何度も申し上げているのですが、
    同時に複数ファイルにアクセスすると読み込み速度が落ちてしまうことです。
    そのため、ファイル読み込み部分のみシングルスレッドで
    処理部分だけをマルチスレッド化する必要があります。


    Dim fs As New FileStream(StrPath(k), FileMode.Open, FileAccess.Read)
    fs.Read(bs, 0, file_size)
    fs.Close()

    MultiTask_in(1) = New Task(AddressOf Data_in, bs)
    MultiTask_in(1).Start()



    Dim fs As New FileStream(StrPath(k), FileMode.Open, FileAccess.Read)
    fs.Read(bs, 0, file_size)
    fs.Close()

    MultiTask_in(2) = New Task(AddressOf Data_in, bs)
    MultiTask_in(2).Start()

    ・・・

    のようにして、10個の処理を書き下せば高速処理できるのは分かるのですが
    コードが見づらくなってしまいます。

    できればfor文でまとめてしまいたいのですが
    どのようにすれば良いでしょうか?

    散々、考えたのですが、
    クローンコピーを使わない限り、他に方法がないように思うのですが
    良い方法があれば教えてください。

    あと、ファイルサイズは固定です。






記事No.83253 のレス /過去ログ142より / 関連記事表示
削除チェック/

■83304  Re[20]: 大量のテキストファイルをマルチスレッドで高速に読み込む方法
□投稿者/ なちゃ -(2017/03/16(Thu) 18:02:36)
    No83302 (なちゃ さん) に返信
    > でもって、タスクは実際には10もいらないはずです。多分一つでも効果があります。
    > これは、一つでもタスクを使うと、読み込みとデータ処理が並列に動作できるようになるからです。

    一つでもと書きましたか、今のコードだとだめでした、毎回待機してしまうので。
    というか、今のコードだと読み込み前に待機してしまうので必ず処理がストールします。

    タスク群のループ毎に独立したバッファを用意して、読み込みまでは全開のタスクと並列で行い、読み込み後に全開のタスクの完了を待つというのが効率的な流れです。
記事No.83253 のレス /過去ログ142より / 関連記事表示
削除チェック/

■83306  Re[21]: 大量のテキストファイルをマルチスレッドで高速に読み込む方法
□投稿者/ 金尾 -(2017/03/16(Thu) 18:10:32)




    とっちゃんさん


    > えーっと。。。これは計測済みの結果ですか?試してみましたか?
    > SSDが対象で、一括読み込みですよね?
    SSDだとほとんど変わりませんが、
    HDDだと速度が低下します。
    SSDだけでなくHDDでも使えるように最適化したいと考えています。



    > シングルスレッドにしないと、Cloneしてる間にデータが壊れてたり
    > Data_in が実行された時点で bs1 が意図したファイルの内容ではないとか
    > そういうことではありませんか?
    この辺りは検証できておりません。



    > まずは、
    > > bs をそのループの中のローカル変数としてメモリを確保。
    > と書いていますが、各ファイルごとにメモリを用意して、
    > Cloneをしないように書き換えてみるところから始めることをお勧めします。
    全て書き下す方法だと所望することが実現できているのですが
    コードが冗長的になってしまうため
    できればForやEachなどを使いたいと考えています
    タスクの数が10個と決まっていれば良いですが
    30とかに増やしたい場合に大変です。


記事No.83253 のレス /過去ログ142より / 関連記事表示
削除チェック/

■83307  Re[18]: 大量のテキストファイルをマルチスレッドで高速に読み込む方法
□投稿者/ PANG2 -(2017/03/16(Thu) 18:36:55)
    No83298 (金尾 さん) に返信
    > ありがとうございます。
    >
    > Parallel.For() というものをしらなかったので非常に勉強になりました。
    >
    > ただ、今回のケースでは、
    > 上で書いたように、ファイルを読み込む場所だけは
    > シングルスレッドにしないと、それぞれのファイルの読み込みがぶつかり合って
    > 逆に遅くなってしまいます。
    >
    > どうするのがもっともスマートな書き方でしょうか?

    ファイルを読み込む場所をlock
記事No.83253 のレス /過去ログ142より / 関連記事表示
削除チェック/

■83308  Re[19]: 大量のテキストファイルをマルチスレッドで高速に読み込む方法
□投稿者/ 金尾 -(2017/03/16(Thu) 19:02:56)
    ありがとうございます。

    試してみたのですが逆に10%程度ですが、更に遅くなってしまいました。



    Dim fs As New FileStream(StrPath(i), FileMode.Open, FileAccess.Read)

    SyncLock fs

    fs.Read(bs, 0, file_size)
    fs.Close()


    End SyncLock


    という場所をロックしたのですが
    場所は合っていますでしょうか?
記事No.83253 のレス /過去ログ142より / 関連記事表示
削除チェック/

■83309  Re[20]: 大量のテキストファイルをマルチスレッドで高速に読み込む方法
□投稿者/ なちゃ -(2017/03/16(Thu) 19:36:44)
    各ファイルの具体的な処理内容がわからないので負荷の程度は分かりませんが、まあ高い確率で状況を正確に把握できていないだけか、無駄な処理をやってしまっているかで、実際はディスクの方が遅い気がしますけどね、それはさておき。

    今の流れだと、タスクが終わってから次の読み込みまでに必ず待機が入るので、最速にはなりません。
    タスク数はCPUコア数以上にしても無駄ですし、実際ディスク読み込みよりも10倍かかるなんてよっぽどです。

    どちらかというと、例えばバッファ10こをリソースと考えて、空く度に使い回すという流れの方がシンプルにできそうです。
記事No.83253 のレス /過去ログ142より / 関連記事表示
削除チェック/

■83311  Re[21]: 大量のテキストファイルをマルチスレッドで高速に読み込む方法
□投稿者/ なちゃ -(2017/03/16(Thu) 19:44:52)
    例えばブロッキングキューを使って、まずバッファを10こ保存します。

    ファイル全部のループを開始し、ブロッキングキューからバッファ取り出して、ファイルを読み込んでタスク起動します。
    各タスクは、渡されたバッファを処理して、終了時にブロッキングキューにバッファを戻します。

    これだけでうまいこと効率的に動くはずです。
    ボトルネックがディスク側でも処理側でも、おおむね最大効率で動けるはずです。
記事No.83253 のレス /過去ログ142より / 関連記事表示
削除チェック/

■83310  Re[21]: 大量のテキストファイルをマルチスレッドで高速に読み込む方法
□投稿者/ 金尾 -(2017/03/16(Thu) 19:41:11)
    No83309 (なちゃ さん) に返信


    そうですよね
    これってbsのバイト配列を毎回生成、削除していますよね

    やはり10個のタスクを書き下すしか方法がないのでしょうか?
     
記事No.83253 のレス /過去ログ142より / 関連記事表示
削除チェック/

■83312  Re[22]: 大量のテキストファイルをマルチスレッドで高速に読み込む方法
□投稿者/ 金尾 -(2017/03/16(Thu) 19:49:52)
    > ■No83309 (なちゃ さん) に返信


    ありがとうございます。

    ブロッキングキューとは何でしょうか?
    検索してもよく分からなかったので
    コードを書いていただけないでしょうか?
     
記事No.83253 のレス /過去ログ142より / 関連記事表示
削除チェック/

■83314  Re[20]: 大量のテキストファイルをマルチスレッドで高速に読み込む方法
□投稿者/ PANG2 -(2017/03/16(Thu) 22:42:52)
    No83308 (金尾 さん) に返信
    > ありがとうございます。
    > 
    > 試してみたのですが逆に10%程度ですが、更に遅くなってしまいました。
    > 
    > 
    > 
    >                 Dim fs As New FileStream(StrPath(i), FileMode.Open, FileAccess.Read)
    > 
    >             SyncLock fs
    > 
    >                 fs.Read(bs, 0, file_size)
    >                 fs.Close()
    > 
    > 
    >             End SyncLock
    > 
    > 
    > という場所をロックしたのですが
    > 場所は合っていますでしょうか?
    
    場所も方法も間違っています。
    こんな感じ
    
    Dim LockObj As New Object
    Parallel.For(略
    	Dim bs() As Byte
    	SyncLock LockObj //二重侵入を禁止
    		Dim fs As New FileStream(StrPath(i), FileMode.Open, FileAccess.Read)
    		fs.Read(bs, 0, file_size)
    		fs.Close()
    	End SyncLock
    
    	//bsを文字列配列に変換して処理
記事No.83253 のレス /過去ログ142より / 関連記事表示
削除チェック/

■83315  Re[21]: 大量のテキストファイルをマルチスレッドで高速に読み込む方法
□投稿者/ なちゃ -(2017/03/17(Fri) 08:30:27)
    Parallel.Forは大変便利なツールですが、最大効率を求めて使うものではありません。
    そのしくみ上まず確実に非効率が発生します。
    また内部でロックするのは定石的には悪手です。
    最初の方法とどちらが効率的かは判断が難しいですが、悪化する可能性がそれなりに高いように思います。
    シンプルに実装できるのでそれがメリットですが。
記事No.83253 のレス /過去ログ142より / 関連記事表示
削除チェック/

■83322  Re[22]: 大量のテキストファイルをマルチスレッドで高速に読み込む方法
□投稿者/ 金尾 -(2017/03/17(Fri) 11:30:56)

    PANG2さん


    ありがとうございます。


    Dim fs As New FileStream(StrPath(i), FileMode.Open, FileAccess.Read)
    SyncLock fs
    fs.Read(bs, 0, file_size)
    fs.Close()
    End SyncLock



    Dim LockObj As New Object
    Parallel.For(略
    Dim bs() As Byte
    SyncLock LockObj //二重侵入を禁止
    Dim fs As New FileStream(StrPath(i), FileMode.Open, FileAccess.Read)
    fs.Read(bs, 0, file_size)
    fs.Close()
    End SyncLock


    の2つの方法で速度を比較したのですが
    SSD、HDDともに、私が最初に提示した一つ目の方法の方が10〜20%程度高速な結果が得られました。


    ただ、気になるのですが、
    上でも書いた通りですが
    HDDの場合には、Taskを使った方法よりもParallel.Forの方が10〜20%程度遅いです。

    一方で、SSDの場合には、なぜかこれが逆転してParallel.Forの方が
    10〜20%速くなります。

    Taskだと410MB/s程度しか読み込み速度が上がらないのに
    Parallel.Forだと510MB/sまで速度が上がります。

    計算処理の有無が関係しているのではないかと思い、
    読み込みだけのコードを書いてみましたが、やはり変わりませんでした。

    実際に総読み込み時間を測定してみても
    やはりParallel.Forの方が速いです。

    もちろん、メモリは毎回クリアして測定しています。

    不思議な結果となりました。




    なちゃ さん
    ブロッキングキューというものの使い方を教えていただけないでしょうか?
記事No.83253 のレス /過去ログ142より / 関連記事表示
削除チェック/

■83321  Re[22]: 大量のテキストファイルをマルチスレッドで高速に読み込む方法
□投稿者/ なちゃ -(2017/03/17(Fri) 11:19:19)
    携帯から手打ちしかできない状態なので多分ミスはあると思いますが、例えばおおむねこんな感じ
    
    Dim count As New CountDownEvent(filePaths.Length)
    
    ' これがいわゆるブロッキングキュー
    Dim queue As New BlockingCollection(Of Byte())()
    Dim maxBuffers = 10
    Dim bufferCount = 0
    
    For Each filePath In filePaths
        Dim buff As Byte() = Nothing
        If bufferCount < maxBuffers Then
            If Not queue.TryTake(buff) Then
                ' 最大数まで作成してなくて空きがないなら新規作成
                buff = New Byte(fileLength - 1){}
                bufferCount += 1
            End If
        Else
            ' 最大数まで作成済みならバッファ空くまで待機
            buff = queue.Take()
        End If
    
        ' buffにファイル読み込み
    
        Task.Factory.StartNew(
            Sub()
                ' buffの処理
    
                ' 処理の終わりを通知してバッファをキューに戻す
                count.Signal()
                queue.Add(buff)
            End Sub
    End For
    
    ' タスクが全部完了するまで待機
    count.Wait()
    
記事No.83253 のレス /過去ログ142より / 関連記事表示
削除チェック/

■83324  Re[23]: 大量のテキストファイルをマルチスレッドで高速に読み込む方法
□投稿者/ 金尾 -(2017/03/17(Fri) 11:36:51)
    なちゃ さん
    ありがとうございます。

    後ほど試してみます。

    上で、TaskとParallel.Forを比較したと書きましたが
    Taskというのはマルチスレッド関係無しで
    Forループで単にファイルをバイト配列を読み込むのを繰り返しているだけです。

    Parallel.Forの方は排他処理を行っているので、
    同じ速度になるはずなのに、不思議です・・・
記事No.83253 のレス /過去ログ142より / 関連記事表示
削除チェック/

■83325  Re[24]: 大量のテキストファイルをマルチスレッドで高速に読み込む方法
□投稿者/ 金尾 -(2017/03/17(Fri) 11:40:01)
    510MB/sではなく560MB/sでした
記事No.83253 のレス /過去ログ142より / 関連記事表示
削除チェック/

■83329  Re[25]: 大量のテキストファイルをマルチスレッドで高速に読み込む方法
□投稿者/ なちゃ -(2017/03/17(Fri) 12:06:09)
    ちょっとミスを見つけたため修正。
    
    Forの終わりをNextに直したのと、End Sub の後に)つけたの、あと一応CountdownEventの大文字小文字修正。
    
    あと、ファイルオープン位置はもっと前にする方が良いかもしれないのでコメント追加。
    
    Dim count As New CountdownEvent(filePaths.Length)
    
    ' これがいわゆるブロッキングキュー
    Dim queue As New BlockingCollection(Of Byte())()
    Dim maxBuffers = 10
    Dim bufferCount = 0
    
    For Each filePath In filePaths
        ' ファイルのオープンはここの方がいいかも(オープンのみ)
    
        Dim buff As Byte() = Nothing
        If bufferCount < maxBuffers Then
            If Not queue.TryTake(buff) Then
                ' 最大数まで作成してなくて空きがないなら新規作成
                buff = New Byte(fileLength - 1){}
                bufferCount += 1
            End If
        Else
            ' 最大数まで作成済みならバッファ空くまで待機
            buff = queue.Take()
        End If
    
        ' buffにファイル読み込み
    
        Task.Factory.StartNew(
            Sub()
                ' buffの処理
    
                ' 処理の終わりを通知してバッファをキューに戻す
                count.Signal()
                queue.Add(buff)
            End Sub)
    Next
    
    ' タスクが全部完了するまで待機
    count.Wait()
    
記事No.83253 のレス /過去ログ142より / 関連記事表示
削除チェック/

■83327  Re[25]: 大量のテキストファイルをマルチスレッドで高速に読み込む方法
□投稿者/ 金尾 -(2017/03/17(Fri) 12:01:57)



    方法1

    Dim bs00(CInt(file_size - 1)) As Byte

    For i As Integer = 1 To filenum
    Dim fs As New FileStream(StrPath(i), FileMode.Open, FileAccess.Read)
    fs.Read(bs00, 0, file_size)
    fs.Close()
    Next i

    方法2

    Parallel.For(1, filenum, Sub(i)

    Dim bs0(CInt(file_size - 1)) As Byte

    Dim fs As New FileStream(StrPath(i), FileMode.Open, FileAccess.Read)
    SyncLock fs
    fs.Read(bs0, 0, file_size)
    End SyncLock
    fs.Close()

    End Sub)


    方法1だと、SSDで410MB/s、HDDで140MB/s
    方法2だと、SSDで550MB/s、HDDで110MB/s

    という転送速度になるのですが、
    理由は分かりますか?

    やっていることは同じなのに
    なぜかSSDとHDDで方法によってここまで速度差が見られるのはなぜなのでしょうか?




記事No.83253 のレス /過去ログ142より / 関連記事表示
削除チェック/

<前の20件 | 次の20件>

<< 0 | 1 | 2 | 3 >>

パスワード/

- Child Tree -