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

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

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

No.83387 の関連記事表示

<< 0 | 1 >>
■83387  巨大なテキストファイルを高速で出力する方法
□投稿者/ バカボドン -(2017/03/20(Mon) 16:51:51)

    分類:[.NET 全般] 

    2017/03/20(Mon) 16:53:45 編集(投稿者)
    2017/03/20(Mon) 16:53:06 編集(投稿者)
    <pre><pre>2017/03/20(Mon) 16:52:46 編集(投稿者)
    2017/03/20(Mon) 16:52:43 編集(投稿者)

    <pre><pre>
    以下のコードで巨大なSingle配列を
    数百MByte程度のサイズの一列のテキストファイルとして出力するコードを書きました。

    Dim a(xmax, ymax, zmax) As Single

    Using writer As New StreamWriter(Path, False, Encoding.ASCII)

    For zzz As Integer = 1 To zmax
    For yyy As Integer = 1 To ymax
    For xxx As Integer = 1 To xmax

    writer.WriteLine(a(xxx, yyy, zzz).ToString)

    Next xxx
    Next yyy
    Next zzz

    End Using

    ただ、タスクマネージャーで確認すると
    ストレージの帯域は5%程度しか使っておらず
    CPU使用率も10%程度となっています。

    これをマルチスレッド化して高速出力したいのですが
    どのようにすれば良いですか?

    Single→String→Byteという2段階のデータ変換を行っていることになるので
    どちらが律速しているか調べるために
    以下のマルチスレッドコードを書いて計算時間を比較しました。



    For zzz As Integer = 1 To zmax

    Dim XY(xmax, ymax) As String

    Dim zzz2 As Integer = zzz

    Parallel.For(1, ymax + 1, Sub(yyy)

    For xxx As Integer = 1 To xmax

    XY(xxx, yyy) = a(xxx, yyy, zzz2).ToString

    Next xxx

    End Sub)

    For yyy As Integer = 1 To ymax
    For xxx As Integer = 1 To xmax

    writer.WriteLine(XY(xxx, yyy))

    Next xxx
    Next yyy
    Next zzz

    しかし計算時間は全く変わりませんでした。
    そのため、String→Byteの変換が律速していると考えています。


    Dim bytesData As Byte() = System.Text.Encoding.GetEncoding(932).GetBytes(str)

    というコードで、String→Byte配列への変換を行うことができるのは分かるのですが、
    どのようにマルチスレッド化すれば良いでしょうか?

    ファイルサイズが数百MByte程度になるので
    一度に全てをバイト配列にして最後に出力するというのは避けたいです。


    For zzz As Integer = 1 To zmax
    の部分を4つくらいのスレッドの分離して
    マルチスレッドで処理するのがもっとも適当でしょうか?



    </pre></pre></pre></pre>
親記事 /過去ログ142より / 関連記事表示
削除チェック/

■83390  Re[1]: 巨大なテキストファイルを高速で出力する方法
□投稿者/ Azulean -(2017/03/20(Mon) 20:44:40)
    2017/03/20(Mon) 21:59:51 編集(投稿者)

    No83387 (バカボドン さん) に返信
    > ただ、タスクマネージャーで確認すると
    > ストレージの帯域は5%程度しか使っておらず
    > CPU使用率も10%程度となっています。

    そういう測り方をするのではなく、Visual Studio のパフォーマンス分析機能などのプロファイリングツールを使い、シングルスレッドで解決不可能と判断できる根拠を得るところからでしょう。
    最近のスレッドにも書かれていますが、並列化すれば速くなるとは限りませんので、分析が先です。
    そして、タスクマネージャでは自分のコードのどこが原因かわからないので、分析のためのツールとしては不適当です。

    > これをマルチスレッド化して高速出力したいのですが
    > どのようにすれば良いですか?

    今のコードからすると無理かと思います。
    文字列化で長さが不定ですし、分散して1つの連続したファイルにするのは無茶です。
    (Windows プログラムとしては、ファイルの任意の位置にバイト列を差し込むことも、ファイルの中にある無駄な隙間を詰めることもできないので、長さ不定のブロックを複数スレッドで同一ファイルに書き込んで、連続した1ファイルにすることは実現できない)


    ところで、派生ネタで質問される際は同じ名前にしていただいた方がよいです。
    前に話した内容をお互いに前提にできるので楽です。
    (さすがに、こんな短期間でこのような特徴的なものが別人である可能性は低そうなので...)
記事No.83387 のレス /過去ログ142より / 関連記事表示
削除チェック/

