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

わんくま同盟

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

C# と VB.NET の入門サイト

ツリー一括表示

大量のテキストファイルをマルチスレッドで高速に読み込む方法 /金尾 (17/03/15(Wed) 20:40) #83253
Re[1]: 大量のテキストファイルをマルチスレッドで高速に読み.. /金尾 (17/03/15(Wed) 20:41) #83254
│└ Re[2]: 大量のテキストファイルをマルチスレッドで高速に読み.. /774RR (17/03/15(Wed) 20:59) #83255
│  └ Re[3]: 大量のテキストファイルをマルチスレッドで高速に読み.. /金尾 (17/03/15(Wed) 21:03) #83256
│    ├ Re[4]: 大量のテキストファイルをマルチスレッドで高速に読み.. /魔界の仮面弁士 (17/03/15(Wed) 21:38) #83258
│    └ Re[4]: 大量のテキストファイルをマルチスレッドで高速に読み.. /なちゃ (17/03/16(Thu) 09:36) #83271
Re[1]: 大量のテキストファイルをマルチスレッドで高速に読み.. /魔界の仮面弁士 (17/03/15(Wed) 21:10) #83257
Re[1]: 大量のテキストファイルをマルチスレッドで高速に読み.. /魔界の仮面弁士 (17/03/15(Wed) 21:56) #83260
│└ Re[2]: 大量のテキストファイルをマルチスレッドで高速に読み.. /金尾 (17/03/15(Wed) 21:44) #83259
│  ├ Re[3]: 大量のテキストファイルをマルチスレッドで高速に読み.. /金尾 (17/03/15(Wed) 22:22) #83261
│  ├ Re[3]: 大量のテキストファイル /Azulean (17/03/15(Wed) 22:52) #83262
│  │└ Re[4]: 大量のテキストファイル /金尾 (17/03/15(Wed) 23:04) #83263
│  │  └ Re[5]: 大量のテキストファイル /Azulean (17/03/15(Wed) 23:17) #83264
│  │    └ Re[6]: 大量のテキストファイル /金尾 (17/03/15(Wed) 23:28) #83265
│  │      └ Re[7]: 大量のテキストファイル /Azulean (17/03/15(Wed) 23:37) #83266
│  └ Re[3]: 大量のテキストファイルをマルチスレッドで高速に読み.. /魔界の仮面弁士 (17/03/16(Thu) 04:43) #83268
Re[1]: 大量のテキストファイルをマルチスレッドで高速に読み.. /ニートプログラマ (17/03/16(Thu) 08:55) #83269
  └ Re[2]: 大量のテキストファイルをマルチスレッドで高速に読み.. /金尾 (17/03/16(Thu) 09:39) #83272
    ├ Re[3]: 大量のテキストファイルをマルチスレッドで高速に読み.. /魔界の仮面弁士 (17/03/16(Thu) 10:40) #83274
    │└ Re[4]: 大量のテキストファイルをマルチスレッドで高速に読み.. /PANG2 (17/03/16(Thu) 13:42) #83293
    └ Re[3]: 大量のテキストファイルをマルチスレッドで高速に読み.. /なちゃ (17/03/16(Thu) 10:25) #83273
      └ Re[4]: 大量のテキストファイルをマルチスレッドで高速に読み.. /金尾 (17/03/16(Thu) 10:58) #83275
        └ Re[5]: 大量のテキストファイルをマルチスレッドで高速に読み.. /魔界の仮面弁士 (17/03/16(Thu) 11:22) #83276
          └ Re[6]: 大量のテキストファイルをマルチスレッドで高速に読み.. /金尾 (17/03/16(Thu) 11:26) #83277
            ├ Re[7]: 大量のテキストファイルをマルチスレッドで高速に読み.. /魔界の仮面弁士 (17/03/16(Thu) 11:44) #83279
            └ Re[7]: 大量のテキストファイルをマルチスレッドで高速に読み.. /金尾 (17/03/16(Thu) 11:34) #83278
              └ Re[8]: 大量のテキストファイルをマルチスレッドで高速に読み.. /魔界の仮面弁士 (17/03/16(Thu) 11:47) #83280
                └ Re[9]: 大量のテキストファイルをマルチスレッドで高速に読み.. /金尾 (17/03/16(Thu) 12:06) #83281
                  └ Re[10]: 大量のテキストファイルをマルチスレッドで高速に読み.. /魔界の仮面弁士 (17/03/16(Thu) 12:18) #83283
                    └ Re[11]: 大量のテキストファイルをマルチスレッドで高速に読み.. /金尾 (17/03/16(Thu) 12:25) #83284
                      ├ Re[12]: 大量のテキストファイルをマルチスレッドで高速に読み.. /金尾 (17/03/16(Thu) 12:32) #83285
                      │└ Re[13]: 大量のテキストファイルをマルチスレッドで高速に読み.. /Azulean (17/03/16(Thu) 12:41) #83289
                      └ Re[12]: 大量のテキストファイルをマルチスレッドで高速に読み.. /Azulean (17/03/16(Thu) 12:36) #83286
                        └ Re[13]: 大量のテキストファイルをマルチスレッドで高速に読み.. /金尾 (17/03/16(Thu) 12:39) #83288
                          └ Re[14]: 大量のテキストファイルをマルチスレッドで高速に読み.. /金尾 (17/03/16(Thu) 13:20) #83292
                            └ Re[15]: 大量のテキストファイルをマルチスレッドで高速に読み.. /金尾 (17/03/16(Thu) 14:28) #83295
                              └ Re[16]: 大量のテキストファイルをマルチスレッドで高速に読み.. /とっちゃん (17/03/16(Thu) 16:00) #83297
                                └ Re[17]: 大量のテキストファイルをマルチスレッドで高速に読み.. /金尾 (17/03/16(Thu) 16:53) #83298
                                  ├ Re[18]: 大量のテキストファイルをマルチスレッドで高速に読み.. /金尾 (17/03/16(Thu) 16:54) #83299
                                  ├ Re[18]: 大量のテキストファイルをマルチスレッドで高速に読み.. /とっちゃん (17/03/16(Thu) 18:01) #83303
                                  │└ Re[19]: 大量のテキストファイルをマルチスレッドで高速に読み.. /なちゃ (17/03/16(Thu) 17:57) #83302
                                  │  ├ Re[20]: 大量のテキストファイルをマルチスレッドで高速に読み.. /金尾 (17/03/16(Thu) 18:06) #83305
                                  │  └ Re[20]: 大量のテキストファイルをマルチスレッドで高速に読み.. /なちゃ (17/03/16(Thu) 18:02) #83304
                                  │    └ Re[21]: 大量のテキストファイルをマルチスレッドで高速に読み.. /金尾 (17/03/16(Thu) 18:10) #83306
                                  └ Re[18]: 大量のテキストファイルをマルチスレッドで高速に読み.. /PANG2 (17/03/16(Thu) 18:36) #83307
                                    └ Re[19]: 大量のテキストファイルをマルチスレッドで高速に読み.. /金尾 (17/03/16(Thu) 19:02) #83308
                                      ├ Re[20]: 大量のテキストファイルをマルチスレッドで高速に読み.. /なちゃ (17/03/16(Thu) 19:36) #83309
                                      │├ Re[21]: 大量のテキストファイルをマルチスレッドで高速に読み.. /なちゃ (17/03/16(Thu) 19:44) #83311
                                      │└ Re[21]: 大量のテキストファイルをマルチスレッドで高速に読み.. /金尾 (17/03/16(Thu) 19:41) #83310
                                      │  └ Re[22]: 大量のテキストファイルをマルチスレッドで高速に読み.. /金尾 (17/03/16(Thu) 19:49) #83312
                                      └ Re[20]: 大量のテキストファイルをマルチスレッドで高速に読み.. /PANG2 (17/03/16(Thu) 22:42) #83314
                                        └ Re[21]: 大量のテキストファイルをマルチスレッドで高速に読み.. /なちゃ (17/03/17(Fri) 08:30) #83315
                                          ├ Re[22]: 大量のテキストファイルをマルチスレッドで高速に読み.. /金尾 (17/03/17(Fri) 11:30) #83322
                                          └ Re[22]: 大量のテキストファイルをマルチスレッドで高速に読み.. /なちゃ (17/03/17(Fri) 11:19) #83321
                                            └ Re[23]: 大量のテキストファイルをマルチスレッドで高速に読み.. /金尾 (17/03/17(Fri) 11:36) #83324
                                              └ Re[24]: 大量のテキストファイルをマルチスレッドで高速に読み.. /金尾 (17/03/17(Fri) 11:40) #83325
                                                ├ Re[25]: 大量のテキストファイルをマルチスレッドで高速に読み.. /なちゃ (17/03/17(Fri) 12:06) #83329
                                                └ Re[25]: 大量のテキストファイルをマルチスレッドで高速に読み.. /金尾 (17/03/17(Fri) 12:01) #83327
                                                  ├ Re[26]: 大量のテキストファイルをマルチスレッドで高速に読み.. /金尾 (17/03/17(Fri) 12:03) #83328
                                                  ├ Re[26]: 大量のテキストファイルをマルチスレッドで高速に読み.. /むー (17/03/17(Fri) 12:14) #83331
                                                  │├ Re[27]: 大量のテキストファイルをマルチスレッドで高速に読み.. /なちゃ (17/03/17(Fri) 12:11) #83330
                                                  ││└ Re[28]: 大量のテキストファイルをマルチスレッドで高速に読み.. /なちゃ (17/03/17(Fri) 12:16) #83332
                                                  │└ Re[27]: 大量のテキストファイルをマルチスレッドで高速に読み.. /なちゃ (17/03/17(Fri) 12:18) #83333
                                                  │  └ Re[28]: 大量のテキストファイルをマルチスレッドで高速に読み.. /金尾 (17/03/17(Fri) 12:43) #83336
                                                  │    ├ Re[29]: 大量のテキストファイルをマルチスレッドで高速に読み.. /魔界の仮面弁士 (17/03/17(Fri) 13:23) #83339
                                                  │    └ Re[29]: 大量のテキストファイルをマルチスレッドで高速に読み.. /774RR (17/03/17(Fri) 13:11) #83337
                                                  └ Re[26]: 大量のテキストファイルをマルチスレッドで高速に読み.. /PANG2 (17/03/17(Fri) 13:18) #83338
                                                    └ Re[27]: 大量のテキストファイルをマルチスレッドで高速に読み.. /魔界の仮面弁士 (17/03/17(Fri) 13:30) #83340
                                                      └ Re[28]: 大量のテキストファイルをマルチスレッドで高速に読み.. /金尾 (17/03/18(Sat) 17:23) #83345 解決済み


