9

C#(.NET 4.0)で記述された.NETアプリケーションがあります。このアプリケーションでは、ファイルから大きなデータセットを読み取り、その内容をグリッドのような構造で表示する必要があります。そこで、これを実現するために、フォームにDataGridViewを配置しました。3つの列があり、すべての列データはファイルから取得されます。当初、ファイルには、DataGridViewの600.000行に対応する約600.000レコードが含まれていました。

DataGridViewがこのような大きなデータセットで崩壊することにすぐに気づいたので、仮想モードに切り替えました。これを実現するために、最初にファイルを3つの異なる配列(3つの列に対応)に完全に読み取り、次にCellValueNeededイベントが発生し、配列から正しい値を提供します。

ただし、すぐにわかったように、このファイルには膨大な数のレコードが含まれている可能性があります。レコードサイズが非常に大きい場合、すべてのデータを配列またはリスト<>などに読み込むことは実行不可能であるように見えます。すぐにメモリ割り当てエラーが発生します。(メモリ不足の例外)。

私たちはそこで立ち往生しましたが、なぜ最初にすべてのデータを配列に読み込むのか、CellValueNeededイベントが発生したときにオンデマンドでファイルを読み込むのはなぜですか?これが現在の作業です。ファイルを開きますが、何も読み取りません。CellValueNeededイベントが発生すると、最初にファイル内の正しい位置にSeek()を実行し、次に対応するデータを読み取ります。

これは私たちが思いつくことができる最高のものですが、まず第一に、これは非常に遅いため、アプリケーションが遅くなり、ユーザーフレンドリーではありません。第二に、これを達成するためのより良い方法がなければならないと考えざるを得ません。たとえば、一部のバイナリエディタ(HXDなど)は、どのファイルサイズでも目がくらむほど高速なので、これを実現する方法を知りたいと思います。

さらに、DataGridViewの仮想モードで、RowCountをファイル内の使用可能な行数(たとえば16.000.000)に設定すると、DataGridViewがそれ自体を初期化するまでに時間がかかります。この「問題」についてのコメントもいただければ幸いです。

ありがとう

4

5 に答える 5

5

データセット全体をメモリに収めることができない場合は、バッファリングスキームが必要です。DataGridViewアプリケーションは、に応答して入力するために必要な量のデータだけを読み取るのではなくCellValueNeeded、ユーザーのアクションを予測して先読みする必要があります。したがって、たとえば、プログラムが最初に起動したときに、最初の10,000レコード(または、1,000レコードまたは100,000レコードのみ-あなたの場合は妥当なもの)を読み取る必要があります。その後、CellValueNeeded要求はメモリからすぐに満たすことができます。

ユーザーがグリッド内を移動するとき、プログラムは可能な限りユーザーの一歩先を行きます。ユーザーがあなたの前にジャンプし(たとえば、前から最後にジャンプしたい場合)、要求を満たすためにディスクに出向かなければならない場合、短い一時停止が発生する可能性があります。

このバッファリングは通常、別のスレッドで行うのが最適ですが、ユーザーの次のアクションを見越してスレッドが先読みしている場合、同期が問題になることがあります。その後、ユーザーはリストの先頭にジャンプするなど、まったく予期しないことを行います。

レコードが非常に大きくない限り、1600万レコードは実際にはメモリに保持するレコードの数だけではありません。または、サーバーに多くのメモリがない場合。確かに、値型(構造)List<T>でない限り、1600万はの最大サイズにはほど遠いです。Tここで何ギガバイトのデータについて話しているのですか?

于 2011-01-26T16:49:50.463 に答える
4

さて、これがはるかにうまくいくように見える解決策です:

ステップ0:dataGridView.RowCountを低い値、たとえば25(またはフォーム/画面に収まる実際の数)に設定します

手順1:dataGridViewのスクロールバーを無効にします。

手順2:独自のスクロールバーを追加します。

ステップ3:CellValueNeededルーチンで、e.RowIndex+scrollBar.Valueに応答します

ステップ4:データストアに関しては、現在Streamを開いており、CellValueNeededルーチンで、最初に必要なデータのSeek()とRead()を実行します。

これらの手順により、非常に大きなファイル(最大0.8GBでテスト済み)のdataGridをスクロールする非常に妥当なパフォーマンスが得られます。

したがって、結論として、速度低下の実際の原因は、Seek()とRead()を維持したという事実ではなく、実際のdataGridView自体であるように見えます。

于 2011-01-27T14:35:04.233 に答える
1

ロールアップ、小計、複数列の計算などで使用できる行と列の管理には、固有の一連の課題があります。問題を編集者が遭遇する問題と比較するのは本当に公平ではありません。サードパーティのデータグリッドコントロールは、VB6の時代から、クライアント側で大規模なデータセットを表示および操作する問題に対処してきました。ロードオンデマンドまたは自己完結型のクライアント側のgarguantuanデータセットを使用して、非常に優れたパフォーマンスを実現することは簡単な作業ではありません。ロードオンデマンドは、サーバー側の遅延の影響を受ける可能性があります。クライアントでデータセット全体を操作すると、メモリとCPUの制限に悩まされる可能性があります。ジャストインタイムの読み込みをサポートするサードパーティのコントロールの中には、クライアント側とサーバー側の両方のロジックを提供するものもあれば、問題を100%クライアント側で解決しようとするものもあります。

于 2011-01-26T17:05:15.327 に答える
1

.netはネイティブOSの上に階層化されているため、ディスクからメモリへのデータのランタイムロードと管理には別のアプローチが必要です。理由と方法をご覧ください:http://www.codeproject.com/Articles/38069/Memory-Management-in-NET

于 2012-01-30T21:20:47.307 に答える
0

この問題に対処するには、すべてのデータを一度にロードしないことをお勧めします。代わりに、データをチャンクでロードし、必要に応じて最も関連性の高いデータを表示します。簡単なテストを行ったところ、aのDataSourceプロパティを設定するのDataGridViewが良い方法であることがわかりましたが、行数が多いと時間がかかります。したがってMerge、DataTableの関数を使用して、データをチャンクでロードし、ユーザーに最も関連性の高いデータを表示します。ここで私はあなたを助けることができる例を示しました。

于 2014-03-30T21:44:17.987 に答える