■83391  Re[2]: 巨大なテキストファイルを高速で出力する方法
□投稿者/ なちゃ -(2017/03/20(Mon) 23:01:15)
    ぞもそも数百NB程度だと多分ほぼ遅延書き込みになってしまうので、ディスク帯域はあまり参考になりません。
    これはディスク処理をCPU処理で肩代わりしているともいえる状況ですので、マルチスレッド化できる部分で早くできるとは限りません。
    実際このシンプルなコードであまり工夫する価値はないと思います。
    最終的にディスク書き込みするバイト情報の作成と、書込みの実行を並列に行えば多少は高速化できるかもしれませんが。
記事No.83387 のレス /過去ログ142より / 関連記事表示
削除チェック/

■83395  Re[1]: 巨大なテキストファイルを高速で出力する方法
□投稿者/ shu -(2017/03/20(Mon) 23:32:57)
    No83387 (バカボドン さん) に返信

    > ファイルサイズが数百MByte程度になるので
    > 一度に全てをバイト配列にして最後に出力するというのは避けたいです。
    この考え方が一番速く出来る可能性が高いと思います。
    一度に出来ないならいくつかにわければよいのではないでしょうか?
    例えば10MB単位で書くとか。

    10MB×2のバイト配列を用意して
    (1) 1つめのバイト配列に書き込む
    (2) 1つめのバイト配列をファイルに書き込む
    (3) 2つめのバイト配列に書き込む((2)とスレッドを分けることが可能)
    (4) 2つめのバイト配列をファイルに書き込む((2)を処理したスレッドが(3)での処理が終了していることを確認して書き込む)
    同様に繰り返す

    とすれば2つのスレッドで多少は速度を稼げそうな気もします。同期をうまくとる必要があります。
    用意出来る配列の本数が多ければ配列への書き込み自体は並列度が上がりますが書き込み待ち制御が大変にはなるでしょう。

    また今回の処理がSingle値の文字列化であり配列に収まるデータ数が可変になってしまうので配列の大きさを固定化しないで
    データ数で分けたほうがやりやすいかもしれません。
記事No.83387 のレス /過去ログ142より / 関連記事表示
削除チェック/

■83393  Re[3]: 巨大なテキストファイルを高速で出力する方法
□投稿者/ なちゃ -(2017/03/20(Mon) 23:20:52)
    あと、そうやって工夫したところで、遅延書き込みの状況次第で、わずかでもストールすると全体にかかる時間は全然変わってきますので、工夫で変わるレベルなんてほぼ無意味になります。
記事No.83387 のレス /過去ログ142より / 関連記事表示
削除チェック/

■83394  Re[4]: 巨大なテキストファイルを高速で出力する方法
□投稿者/ バカボドン -(2017/03/20(Mon) 23:31:31)
    No83393 (なちゃ さん) に返信
    > 最終的にディスク書き込みするバイト情報の作成と、書込みの実行を並列に行えば多少は高速化できるかもしれませんが。

    できれば、これを試してみたいと考えています。
    もしかするとあまり速くならないかも知れませんが
    どう改良すべきかお教え願えないでしょうか?
     
記事No.83387 のレス /過去ログ142より / 関連記事表示
削除チェック/

■83401  Re[5]: 巨大なテキストファイルを高速で出力する方法
□投稿者/ なちゃ -(2017/03/21(Tue) 12:29:22)
    ちなみに、10MB単位とかで書き込みを行うと、激烈に遅くなります。
    ※遅延書き込みが無効になるため

    経験的には256KB程度までにする方がよい。
記事No.83387 のレス /過去ログ142より / 関連記事表示
削除チェック/

■83402  Re[6]: 巨大なテキストファイルを高速で出力する方法
□投稿者/ バカボドン -(2017/03/21(Tue) 13:19:14)
    ご回答ありがとうございます。
    できれば、具体的なコードを示していただけないでしょうか?
    よろしくお願いいたします。
記事No.83387 のレス /過去ログ142より / 関連記事表示
削除チェック/

■83403  Re[7]: 巨大なテキストファイルを高速で出力する方法
□投稿者/ もりお -(2017/03/21(Tue) 20:47:23)
    面白そうですね、私もちょっと書いてみようかしら。

    ・GetBytesとGetBytesの非同期化
    ・GetBytesとWriteLineの非同期化

    2つの非同期化があり、それぞれでどれだけ速くなるでしょうか、
    2つを組み合わせたときにどれだけ速くなるでしょうか、
    というのがわかればいい感じですかね。

    まずは比較の軸になるシングルスレッドのプログラムを
    こしらえないといけないですね。

    JITコンパイルは切った方が良さそうな気がしますが
    アノテーションでなんかあれできたような。