親記事 / ▼[ 83254 ] ▼[ 83257 ] ▼[ 83260 ] ▼[ 83269 ]
■83253 / 親階層)  大量のテキストファイルをマルチスレッドで高速に読み込む方法
□投稿者/ 金尾 (1回)-(2017/03/15(Wed) 20:40:08)

分類:[.NET 全般] 


10MBのテキストファイルが1万個ほどあり、
これを読み込んで、その内容をまとめたデータを作成したいと考えています。

普通にReadlineで読み込んでいくと
10分くらいかかってしまいます。
ただ、CPU使用率を見ていると、シングルスレッドで動作しているため、
10%程度しか使用していないため
マルチスレッドにすれば10倍近くに高速化できると思います。

それで、以下のように10個ずつファイルを読み込んで
マルチスレッドでプログラムを作成したみたのですが
どうも、
Encoding.UTF8.GetString(bs).Split(CChar(vbCrLf))
というコマンドをマルチスレッドにしてもほとんど速くなりません。

恐らく、メモリがボトルネックになり、
同時に複数の配列を処理できていないのではないかと思うのですが、
タスクマネージャやリソースモニターでは
メモリの使用量は確認できても、メモリの転送速度はモニターできないので
それが正しいかどうか分かりません。

どうすれば、もっと高速化することができますか?


以下がコードです。



Sub code


For i = 1 to 1000

Dim fs As New FileStream(Path(i), FileMode.Open, FileAccess.Read)
Dim bs1(file_size - 1) As Byte
fs.Read(bs1, 0, file_size)
fs.Close()
MultiTask_in01 = New Task(AddressOf Data_in, bs01)
MultiTask_in01.Start()

fs = New FileStream(Path(i + 1), FileMode.Open, FileAccess.Read)
Dim bs2(file_size - 1) As Byte
fs.Read(bs2, 0, file_size)
fs.Close()
MultiTask_in02 = New Task(AddressOf Data_in, bs02)
MultiTask_in02.Start()

'・・・・

fs = New FileStream(Path(i + 9), FileMode.Open, FileAccess.Read)
Dim bs10(file_size - 1) As Byte
fs.Read(bs10, 0, file_size)
fs.Close()
MultiTask_in10 = New Task(AddressOf Data_in, bs10)
MultiTask_in10.Start()


next i


End Sub


Private Sub Data_in(ByVal bs0() As Byte)


Dim bs() As Byte = bs0.Clone

Dim str() As String = Encoding.UTF8.GetString(bs).Split(CChar(vbCrLf))

'ここにデータをまとめるための処理を書く。

End Sub





[ □ Tree ] 返信 編集キー/

▲[ 83253 ] / ▼[ 83255 ]
■83254 / 1階層)  Re[1]: 大量のテキストファイルをマルチスレッドで高速に読み込む方法
□投稿者/ 金尾 (2回)-(2017/03/15(Wed) 20:41:35)


Waitが抜けていました


For i = 1 to 1000

 Dim fs As New FileStream(Path(i), FileMode.Open, FileAccess.Read)
 Dim bs1(file_size - 1) As Byte
 fs.Read(bs1, 0, file_size)
 fs.Close()
 MultiTask_in01 = New Task(AddressOf Data_in, bs01)
 MultiTask_in01.Start()

 fs = New FileStream(Path(i + 1), FileMode.Open, FileAccess.Read)
 Dim bs2(file_size - 1) As Byte
 fs.Read(bs2, 0, file_size)
 fs.Close()
 MultiTask_in02 = New Task(AddressOf Data_in, bs02)
 MultiTask_in02.Start()

	'・・・・

fs = New FileStream(Path(i + 9), FileMode.Open, FileAccess.Read)
 Dim bs10(file_size - 1) As Byte
 fs.Read(bs10, 0, file_size)
 fs.Close()
 MultiTask_in10 = New Task(AddressOf Data_in, bs10)
 MultiTask_in10.Start()


        MultiTask_in01.Wait()
        MultiTask_in02.Wait()
        MultiTask_in03.Wait()
        MultiTask_in04.Wait()
        MultiTask_in05.Wait()
        MultiTask_in06.Wait()
        MultiTask_in07.Wait()
        MultiTask_in08.Wait()
        MultiTask_in09.Wait()
        MultiTask_in10.Wait()

next i

[ 親 83253 / □ Tree ] 返信 編集キー/

▲[ 83254 ] / ▼[ 83256 ]
■83255 / 2階層)  Re[2]: 大量のテキストファイルをマルチスレッドで高速に読み込む方法
□投稿者/ 774RR (488回)-(2017/03/15(Wed) 20:59:11)
# コードは見てない

ファイルの読み込みは I/O bound つまりはハードディスクや SSD の性能で決まってしまうので
> 10%程度しか使用していないため
は当たり前(CPU はハードディスクの応答を待っているので 10% 以上つかいようがない)んでないかな。

マルチスレッドで複数のファイルを読むとシーク時間がかかる分逆効果、かもしれない。

高速化するにはまず測定。何が遅いのかを確かめてから。

[ 親 83253 / □ Tree ] 返信 編集キー/

▲[ 83255 ] / ▼[ 83258 ] ▼[ 83271 ]
■83256 / 3階層)  Re[3]: 大量のテキストファイルをマルチスレッドで高速に読み込む方法
□投稿者/ 金尾 (4回)-(2017/03/15(Wed) 21:03:17)
No83255 (774RR さん) に返信


SSDを使っていますが、
まったくここはボトルネックになっていないことが分かっています。
SSDからファイルをバイナリ配列に読み込む時間よりも20倍くらい
バイナリ配列からストリング配列に変換する方が時間がかかります。

そのため、マルチスレッド化すれば高速化できると考えました

[ 親 83253 / □ Tree ] 返信 編集キー/

▲[ 83256 ] / 返信無し
■83258 / 4階層)  Re[4]: 大量のテキストファイルをマルチスレッドで高速に読み込む方法
□投稿者/ 魔界の仮面弁士 (1187回)-(2017/03/15(Wed) 21:38:10)
2017/03/15(Wed) 21:39:35 編集(投稿者)

No83256 (金尾 さん) に返信
> SSDを使っていますが、

SSD なのですね。

定常状態のパフォーマンスは、実機で測定してみないとわかりませんが、
下記のような測定結果がありました。


