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

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

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

大量のテキストファイルをマルチスレッドで高速に読み込む方法 [1]

[トピック内 69 記事 (21 - 40 表示)]  << 0 | 1 | 2 | 3 >>

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

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


これは私も考えていたところです。
1次元のバイト配列で
ある要素からある要素までのみをstring化するにはどうしたら良いですか?
一度別の小さな配列にforループでコピーする必要がありますか?
 
引用返信 編集キー/
■83276 / inTopicNo.22)  Re[5]: 大量のテキストファイルをマルチスレッドで高速に読み込む方法
□投稿者/ 魔界の仮面弁士 (1191回)-(2017/03/16(Thu) 11:22:45)
No83275 (金尾 さん) に返信
> 一度別の小さな配列にforループでコピーする必要がありますか?
部分コピーを作らないで下さい。「無駄な配列を作らない」ことが目的なので。

> 1次元のバイト配列で
> ある要素からある要素までのみをstring化するにはどうしたら良いですか?
Encoding クラスの GetString メソッドには、
引数 1 個の「bytes」なオーバーロードだけではなく、
引数 3 個の「bytes, index, count」なオーバーロードもあります。
これを使えば、バイナリの一部のみを文字列化できるかと。
引用返信 編集キー/
■83277 / inTopicNo.23)  Re[6]: 大量のテキストファイルをマルチスレッドで高速に読み込む方法
□投稿者/ 金尾 (16回)-(2017/03/16(Thu) 11:26:49)
ありがとうございます。

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

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

vbcrlfはバイトにしたときにどういう数値になりますか?
引用返信 編集キー/
■83278 / inTopicNo.24)  Re[7]: 大量のテキストファイルをマルチスレッドで高速に読み込む方法
□投稿者/ 金尾 (17回)-(2017/03/16(Thu) 11:34:24)
試しに
vbcrlfを検索せずにforループで行数分splitしてみましたが
5%程度しか速くなりませんでした。
 
引用返信 編集キー/
■83279 / inTopicNo.25)  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 かどうかを
追加チェックすればことで対応してみて下さい。
引用返信 編集キー/
■83280 / inTopicNo.26)  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

となるような実装を用意すれば十分ではなかろうか、という意味です。
引用返信 編集キー/
■83281 / inTopicNo.27)  Re[9]: 大量のテキストファイルをマルチスレッドで高速に読み込む方法
□投稿者/ 金尾 (18回)-(2017/03/16(Thu) 12:06:02)
ありがとうございます。

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

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

vbcrなら13、vblfなら10といったことを調べられる関数を探しています
 
引用返信 編集キー/
■83283 / inTopicNo.28)  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 バイトに符号化されます。
引用返信 編集キー/
■83284 / inTopicNo.29)  Re[11]: 大量のテキストファイルをマルチスレッドで高速に読み込む方法
□投稿者/ 金尾 (19回)-(2017/03/16(Thu) 12:25:57)
とりあえず、全てのコードを文字列化する方法で
高速化できるか試してみたのですが
SSDだと2倍程度高速化できましたが
HDDだと10%程度逆に遅くなってしまいました。

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

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

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

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

なちゃさんが途中で示唆されていたと思いますが、同じファイルの読み込みを繰り返すと、OS などによって、メモリにキャッシュされるので HDD と SSD の差がなくなるように見えることがあります。
ただ、所詮はキャッシュなのでいつかはなくなり、ストレージの差が出ます。
これは、マルチスレッドにしようと、メモリをどうしようとも変わりません。(打つ手がない)
引用返信 編集キー/
■83288 / inTopicNo.32)  Re[13]: 大量のテキストファイルをマルチスレッドで高速に読み込む方法
□投稿者/ 金尾 (21回)-(2017/03/16(Thu) 12:39:50)
すいません
「欧文」の件は解決しました
ファイル読み込み時にバイナリデータとして読んでいるので
この当たりは速度と無関係でした

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

それが、Window-1252 と呼ばれるエンコードなら、Encoding.GetEncoding(1252) で取れるものだったはず。(スマホからなのでテストしてません)
引用返信 編集キー/
■83292 / inTopicNo.34)  Re[14]: 大量のテキストファイルをマルチスレッドで高速に読み込む方法
□投稿者/ 金尾 (22回)-(2017/03/16(Thu) 13:20:50)
Azuleanさん

ありがとうございます。

ちなみにメモリは毎回RamMapというソフトでクリアしていますので
この影響は排除できています
 
引用返信 編集キー/
■83293 / inTopicNo.35)  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

引用返信 編集キー/
■83295 / inTopicNo.36)  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を使わずに書くしかないでしょうか?
もっと良い方法があれば教えてください。


引用返信 編集キー/
■83297 / inTopicNo.37)  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 内部で同時実行制御ができてれば大丈夫です(データ取りまとめ処理部分の実装がわからないので我々には不明ですが)。

引用返信 編集キー/
■83298 / inTopicNo.38)  Re[17]: 大量のテキストファイルをマルチスレッドで高速に読み込む方法
□投稿者/ 金尾 (24回)-(2017/03/16(Thu) 16:53:44)
ありがとうございます。

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

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

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

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



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

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

あと複数タスクの待機は、Task.WaitAll使いましょう。
引用返信 編集キー/

このトピックをツリーで一括表示

<前の20件 | 次の20件>
トピック内ページ移動 / << 0 | 1 | 2 | 3 >>

管理者用

- Child Tree -