記事No.83387 のレス /過去ログ142より / 関連記事表示
削除チェック/

■83404  Re[8]: 巨大なテキストファイルを高速で出力する方法
□投稿者/ バカボドン -(2017/03/21(Tue) 21:01:30)
    No83403 (もりお さん) に返信
    > 面白そうですね、私もちょっと書いてみようかしら。
    >

    ぜひともよろしくお願いいたします。
    プロの方がどういう書き方をするのか
    手本にさせていただきたいと考えております。
     
記事No.83387 のレス /過去ログ142より / 関連記事表示
削除チェック/

■83405  Re[9]: 巨大なテキストファイルを高速で出力する方法
□投稿者/ もりお -(2017/03/21(Tue) 21:13:39)
    No83404 (バカボドン さん) に返信

    やめて、ハードル上げるのやめて。
    プロはまずVisual Studio 2017 コミュニティエディションをインストールします。
    荒らしと区別つかないので実況はこれでやめようと思いますが、ちょっと書いてみます。
記事No.83387 のレス /過去ログ142より / 関連記事表示
削除チェック/

■83406  Re[10]: 巨大なテキストファイルを高速で出力する方法
□投稿者/ バカボドン -(2017/03/21(Tue) 21:47:48)
    No83405 (もりお さん) に返信
    素晴らしいプログラムコードをお待ちしておりますので
    どうぞよろしくお願いいたします。
記事No.83387 のレス /過去ログ142より / 関連記事表示
削除チェック/

■83408  Re[11]: 巨大なテキストファイルを高速で出力する方法
□投稿者/ もりお -(2017/03/22(Wed) 01:29:21)
    2017/03/22(Wed) 01:37:36 編集(投稿者)

    No83406 (バカボドン さん) に返信

    書きました
    https://paiza.io/projects/1OxpG-adPgJKBpkwDhgBzg

    Parallel.Forを使えば単純に速くなるというものでもありませんでした。
    外側のループを増やすと非同期の方が遅くなります。
    Parallel.Forを呼び出す回数を減らさないと裏目にでますねこれ。

    書き込みの方も同様で外側のループを減らすと非同期の方が速くなりました。



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

■83410  Re[12]: 巨大なテキストファイルを高速で出力する方法
□投稿者/ バカボドン -(2017/03/22(Wed) 09:22:08)
    素晴らしい計算コードをありがとうございます。

    仰る通り、
    300,300,300の条件で50%程度
    3,3000,3000の条件で90%程度しか速くなりませんでした。
    3000,3000,3の条件では逆に6分の1程度に速度が低下してしまいました。

    CPU使用率を見ると、マルチスレッド動作がうまくいっているので
    数倍程度違いが見られるのに、その違いは全く計算時間に反映されていないことが分かりました。

    SSDとHDDも比較しましたが、全く差異は見られませんでした。

    もう少しいろいろと試してからコメントさせていただきます。
記事No.83387 のレス /過去ログ142より / 関連記事表示
削除チェック/

■83412  Re[13]: 巨大なテキストファイルを高速で出力する方法
□投稿者/ バカボドン -(2017/03/22(Wed) 09:30:59)
    ところで生成されたtest.txtファイルの中身が空になっているのですが
    これは一体なぜでしょうか?
     
記事No.83387 のレス /過去ログ142より / 関連記事表示
削除チェック/

■83413  Re[14]: 巨大なテキストファイルを高速で出力する方法
□投稿者/ バカボドン -(2017/03/22(Wed) 09:41:40)
    あと、WriteLineとの方法とも比較してみましたが、
    外側のループのサイズに依存せず、WriteLineの方が常に20%程度遅くなる結果が得られました
    やはり、少しずつ細切れに出力するように
    一気に出力する方が良いのでしょうか・・・?
記事No.83387 のレス /過去ログ142より / 関連記事表示
削除チェック/