2012年9月「HDD,SSD,USBメモリのアクセス速度比較実験」
https://hikaku.cman.jp/hdd_rpm/

2015年「SSD 徹底比較」
http://www.ssd-fan.com/pcmark8/ssd_bm.php

2017年冬「2.5インチSSD性能」
http://pssection9.com/archives/2-5-sata3-ssd-spec.html


今時の SSD だと、500MB/sec 程度出せそうですね。

仮に 500MB/sec だったとしても、100GB の連続転送は、
 100GB ÷ 500MB/sec = 200 秒 ≒ 3 分
程度を要します。

「10分くらい」の処理を「10倍」にするには、
約 1.5GB/sec の転送速度が必要なので、
SSD から NVMe に換装しないと無理かも知れません。
[ 親 83253 / □ Tree ] 返信 編集キー/

▲[ 83256 ] / 返信無し
■83271 / 4階層)  Re[4]: 大量のテキストファイルをマルチスレッドで高速に読み込む方法
□投稿者/ なちゃ (181回)-(2017/03/16(Thu) 09:36:14)
No83256 (金尾 さん) に返信
> ■No83255 (774RR さん) に返信
>
>
> SSDを使っていますが、
> まったくここはボトルネックになっていないことが分かっています。
> SSDからファイルをバイナリ配列に読み込む時間よりも20倍くらい
> バイナリ配列からストリング配列に変換する方が時間がかかります。
>
> そのため、マルチスレッド化すれば高速化できると考えました

これってトータルどのくらいのファイルサイズで確認しました?
また実行環境のメモリ量はどのくらいでしょうか?
こういうシンプルな処理でディスクが全く影響しないとか、後であったようにHDDにしても全く変わらないとかはほぼ考えられないので、単にキャッシュから読んだだけでは?という気がします。

[ 親 83253 / □ Tree ] 返信 編集キー/

▲[ 83253 ] / 返信無し
■83257 / 1階層)  Re[1]: 大量のテキストファイルをマルチスレッドで高速に読み込む方法
□投稿者/ 魔界の仮面弁士 (1186回)-(2017/03/15(Wed) 21:10:15)
2017/03/15(Wed) 21:20:52 編集(投稿者)

No83253 (金尾 さん) に返信
> 10MBのテキストファイルが1万個ほどあり、

トータルで 100GB ぐらいですね。


> 普通にReadlineで読み込んでいくと
> 10分くらいかかってしまいます。

どのようなストレージを利用していますか?

シーケンシャルリードが 64MB/sec 程度の 5400 rpm な HDD と仮定した場合、
100GB のデータ転送にかかる時間は、約 26 分換算。

シーケンシャルリードが 100MB/sec 程度の 7200 rpm な HDD と仮定した場合、
100GB のデータ転送にかかる時間は、約 17 分換算。

シーケンシャルリードが 210 MB/sec 程度の SSD と仮定した場合、
100GB のデータ転送にかかる時間は、約 8 分換算。

ランダムアクセスの場合は、処理にもよりますが、概ねこの倍ぐらいでしょうか。


> マルチスレッドにすれば10倍近くに高速化できると思います。

1 万個のファイルが、10 台のSSD に分散配置されているなら、
各ドライブごとに専任のタスクを割り当てれば早くなりそうです。

しかし同一の物理ドライブ上にある場合は、マルチスレッドにすることで、
むしろ遅くなってしまう可能性が高そうです。
[ 親 83253 / □ Tree ] 返信 編集キー/

▲[ 83253 ] / 返信無し
■83260 / 1階層)  Re[1]: 大量のテキストファイルをマルチスレッドで高速に読み込む方法
□投稿者/ 魔界の仮面弁士 (1188回)-(2017/03/15(Wed) 21:56:43)
No83253 (金尾 さん) に返信
No83253 (金尾 さん) に返信
> For i = 1 to 1000
>  Dim fs As New FileStream(Path(i), FileMode.Open, FileAccess.Read)
>  Dim bs1(file_size - 1) As Byte
ファイル数が多いので、配列を都度作成するのではなく、
確保済みの配列を使いまわすようにした方が良いでしょう。

100MB もの巨大な配列を、毎回廃棄&確保し続けるのは効率が悪いです。

Dim a = Now
Dim size_100MB = (100 * 1024 * 1024) - 1
For i = 1 To 1000
 Dim bs1(size_100MB) As Byte
 bs1(0) = i And &HFF
Next
Dim b = Now
MsgBox((b - a).TotalSeconds)


上記は、100MB の配列確保を 1000 回行っていますが、
当方環境では約 4 秒という時間を要しました。

しかし bs1配列の宣言をループの外に出した場合、
その 4 秒間で 20億回のループが可能でした。
[ 親 83253 / □ Tree ] 返信 編集キー/

▲[ 83257 ] / ▼[ 83261 ] ▼[ 83262 ] ▼[ 83268 ]
■83259 / 2階層)  Re[2]: 大量のテキストファイルをマルチスレッドで高速に読み込む方法
□投稿者/ 金尾 (6回)-(2017/03/15(Wed) 21:44:28)
確かに、100GBのファイルをコピーしようとすると10分くらいかかってしまいます。

しかし、VB.NETで
Dim str() As String = Encoding.UTF8.GetString(bs).Split(CChar(vbCrLf))

の部分をコメントアウトし、
バイト配列→ストリング配列の処理を行わなければ
1分くらいで処理が完了するのですが
これはなぜでしょうか?

配列が空になっているのではないかと思い
if bs(filesize -1000)= 0 then msgbox ""
みたいなのも入れてみましたが
配列にはデータが格納できているみたいです

[ 親 83253 / □ Tree ] 返信 編集キー/

▲[ 83259 ] / 返信無し
■83261 / 3階層)  Re[3]: 大量のテキストファイルをマルチスレッドで高速に読み込む方法
□投稿者/ 金尾 (8回)-(2017/03/15(Wed) 22:22:09)
すいません、上記のコードは分かりやすく書き換えたものです
もちろん配列の定義はForの外で行っています
また、Forの外と中でも比較を行いましたが
それ以外に律速する要素が大きいため計算時間はほとんど差異が見られませんでした。

ところで、
マルチスレッド読み込みを行うプログラムを使って
SSDとHDDで計算時間の比較を行いましたが
全く同じ結果が得られました
やはりストレージ速度がボトルネックにはなっていないようです

メモリがボトルネックになっているかどうかは
どのようにすれば調べられますでしょうか?
 
[ 親 83253 / □ Tree ] 返信 編集キー/

▲[ 83259 ] / ▼[ 83263 ]
■83262 / 3階層)  Re[3]: 大量のテキストファイル
□投稿者/ Azulean (793回)-(2017/03/15(Wed) 22:52:00)
2017/03/15(Wed) 22:54:07 編集(投稿者)

No83261 (金尾 さん) に返信
> メモリがボトルネックになっているかどうかは
> どのようにすれば調べられますでしょうか?

ストレージの影響ではないと仮定しても、メモリのスピードの影響だと言い切るのは弱いと思いますね。
(仮にメモリのスピードの影響だとしても、それはマルチスレッドにしたところで改善しないので、打つ手なしなので建設的ではありません…)
というより、たいていはストレージよりもメモリの方が速いはずなので、「メモリ(のアクセススピード)がボトルネックである」という仮定をしたり、調べたりすることがありません。

巨大な配列の確保・解放を繰り返した場合、スワップが発生する、ページフォールトが起きるといった意味ではメモリ・ストレージ間のデータ転送待ちになることはよくあります。
それが起きている場合、タスクマネージャーの詳細タブでページフォールトの数が勢いよく上がるはずなので、それで観測できるかもしれません。
それが要因の場合は、メモリの確保・解放を繰り返さない、それでも発生するならメモリ確保量・処理量を見直すってことになると思います。


No83259 (金尾 さん) に返信
> しかし、VB.NETで
> Dim str() As String = Encoding.UTF8.GetString(bs).Split(CChar(vbCrLf))
>
> の部分をコメントアウトし、
> バイト配列→ストリング配列の処理を行わなければ
> 1分くらいで処理が完了するのですが
> これはなぜでしょうか?

「バイト列を UTF-8 と解釈して String にする処理」と「改行ごとに区切った String 配列を生み出す処理」が時間かかるってことでは。
前者が遅いのか、後者が遅いのかは、Split なくしたコードを実験すれば切り分けられるかと。


ところで、Split は配列を生み出すので、そういう意味だとループの中で確保・解放を助長していることになります。
10MB のテキストというと、かなりの分量でしょうから、それを 1 万ファイルとなると、メモリの確保・解放による処理のロスは大きいかもしれませんね。
[ 親 83253 / □ Tree ] 返信 編集キー/

▲[ 83262 ] / ▼[ 83264 ]
■83263 / 4階層)  Re[4]: 大量のテキストファイル
□投稿者/ 金尾 (10回)-(2017/03/15(Wed) 23:04:38)
「バイト列を UTF-8 と解釈して String にする処理」と「改行ごとに区切った String 配列を生み出す処理」
では、後者の方が数倍時間がかかります。

>10MB のテキストというと、かなりの分量でしょうから、それを 1 万ファイル
一万ファイル全て読み込ませるのではなく
10ファイルずつあるいは30ファイルずつ読み込ませています

[ 親 83253 / □ Tree ] 返信 編集キー/

▲[ 83263 ] / ▼[ 83265 ]
■83264 / 5階層)  Re[5]: 大量のテキストファイル
□投稿者/ Azulean (794回)-(2017/03/15(Wed) 23:17:18)
No83263 (金尾 さん) に返信
> 「バイト列を UTF-8 と解釈して String にする処理」と「改行ごとに区切った String 配列を生み出す処理」
> では、後者の方が数倍時間がかかります。

配列の要素数と、それぞれの要素(String) 1 つあたりの文字数のイメージを共有してみてください。


> >10MB のテキストというと、かなりの分量でしょうから、それを 1 万ファイル
> 一万ファイル全て読み込ませるのではなく
> 10ファイルずつあるいは30ファイルずつ読み込ませています

それは理解していますが、「ずつ」とはいえ、ボタンを押すか、プログラムをスタートさせるかで、「連続的に 1 万ファイルの分量を処理するなら、メモリの確保・解放の回数が非常に多いでしょう」ということを言いたかったのです。


もっとも、配列の数が多く、かつそれの確保・解放が支配的なのか、文字列の走査が支配的なのかはなんとも言えませんが。
[ 親 83253 / □ Tree ] 返信 編集キー/

▲[ 83264 ] / ▼[ 83266 ]
■83265 / 6階層)  Re[6]: 大量のテキストファイル
□投稿者/ 金尾 (12回)-(2017/03/15(Wed) 23:28:22)
ありがとうございます。

> 配列の要素数と、それぞれの要素(String) 1 つあたりの文字数のイメージを共有してみてください。

これはどのようにすれば良いのでしょうか?

> もっとも、配列の数が多く、かつそれの確保・解放が支配的なのか、文字列の走査が支配的なのかはなんとも言えませんが。

もし、後者が支配的なのだとすると
マルチスレッドで速度向上が見られるはずですが
それが見られないので
前者が支配的なのではないかと考えました。


[ 親 83253 / □ Tree ] 返信 編集キー/

▲[ 83265 ] / 返信無し
■83266 / 7階層)  Re[7]: 大量のテキストファイル
□投稿者/ Azulean (795回)-(2017/03/15(Wed) 23:37:37)
2017/03/15(Wed) 23:40:00 編集(投稿者)

No83265 (金尾 さん) に返信
>>配列の要素数と、それぞれの要素(String) 1 つあたりの文字数のイメージを共有してみてください。
>
> これはどのようにすれば良いのでしょうか?

デバッグしていくつかのファイルで配列の数を見るとか、配列の中の文字数を調べてみていって、何文字くらいになっているか、数量を適当に量ってください。
統一的な手法はないので、周りの人が処理量を把握できるように、あなたが考えて決めてください。


>>もっとも、配列の数が多く、かつそれの確保・解放が支配的なのか、文字列の走査が支配的なのかはなんとも言えませんが。
(略)
> 前者が支配的なのではないかと考えました。

その仮定が正しいのであれば、「今のやり方=Split した結果を欲する」が崩せない限り、時間短縮は難しいかと思います。
本当に、「改行ごとにばらした String 配列」じゃないといけないのか、考え直すのも一手でしょう。
[ 親 83253 / □ Tree ] 返信 編集キー/

▲[ 83259 ] / 返信無し
■83268 / 3階層)  Re[3]: 大量のテキストファイルをマルチスレッドで高速に読み込む方法
□投稿者/ 魔界の仮面弁士 (1189回)-(2017/03/16(Thu) 04:43:55)
No83259 (金尾 さん) に返信
> CChar(vbCrLf)
これは ControlChars.Cr と同義ですが、本当にそれで良いのでしょうか。
CR + LF ではなく、CR で分割していることになりますが…。


> Dim str() As String = Encoding.UTF8.GetString(bs).Split(CChar(vbCrLf))

パフォーマンスの検証とするならば、このようにまとめて書くのではなく、
・GetString メソッドの実行時間
・Split メソッドの実行時間
のように、各メソッドごとに調べるべきかと思います。

処理的には Split の方が時間がかかりそうですね。
以下、当方環境での実験結果。


'検証用の 10MB のダミー配列
Dim bin10MB(10 * 1024 * 1024 - 1) As Byte
For n = 0 To bin10MB.GetUpperBound(0)
bin10MB(n) = &H2A
Next
For n = 10 To bin10MB.GetUpperBound(0) Step 255
bin10MB(n) = 13
Next

'GetString および Split の速度調査
Dim a = Now
Dim str10MB = System.Text.Encoding.UTF8.GetString(bin10MB)
Dim b = Now
Dim aryLines = str10MB.Split(ControlChars.Cr)
Dim c = Now

Console.WriteLine((b - a).TotalSeconds) '0.0110029 〜 0.0139725
Console.WriteLine((c - b).TotalSeconds) '0.0369978 〜 0.0409782

上記は単一回の実行ですが、ループ処理で複数回連続で呼び出す場合は、
前回のループ処理で使われていた String 配列(≠Byte 配列)が破棄される分のコストも
もしかしたら足かせになってくるかもしれません。



No83261 (金尾 さん) に返信
> すいません、上記のコードは分かりやすく書き換えたものです
処理は単純化してもらった方が良いですが、実際のコードとあまりに異なっていると、
第三者が試したときに結果がブレてしまい、むしろ分かりにくくなる恐れがありそうです。


ところで、最初の質問にあった ReadLine だったものを並列化したいという話だけなら、
パフォーマンスはさておき、ReadLine のままで並列化できるようにも思うのですが、
何故 ReadLine を使わない方法に変更されたのでしょうか?

全バイナリの一括読み取りなら File.ReadAllBytes の方がスマートに書けそうですし、
テキストファイルから「内容をまとめたデータ」を作成するための処理というのが、
行単位の順次読み取りで済むのであれば、わざわざ自前で Split して配列化せずとも、
File.ReadLines メソッドで十分であるような気もするのですが。

もしかして、FileAccess 等の制御をしたいが故に FileStream を使っているということなのでしょうか。
それとも、System.IO.File の静的メソッドだと、エラーになったとか、極端に遅くなるなどの
何かしらの弊害があって現状のコードになったのでしょうか。
[ 親 83253 / □ Tree ] 返信 編集キー/

▲[ 83253 ] / ▼[ 83272 ]
■83269 / 1階層)  Re[1]: 大量のテキストファイルをマルチスレッドで高速に読み込む方法
□投稿者/ ニートプログラマ (1回)-(2017/03/16(Thu) 08:55:10)
興味がある内容なので注目しています。えっとまずは
本当にCPUを100%使えるのかを確認したらいかが
でしょう??

https://www.axfc.net/u/3787171.zip

32bitのx86尽瘁バイナリexeで何もしないスレッドを
4つ作成します。当方のCore2 Quadマシンではきっ
ちり全コアが100%の使用率になります。
各スレッドはフレッド終了フラグをテストループして
いるだけです。

C#で同様のプログラムを作成してまずはきっちり
CPUを使いきれるのかを確認し、その後実際の処理を
記述してどの処理がCPUを遊ばせるのかを追及する
手法もあるかと思います。
C#は詳しくないですがスレッドの管理のために
クリティカルセッションを頻繁に使っていると
スレッド化してもCPUの使用率は上がりません。
あ、スレッド化の意味が無いってことではない
です。



[ 親 83253 / □ Tree ] 返信 編集キー/

▲[ 83269 ] / ▼[ 83274 ] ▼[ 83273 ]
■83272 / 2階層)  Re[2]: 大量のテキストファイルをマルチスレッドで高速に読み込む方法
□投稿者/ 金尾 (13回)-(2017/03/16(Thu) 09:39:54)
Azuleanさん