■83414  Re[15]: 巨大なテキストファイルを高速で出力する方法
□投稿者/ バカボドン -(2017/03/22(Wed) 10:18:30)
    どうも、元のコードだと
    GetBytesAndWriteSync
    がファイルを書き込むコードになっていないようでした。
    
    あと、
    外側のループを減らすために、以下のようにxyまるごと配列にして出力する方法も試してみましたが
    なぜか10倍くらい遅くなってしまいました
    
    
    
    
        <MethodImpl(MethodImplOptions.NoOptimization)>
        Sub GetBytesAndWriteASync4(ByVal a(,,) As Single, ByVal enc As Encoding, ByVal path As String)
            Using stream As New FileStream(path, FileMode.Create)
    
    
                For z As Integer = 0 To a.GetUpperBound(0)
    
    
                        Dim buf(a.GetUpperBound(1))() As Byte
    
    
                        Dim zz As Integer = z
    
                        Parallel.For(0, buf.Length,
                                     Sub(y)
                                    buf(y) = {}
                                    For x As Integer = 0 To a.GetUpperBound(2)
    
                                        buf(y) = buf(y).Concat(enc.GetBytes(a(zz, y, x).ToString())).ToArray()
    
                                    Next
    
                                     End Sub)
    
                       For Each b In buf
                            stream.Write(b, 0, b.Length)
                            stream.WriteByte(&HD)       'CR
                            stream.WriteByte(&HA)       'LF
                      Next b
    
    
                Next z
    
    
            End Using
        End Sub
    
    
記事No.83387 のレス /過去ログ142より / 関連記事表示
削除チェック/

■83418  Re[16]: 巨大なテキストファイルを高速で出力する方法
□投稿者/ PANG2 -(2017/03/22(Wed) 13:53:27)
    2017/03/22(Wed) 14:18:07 編集(投稿者)

    No83414 (バカボドン さん) に返信
    > 外側のループを減らすために、以下のようにxyまるごと配列にして出力する方法も試してみましたが
    > なぜか10倍くらい遅くなってしまいました

    改良案
    Sub GetBytesAndWriteASync5(ByVal a(,,) As Single, ByVal enc As Encoding, ByVal path As String)
    Using stream As New StreamWriter(path, FileMode.Create)
    For z As Integer = 0 To a.GetUpperBound(0)
    Dim buf(a.GetUpperBound(1)) As StringWriter
    Dim zz As Integer = z
    Parallel.For(0, buf.Length,
    Sub(y)
    buf(y) = New StringWriter()
    For x As Integer = 0 To a.GetUpperBound(2)
    buf(y).WriteLine(a(zz, y, x))
    Next
    End Sub
    )

    For Each b In buf
    stream.Write(b.ToString())
    Next b
    Next z
    End Using
    End Sub

    追記
    For Each b In buf
    をTask化すれば、さらに速くなる
記事No.83387 のレス /過去ログ142より / 関連記事表示
削除チェック/

■83420  Re[17]: 巨大なテキストファイルを高速で出力する方法
□投稿者/ バカボドン -(2017/03/22(Wed) 14:28:54)

    改良案ありがとうございます。

    配列サイズが大きい時に高速で処理できることが分かりました


    ところで、いろいろと試していて気がついたことなのですが


    Dim zmax As Integer = 1
    Dim ymax As Integer = 30
    Dim xmax As Integer = 30

    の時、
    GetBytesAndWriteSync 0.0013647秒
    GetBytesAndWriteASync5 0.0476627秒
    と40倍くらいの差が出ます。

    一方で、

    Dim zmax As Integer = 300
    Dim ymax As Integer = 30
    Dim xmax As Integer = 30

    の時、
    GetBytesAndWriteSync 0.1473秒
    GetBytesAndWriteASync5 0.1095秒

    のように差異が狭まり1.5倍程度しか差が出なくなります。

    xmaxやymaxを大きくすると
    並列化処理が有効になるので、
    GetBytesAndWriteASync5が高速になるのは予想できることですが
    xmaxやymaxを固定したまま
    zmaxを大きくしても
    GetBytesAndWriteASync5が高速になるのはなぜなのでしょうか?

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

■83421  Re[18]: 巨大なテキストファイルを高速で出力する方法
□投稿者/ なちゃ -(2017/03/22(Wed) 15:29:53)
    > xmaxやymaxを大きくすると
    > 並列化処理が有効になるので、
    > GetBytesAndWriteASync5が高速になるのは予想できることですが
    > xmaxやymaxを固定したまま
    > zmaxを大きくしても
    > GetBytesAndWriteASync5が高速になるのはなぜなのでしょうか?

    高速になったというよりは、オーバーヘッドを相殺できるようになったということじゃないかと思います。
    処理時間が短すぎて、ライブラリのロードとかJITコンパイルによるオーバーヘッドが大きな割合を占めてしまっているというような意味です。
記事No.83387 のレス /過去ログ142より / 関連記事表示
削除チェック/

次の20件>

<< 0 | 1 >>

パスワード/

- Child Tree -