> 配列の中の文字数を調べてみていって、何文字くらいになっているか、数量を適当に量ってください。
もちろん数量のはかり方は分かるのですが
そのはかった数量をどのようにして共有したら良いのか分からないのですが
Split関数では生成した配列をそのままコピーされますので
前回使用した配列のサイズなどは流用できないと思うのですが
どのようにすれば良いですか?

魔界の仮面弁士さん

> 何故 ReadLine を使わない方法に変更されたのでしょうか?

これは
魔界の仮面弁士さん自身も仰っているように
> しかし同一の物理ドライブ上にある場合は、マルチスレッドにすることで、
> むしろ遅くなってしまう可能性が高そうです。

これが起きるためです。
SSDだとそれほど違いはないですがHDDでReadlineのコードをマルチスレッド化すると
3倍くらい速度が落ちる結果が得られました。
ストレージからデータをメモリに読み込む部分だけは
シングルスレッドにしておき、
読み込んだデータをストリング配列にする部分だけをマルチスレッド化しないと
逆に速度が低下してしまいます。

> の一括読み取りなら File.ReadAllBytes の方がスマートに書けそうですし
実際にはファイルの全てを読み込む必要はなく、後半の8割程度のみが必要なためです
File.ReadAllBytes だと、ファイルの一部のみを読み込むことができません。


ニートプログラマさん

> がある内容なので注目しています。えっとまずは
> 本当にCPUを100%使えるのかを確認したらいかが
> でしょう??


マルチスレッドはこれまでに何度も作成していますので
CPU使用率が100%になるのは確認でてきます



[ 親 83253 / □ Tree ] 返信 編集キー/

▲[ 83272 ] / ▼[ 83293 ]
■83274 / 3階層)  Re[3]: 大量のテキストファイルをマルチスレッドで高速に読み込む方法
□投稿者/ 魔界の仮面弁士 (1190回)-(2017/03/16(Thu) 10:40:34)
2017/03/16(Thu) 10:46:28 編集(投稿者)

No83272 (金尾 さん) に返信
> ストレージからデータをメモリに読み込む部分だけは
> シングルスレッドにしておき、
ストレージからの読み取りは、どうあっても 10 分間を要すると思っていたのですが、

> 実際にはファイルの全てを読み込む必要はなく、後半の8割程度のみが必要なためです
を実施していたために、実際にはそれより短い時間で処理できる目算ということなのですね。

# 情報の後出し(というか、提示のサンプルと実際の処理との乖離)が他にもありそうな予感…。


> Split関数では生成した配列をそのままコピーされますので
> 前回使用した配列のサイズなどは流用できないと思うのですが
> どのようにすれば良いですか?

「何を目的として Split を使っているのか」を説明していただいていないため
今回の事象に適合できるか分からないですが、元のコードの
 Encoding.UTF8.GetString(bs).Split(c)
だと、「元のバイナリ」「それを文字列化したもの」「さらにそれを分割したもの」
が必要なので、元データの約 3 倍のメモリを消費することになると思います。

現状のこの、
  UTF-8 Binary → String → 改行単位の String()
という変換処理を見直して、
  UTF-8 Binary → 改行文字の位置のみを列挙 → 必要な部分だけ随時 String 化
という手順にすれば、消費メモリを 3 割程度削減できる気がします。


データ量が多いので、手順が多少増えたとしても、メモリの破棄と再確保の
頻度と総量を減らす方が、結果的に高速化に貢献するものと予想。
(本当に高速化するかは、検証してみないと分からないですけれども)
[ 親 83253 / □ Tree ] 返信 編集キー/

▲[ 83274 ] / 返信無し
■83293 / 4階層)  Re[4]: 大量のテキストファイルをマルチスレッドで高速に読み込む方法
□投稿者/ PANG2 (161回)-(2017/03/16(Thu) 13:42:36)
2017/03/16(Thu) 14:09:24 編集(投稿者)
No83274 (魔界の仮面弁士 さん) に返信
> 現状のこの、
>   UTF-8 Binary → String → 改行単位の String()
> という変換処理を見直して、

MemoryStream / StreamReader で一行ずつ処理するのがよいかもしれません。

List<string> rows = new List<string>();
using (MemoryStream ms = new MemoryStream(bin))
using (var sr = new StreamReader(ms, Encoding.UTF8)) {
	string line;
	while ((line = sr.ReadLine()) != null) {
		rows.Add(line);
	}
}

追記
FileStream.CopyToで、FileStreamからMemoryStreamに直接バイナリを渡す
http://www.atmarkit.co.jp/fdotnet/dotnettips/1047streamcopyto/streamcopyto.html

[ 親 83253 / □ Tree ] 返信 編集キー/

▲[ 83272 ] / ▼[ 83275 ]
■83273 / 3階層)  Re[3]: 大量のテキストファイルをマルチスレッドで高速に読み込む方法
□投稿者/ なちゃ (182回)-(2017/03/16(Thu) 10:25:27)
マルチスレッド化して高速化できる可能性があるとすれば、読み込みとその後の処理の並列化です。
何倍も変わることはまずあり得ないですが。

この場合、読み込んだ後の処理だけを並列化しても意味がありません。
読み込みと処理を、並列化する必要があります。
前の読み込みデータを処理しながら、次の読み込みを並行して行うということです。
[ 親 83253 / □ Tree ] 返信 編集キー/

▲[ 83273 ] / ▼[ 83276 ]
■83275 / 4階層)  Re[4]: 大量のテキストファイルをマルチスレッドで高速に読み込む方法
□投稿者/ 金尾 (15回)-(2017/03/16(Thu) 10:58:24)
ありがとうございます。

> UTF-8 Binary → 改行文字の位置のみを列挙 → 必要な部分だけ随時 String 化


これは私も考えていたところです。
1次元のバイト配列で
ある要素からある要素までのみをstring化するにはどうしたら良いですか?
一度別の小さな配列にforループでコピーする必要がありますか?
 
[ 親 83253 / □ Tree ] 返信 編集キー/

▲[ 83275 ] / ▼[ 83277 ]
■83276 / 5階層)  Re[5]: 大量のテキストファイルをマルチスレッドで高速に読み込む方法
□投稿者/ 魔界の仮面弁士 (1191回)-(2017/03/16(Thu) 11:22:45)
No83275 (金尾 さん) に返信
> 一度別の小さな配列にforループでコピーする必要がありますか?
部分コピーを作らないで下さい。「無駄な配列を作らない」ことが目的なので。

> 1次元のバイト配列で
> ある要素からある要素までのみをstring化するにはどうしたら良いですか?
Encoding クラスの GetString メソッドには、
引数 1 個の「bytes」なオーバーロードだけではなく、
引数 3 個の「bytes, index, count」なオーバーロードもあります。
これを使えば、バイナリの一部のみを文字列化できるかと。
[ 親 83253 / □ Tree ] 返信 編集キー/

▲[ 83276 ] / ▼[ 83279 ] ▼[ 83278 ]
■83277 / 6階層)  Re[6]: 大量のテキストファイルをマルチスレッドで高速に読み込む方法
□投稿者/ 金尾 (16回)-(2017/03/16(Thu) 11:26:49)
ありがとうございます。

あと、vbcrlfの検索の仕方なのですが

Array.IndexOfを使えば良いとは思うのですが
合っていますか?

vbcrlfはバイトにしたときにどういう数値になりますか?
[ 親 83253 / □ Tree ] 返信 編集キー/

▲[ 83277 ] / 返信無し
■83279 / 7階層)  Re[7]: 大量のテキストファイルをマルチスレッドで高速に読み込む方法
□投稿者/ 魔界の仮面弁士 (1192回)-(2017/03/16(Thu) 11:44:16)
No83277 (金尾 さん) に返信
> vbcrlfはバイトにしたときにどういう数値になりますか?

Encoding クラスの GetBytes メソッドで変換してみれば分かる事ですよね? (^^;

結論から言えば、
 vbCr → {&H0D}
 vbLf → {&H0A}
 vbCrLf → {&H0D, &H0A}
です。数値で言えば 13 と 10 。この機会に覚えておくことをお奨めします。



> あと、vbcrlfの検索の仕方なのですが
> Array.IndexOfを使えば良いとは思うのですが
> 合っていますか?

それで良いとおもいます。今回は Cr + Lf の探索になるので、
まずは Cr の位置を探してから、その後続バイトが Lf かどうかを
追加チェックすればことで対応してみて下さい。
[ 親 83253 / □ Tree ] 返信 編集キー/

▲[ 83277 ] / ▼[ 83280 ]
■83278 / 7階層)  Re[7]: 大量のテキストファイルをマルチスレッドで高速に読み込む方法
□投稿者/ 金尾 (17回)-(2017/03/16(Thu) 11:34:24)
試しに
vbcrlfを検索せずにforループで行数分splitしてみましたが
5%程度しか速くなりませんでした。
 
[ 親 83253 / □ Tree ] 返信 編集キー/

▲[ 83278 ] / ▼[ 83281 ]
■83280 / 8階層)  Re[8]: 大量のテキストファイルをマルチスレッドで高速に読み込む方法
□投稿者/ 魔界の仮面弁士 (1193回)-(2017/03/16(Thu) 11:47:22)
2017/03/16(Thu) 12:03:35 編集(投稿者)

No83278 (金尾 さん) に返信
>> UTF-8 Binary → 改行文字の位置のみを列挙 → 必要な部分だけ随時 String 化
> vbcrlfを検索せずにforループで行数分splitしてみましたが
> 5%程度しか速くなりませんでした。


「必要な部分だけ随時 String 化」するのではなく、
「列挙した位置情報ですべてを String() 化」しているとか?

-- 追記 --

改行位置を取り出して、その後何をするのかにもよりますが、
「一行ずつ読み取る」ことだけが目的なら、データすべてを
String() として変換する必要は無く、

For Each 改行位置情報 In 改行位置列挙処理(rawBinary)
 Dim line As String = enc.GetString(rawBinary, 改行位置情報.Index, 改行位置情報.Length)


Next

となるような実装を用意すれば十分ではなかろうか、という意味です。
[ 親 83253 / □ Tree ] 返信 編集キー/

▲[ 83280 ] / ▼[ 83283 ]
■83281 / 9階層)  Re[9]: 大量のテキストファイルをマルチスレッドで高速に読み込む方法
□投稿者/ 金尾 (18回)-(2017/03/16(Thu) 12:06:02)
ありがとうございます。

納得しました、
コードが複雑になることは避けられませんが、仰る通り高速化は実現できると思います。

ところで、
ある任意の文字列をバイトにしたときにどのような数値になるか調べたい場合
どのような関数を使えば調べられますか?

vbcrなら13、vblfなら10といったことを調べられる関数を探しています
 
[ 親 83253 / □ Tree ] 返信 編集キー/

▲[ 83281 ] / ▼[ 83284 ]
■83283 / 10階層)  Re[10]: 大量のテキストファイルをマルチスレッドで高速に読み込む方法
□投稿者/ 魔界の仮面弁士 (1194回)-(2017/03/16(Thu) 12:18:40)
No83281 (金尾 さん) に返信
> ありがとうございます。
> 納得しました、

で、これに相当することを行ってくれるのが、
System.IO.File.ReadLines メソッドなんですが、これだと
「後半の8割程度のみ」まで進めるには、Skip 拡張メソッドで進めるか
TakeWhile して読み捨てるしか無さそうです。

FileStream とどちらが早くなるかは、データサイズと内容次第では
速度差が逆転する可能性もあるので、測定してみないと何とも言えません。


> ある任意の文字列をバイトにしたときにどのような数値になるか調べたい場合
> どのような関数を使えば調べられますか?

No83279 の冒頭で答えたばかりですよね…?


> vbcrなら13、vblfなら10といったことを調べられる関数を探しています
既定のコードページでの文字列符号化なら、Asc 関数、
UCS-2 としてなら、AscW 関数がお手軽です。逆変換は Chr / ChrW。

それ以外のエンコードの場合は、No83279 の手法を使いましょう。

たとえば System.Text.Encoding.UTF32.GetBytes(vbCrLf) なら、
0D-00-00-00-0A-00-00-00 な 8 バイトに符号化されます。
[ 親 83253 / □ Tree ] 返信 編集キー/

▲[ 83283 ] / ▼[ 83285 ] ▼[ 83286 ]
■83284 / 11階層)  Re[11]: 大量のテキストファイルをマルチスレッドで高速に読み込む方法
□投稿者/ 金尾 (19回)-(2017/03/16(Thu) 12:25:57)
とりあえず、全てのコードを文字列化する方法で
高速化できるか試してみたのですが
SSDだと2倍程度高速化できましたが
HDDだと10%程度逆に遅くなってしまいました。

ストレージの読み込みは律速していないと思ったのですが
なぜでしょうか?
もう少し試してみます
 
[ 親 83253 / □ Tree ] 返信 編集キー/

▲[ 83284 ] / ▼[ 83289 ]
■83285 / 12階層)  Re[12]: 大量のテキストファイルをマルチスレッドで高速に読み込む方法
□投稿者/ 金尾 (20回)-(2017/03/16(Thu) 12:32:21)
ところで、
読み出しソースのテキストファイルですが
秀丸で確認すると、
shift jisやutfなどではなく
「欧文」となっています。
Encoding.UTF8.GetStringのところも「欧文」に合わせてやれば
高速化できるのではないかと思うのですが、
https://dobon.net/vb/dotnet/string/getencodingobject.html

このページでは「欧文」というものがないようです。
「欧文」での取得はできないのでしょうか?
 
[ 親 83253 / □ Tree ] 返信 編集キー/

▲[ 83285 ] / 返信無し
■83289 / 13階層)  Re[13]: 大量のテキストファイルをマルチスレッドで高速に読み込む方法
□投稿者/ Azulean (797回)-(2017/03/16(Thu) 12:41:03)
No83285 (金尾 さん) に返信
> このページでは「欧文」というものがないようです。
> 「欧文」での取得はできないのでしょうか?

それが、Window-1252 と呼ばれるエンコードなら、Encoding.GetEncoding(1252) で取れるものだったはず。(スマホからなのでテストしてません)
[ 親 83253 / □ Tree ] 返信 編集キー/

▲[ 83284 ] / ▼[ 83288 ]
■83286 / 12階層)  Re[12]: 大量のテキストファイルをマルチスレッドで高速に読み込む方法
□投稿者/ Azulean (796回)-(2017/03/16(Thu) 12:36:40)
No83272 (金尾 さん) に返信
> もちろん数量のはかり方は分かるのですが
> そのはかった数量をどのようにして共有したら良いのか分からないのですが
> Split関数では生成した配列をそのままコピーされますので
> 前回使用した配列のサイズなどは流用できないと思うのですが
> どのようにすれば良いですか?

私の共有は、変数やコードの話ではなく、金尾さんと、ここを見ている他の方との情報共有という意味で書いていましたのでご留意ください。
処理量が第三者の予想のつかない数値になってたら話が通じない状態が続くので、それを懸念してです。

No83284 (金尾 さん) に返信
> HDDだと10%程度逆に遅くなってしまいました。
>
> ストレージの読み込みは律速していないと思ったのですが
> なぜでしょうか?

なちゃさんが途中で示唆されていたと思いますが、同じファイルの読み込みを繰り返すと、OS などによって、メモリにキャッシュされるので HDD と SSD の差がなくなるように見えることがあります。
ただ、所詮はキャッシュなのでいつかはなくなり、ストレージの差が出ます。
これは、マルチスレッドにしようと、メモリをどうしようとも変わりません。(打つ手がない)
[ 親 83253 / □ Tree ] 返信 編集キー/

▲[ 83286 ] / ▼[ 83292 ]
■83288 / 13階層)  Re[13]: 大量のテキストファイルをマルチスレッドで高速に読み込む方法
□投稿者/ 金尾 (21回)-(2017/03/16(Thu) 12:39:50)
すいません
「欧文」の件は解決しました
ファイル読み込み時にバイナリデータとして読んでいるので
この当たりは速度と無関係でした

[ 親 83253 / □ Tree ] 返信 編集キー/

▲[ 83288 ] / ▼[ 83295 ]
■83292 / 14階層)  Re[14]: 大量のテキストファイルをマルチスレッドで高速に読み込む方法
□投稿者/ 金尾 (22回)-(2017/03/16(Thu) 13:20:50)
Azuleanさん

ありがとうございます。

ちなみにメモリは毎回RamMapというソフトでクリアしていますので
この影響は排除できています
 
[ 親 83253 / □ Tree ] 返信 編集キー/

▲[ 83292 ] / ▼[ 83297 ]
■83295 / 15階層)  Re[15]: 大量のテキストファイルをマルチスレッドで高速に読み込む方法
□投稿者/ 金尾 (23回)-(2017/03/16(Thu) 14:28:01)
Private Sub main()

    For i As Integer = 1 To 1000

        Dim s As Integer = (i - 1) * div

    For j As Integer = 1 To 10

        Dim k As Integer = (i - 1) * div + j


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


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

    Next j



    For j As Integer = 1 To 10

        MultiTask_in(j).Wait()

    Next j

    Next i

End Sub



Private Sub Data_in(ByVal bs1() As Byte)

Dim bs0() As Byte = bs1.Clone

 Dim foundIndex As Integer = Array.IndexOf(Of Byte)(bs0, 13)


While 0 <= foundIndex

    If foundIndex + 1 < bs0.Length - 130 Then
        '次の要素を検索する
        Dim str As String = Encoding.UTF8.GetString(bs0, foundIndex, 130)
        foundIndex = Array.IndexOf(Of Byte)(bs0, 13, foundIndex + 1)
    Else
        '最後まで検索したときはループを抜ける
        Exit While
    End If
End While

End Sub

というコードを書いたのですが
Forループでバイト配列を流用したい場合
Dim bs0() As Byte = bs1.Clone
のようにして配列を一度コピーしないと、次の
ループで前のデータが上書きされてしまうと思います。

しかし、このクローンコピーは結構時間がかかってしまいます。

このクローンコピーを避けるためには、


    For j As Integer = 1 To 10
・・・・

    Next j

のところを



        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()

・・・


のようにしてforを使わずに書くしかないでしょうか?
もっと良い方法があれば教えてください。


[ 親 83253 / □ Tree ] 返信 編集キー/

▲[ 83295 ] / ▼[ 83298 ]
■83297 / 16階層)  Re[16]: 大量のテキストファイルをマルチスレッドで高速に読み込む方法
□投稿者/ とっちゃん (429回)-(2017/03/16(Thu) 16:00:36)
No83295 (金尾 さん) に返信
> もっと良い方法があれば教えてください。
>


> For j As Integer = 1 To 10
>
> Dim k As Integer = (i - 1) * div + j
>
>
> Dim fs As New FileStream(StrPath(k), FileMode.Open, FileAccess.Read)
> fs.Read(bs, 0, file_size)
> fs.Close()
>
>
> MultiTask_in(j) = New Task(AddressOf Data_in, bs)
> MultiTask_in(j).Start()
>
> Next j

このループを、Parallel.For() にして、
bs をそのループの中のローカル変数としてメモリを確保。
Data_in は、Taskにせず直接呼び出して Clone せずに利用でファイルごとにユニークデータにできます。

あとは、Data_in 内部で同時実行制御ができてれば大丈夫です(データ取りまとめ処理部分の実装がわからないので我々には不明ですが)。

[ 親 83253 / □ Tree ] 返信 編集キー/

▲[ 83297 ] / ▼[ 83299 ] ▼[ 83303 ] ▼[ 83307 ]
■83298 / 17階層)  Re[17]: 大量のテキストファイルをマルチスレッドで高速に読み込む方法
□投稿者/ 金尾 (24回)-(2017/03/16(Thu) 16:53:44)
ありがとうございます。

Parallel.For() というものをしらなかったので非常に勉強になりました。

ただ、今回のケースでは、
上で書いたように、ファイルを読み込む場所だけは
シングルスレッドにしないと、それぞれのファイルの読み込みがぶつかり合って
逆に遅くなってしまいます。

どうするのがもっともスマートな書き方でしょうか?

[ 親 83253 / □ Tree ] 返信 編集キー/

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



[ 親 83253 / □ Tree ] 返信 編集キー/

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


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

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

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

まずは、
> bs をそのループの中のローカル変数としてメモリを確保。
と書いていますが、各ファイルごとにメモリを用意して、
Cloneをしないように書き換えてみるところから始めることをお勧めします。

[ 親 83253 / □ Tree ] 返信 編集キー/

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

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

あと複数タスクの待機は、Task.WaitAll使いましょう。
[ 親 83253 / □ Tree ] 返信 編集キー/

▲[ 83302 ] / 返信無し
■83305 / 20階層)  Re[20]: 大量のテキストファイルをマルチスレッドで高速に読み込む方法
□投稿者/ 金尾 (26回)-(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文でまとめてしまいたいのですが
どのようにすれば良いでしょうか?

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

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







[ 親 83253 / □ Tree ] 返信 編集キー/

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

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

タスク群のループ毎に独立したバッファを用意して、読み込みまでは全開のタスクと並列で行い、読み込み後に全開のタスクの完了を待つというのが効率的な流れです。

[ 親 83253 / □ Tree ] 返信 編集キー/

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




とっちゃんさん


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



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



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



[ 親 83253 / □ Tree ] 返信 編集キー/

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

ファイルを読み込む場所をlock
[ 親 83253 / □ Tree ] 返信 編集キー/

▲[ 83307 ] / ▼[ 83309 ] ▼[ 83314 ]
■83308 / 19階層)  Re[19]: 大量のテキストファイルをマルチスレッドで高速に読み込む方法
□投稿者/ 金尾 (28回)-(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


という場所をロックしたのですが
場所は合っていますでしょうか?

[ 親 83253 / □ Tree ] 返信 編集キー/

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

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

どちらかというと、例えばバッファ10こをリソースと考えて、空く度に使い回すという流れの方がシンプルにできそうです。

[ 親 83253 / □ Tree ] 返信 編集キー/

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

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

これだけでうまいこと効率的に動くはずです。
ボトルネックがディスク側でも処理側でも、おおむね最大効率で動けるはずです。
[ 親 83253 / □ Tree ] 返信 編集キー/

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


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

やはり10個のタスクを書き下すしか方法がないのでしょうか?
 
[ 親 83253 / □ Tree ] 返信 編集キー/

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


ありがとうございます。

ブロッキングキューとは何でしょうか?
検索してもよく分からなかったので
コードを書いていただけないでしょうか?
 
[ 親 83253 / □ Tree ] 返信 編集キー/

▲[ 83308 ] / ▼[ 83315 ]
■83314 / 20階層)  Re[20]: 大量のテキストファイルをマルチスレッドで高速に読み込む方法
□投稿者/ PANG2 (163回)-(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を文字列配列に変換して処理

[ 親 83253 / □ Tree ] 返信 編集キー/

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

▲[ 83315 ] / 返信無し
■83322 / 22階層)  Re[22]: 大量のテキストファイルをマルチスレッドで高速に読み込む方法
□投稿者/ 金尾 (31回)-(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の方が速いです。

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

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




なちゃ さん
ブロッキングキューというものの使い方を教えていただけないでしょうか?

[ 親 83253 / □ Tree ] 返信 編集キー/

▲[ 83315 ] / ▼[ 83324 ]
■83321 / 22階層)  Re[22]: 大量のテキストファイルをマルチスレッドで高速に読み込む方法
□投稿者/ なちゃ (188回)-(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()

[ 親 83253 / □ Tree ] 返信 編集キー/

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

後ほど試してみます。

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

Parallel.Forの方は排他処理を行っているので、
同じ速度になるはずなのに、不思議です・・・

[ 親 83253 / □ Tree ] 返信 編集キー/

▲[ 83324 ] / ▼[ 83329 ] ▼[ 83327 ]
■83325 / 24階層)  Re[24]: 大量のテキストファイルをマルチスレッドで高速に読み込む方法
□投稿者/ 金尾 (33回)-(2017/03/17(Fri) 11:40:01)
510MB/sではなく560MB/sでした
[ 親 83253 / □ Tree ] 返信 編集キー/

▲[ 83325 ] / 返信無し
■83329 / 25階層)  Re[25]: 大量のテキストファイルをマルチスレッドで高速に読み込む方法
□投稿者/ なちゃ (189回)-(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()

[ 親 83253 / □ Tree ] 返信 編集キー/

▲[ 83325 ] / ▼[ 83328 ] ▼[ 83331 ] ▼[ 83338 ]
■83327 / 25階層)  Re[25]: 大量のテキストファイルをマルチスレッドで高速に読み込む方法
□投稿者/ 金尾 (35回)-(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で方法によってここまで速度差が見られるのはなぜなのでしょうか?





[ 親 83253 / □ Tree ] 返信 編集キー/

▲[ 83327 ] / 返信無し
■83328 / 26階層)  Re[26]: 大量のテキストファイルをマルチスレッドで高速に読み込む方法
□投稿者/ 金尾 (36回)-(2017/03/17(Fri) 12:03:55)
もしかしてデータが読み込まれていないのではないかと思い
読み込んだデータを出力してもみましたが
両方とも正常に読み込むことができています。
 
[ 親 83253 / □ Tree ] 返信 編集キー/

▲[ 83327 ] / ▼[ 83333 ]
■83331 / 26階層)  Re[26]: 大量のテキストファイルをマルチスレッドで高速に読み込む方法
□投稿者/ むー (1回)-(2017/03/17(Fri) 12:14:07)
No83327 (金尾 さん) に返信
> 方法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)


これらは同じではありませんよね
方法2はロックオブジェクトを共有していないので
排他がされていませんよ、複数のファイルが並行で読み取られてます

なので
・SSDは並行で読み取ったら速くなる
・HDDは並行で読み取ったら遅くなる
ということだと思います

[ 親 83253 / □ Tree ] 返信 編集キー/

▲[ 83328 ] / ▼[ 83332 ]
■83330 / 27階層)  Re[27]: 大量のテキストファイルをマルチスレッドで高速に読み込む方法
□投稿者/ なちゃ (190回)-(2017/03/17(Fri) 12:11:10)
No83328 (金尾 さん) に返信
> もしかしてデータが読み込まれていないのではないかと思い
> 読み込んだデータを出力してもみましたが
> 両方とも正常に読み込むことができています。

単にシーケンシャルに処理を書いた場合、ファイル読み込みが終わってから次のリードでファイル転送を開始するまでに空きが入るため、SSDだとマックスの効率にならないとか(この理由だとブロッキングキュー使ったものでも同じ問題あるのでさらに工夫が必要)。
HDDの場合、逆に同時オープンすることで物理シークが発生して効率悪化という可能性がありますが、同時オープンしてなくても変わらないという話もある感じなので何ともいえません。
ディスク処理の最適化ってなかなか難しいです。

[ 親 83253 / □ Tree ] 返信 編集キー/

▲[ 83330 ] / 返信無し
■83332 / 28階層)  Re[28]: 大量のテキストファイルをマルチスレッドで高速に読み込む方法
□投稿者/ なちゃ (191回)-(2017/03/17(Fri) 12:16:48)
あ、パラレルの方はスレッド使いすぎて、ファイル転送用のシステムスレッドが効率的に動けてないとかもあるかも、分かりませんが。
Forのパラメータで最大並列数を適度に絞ってみた方が良いかもしれません。
[ 親 83253 / □ Tree ] 返信 編集キー/

▲[ 83331 ] / ▼[ 83336 ]
■83333 / 27階層)  Re[27]: 大量のテキストファイルをマルチスレッドで高速に読み込む方法
□投稿者/ なちゃ (192回)-(2017/03/17(Fri) 12:18:53)
No83331 (むー さん) に返信
> ■No83327 (金尾 さん) に返信
>>方法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)
>
>
> これらは同じではありませんよね
> 方法2はロックオブジェクトを共有していないので
> 排他がされていませんよ、複数のファイルが並行で読み取られてます
>
> なので
> ・SSDは並行で読み取ったら速くなる
> ・HDDは並行で読み取ったら遅くなる
> ということだと思います

あーほんとですね、これだとHDDで悪くなるのはいかにもありそうです(理由はつまり先に書いたのと同じ)。
[ 親 83253 / □ Tree ] 返信 編集キー/

▲[ 83333 ] / ▼[ 83339 ] ▼[ 83337 ]
■83336 / 28階層)  Re[28]: 大量のテキストファイルをマルチスレッドで高速に読み込む方法
□投稿者/ 金尾 (37回)-(2017/03/17(Fri) 12:43:36)
ありがとうございます。

納得しました。
そういうことでしたか。

どうも、HDDとSSDで取得方法を変えた方が効率が良さそうです。

指定したドライブがHDDかSSDかを調べたいのですが
どのようなコードで調べることができますか?

ざっと検索して調べてみたのですが
分かりませんでしたので教えてください。
 
[ 親 83253 / □ Tree ] 返信 編集キー/

▲[ 83336 ] / 返信無し
■83339 / 29階層)  Re[29]: 大量のテキストファイルをマルチスレッドで高速に読み込む方法
□投稿者/ 魔界の仮面弁士 (1199回)-(2017/03/17(Fri) 13:23:52)
No83336 (金尾 さん) に返信
> どうも、HDDとSSDで取得方法を変えた方が効率が良さそうです。

やるとしても、HDD か SSD かではなく、「SSD かどうか」の判定の方が良いかも。

それ以外のケース、たとえばネットワークドライブや
ソフトウェアRAIDなど、判定できないケースもあるでしょうし。


> 指定したドライブがHDDかSSDかを調べたいのですが
> どのようなコードで調べることができますか?
この手の判断なら、WMI の出番でしょうね。

Windows 8 以降なら、MSFT_PhysicalDisk クラスの
MediaType プロパティ(3=HDD,4=SSD,5=SCM,0=Unspecified)で判断できます。
USB 接続タイプのものだと、SSD/HDD共に 0 が返されることもありますので、
その場合は SpindleSpeed プロパティでディスクの回転数も確認してみて下さい。
SSD なら 0 が返ります。(回転数情報が無い場合は UInt32.MaxValue)

https://msdn.microsoft.com/en-us/library/hh830532.aspx
[ 親 83253 / □ Tree ] 返信 編集キー/

▲[ 83336 ] / 返信無し
■83337 / 29階層)  Re[29]: 大量のテキストファイルをマルチスレッドで高速に読み込む方法
□投稿者/ 774RR (489回)-(2017/03/17(Fri) 13:11:39)
質問内容が変わってきたらスレッドを更新してくれると読者が楽できるような気がする。

WMI で取れるはず。回転速度が 0rpm だったら SSD
http://8thway.blogspot.jp/2014/04/wmi-ssd.html

# ネットワークディスクのときどうすんだろ?

[ 親 83253 / □ Tree ] 返信 編集キー/

▲[ 83327 ] / ▼[ 83340 ]
■83338 / 26階層)  Re[26]: 大量のテキストファイルをマルチスレッドで高速に読み込む方法
□投稿者/ PANG2 (164回)-(2017/03/17(Fri) 13:18:04)
2017/03/17(Fri) 13:31:51 編集(投稿者)
No83327 (金尾 さん) に返信
> やっていることは同じなのに
> なぜかSSDとHDDで方法によってここまで速度差が見られるのはなぜなのでしょうか?

ファイル読込がボトルネックでしたっけ?
振り出しに戻って、まずはシングルスレッドで計測してボトルネックを見つけて下さい。

--
    Sub Main()
        Dim sw1 As New Stopwatch
        Dim sw2 As New Stopwatch
        Dim sw3 As New Stopwatch
        Dim sw4 As New Stopwatch

        For i As Integer = 1 To 10000
            sw1.Start()
            '@メモリ確保
            Dim bs(100 * 1024 * 1024 - 1) As Byte
            sw1.Stop()

            sw2.Start()
            'Aファイル読み込み
            sw2.Stop()

            sw3.Start()
            'Bbyte配列を文字列配列へ変換
            sw3.Stop()

            sw4.Start()
            'Cデータをまとめるための処理
            sw4.Stop()
        Next
        Console.WriteLine(sw1.ElapsedMilliseconds)
        Console.WriteLine(sw2.ElapsedMilliseconds)
        Console.WriteLine(sw3.ElapsedMilliseconds)
        Console.WriteLine(sw4.ElapsedMilliseconds)
    End Sub

[ 親 83253 / □ Tree ] 返信 編集キー/

▲[ 83338 ] / ▼[ 83345 ]
■83340 / 27階層)  Re[27]: 大量のテキストファイルをマルチスレッドで高速に読み込む方法
□投稿者/ 魔界の仮面弁士 (1200回)-(2017/03/17(Fri) 13:30:27)
No83338 (PANG2 さん) に返信
> ファイル読込がボトルネックでしたっけ?
> まずはシングルスレッドで計測してボトルネックを見つけて下さい。

自分も、ボトルネックの再計測が必要という案に一票。

それと、ファイルによって処理時間が異なるでしょうから、
各区間ごとの累計時間だけでなく、各回の最短時間/最長秒数/平均時間も
区間ごとに集計しておいた方が良いかと思います。
[ 親 83253 / □ Tree ] 返信 編集キー/

▲[ 83340 ] / 返信無し
■83345 / 28階層)  Re[28]: 大量のテキストファイルをマルチスレッドで高速に読み込む方法
□投稿者/ 金尾 (38回)-(2017/03/18(Sat) 17:23:25)
ほぼ全ての問題が解決しました
みなさまどうもありがとうございました。
 
解決済み
[ 親 83253 / □ Tree ] 返信 編集キー/


管理者用

- Child Tree -