2

仮想モードとキャッシュを使用してデータを取得するためにSQLiteデータベースを使用する非常に遅いDataGridViewを最適化しようとしていますが、すでにダブルバッファリングのトリックを使用して、列と行の自動サイズ設定を削除しています。ただし、これを読んでいるにもかかわらず:http: //msdn.microsoft.com/en-us/library/ha5xt0d9.aspx およびそれ:http: //msdn.microsoft.com/en-us/library/ha5xt0d9.aspx

私のグリッドは非常に遅いです。実際、DBへのクエリはかなり速いですが、データがすでにキャッシュを介してロードされている場合でも、DataGridViewの描画は非常に遅いようです...

しかし、おそらく私のCacheクラスはそれほど良くないので何か間違ったことをしたのではないかと思います。原則は非常に単純で、キャッシュは3つの部分に分割されます。上限、現在(「中間」、下限とも呼ばれます)、それぞれがインデックス(開始と終了)で区切られます。データが既に読み込まれている場合、キャッシュは次のようになります。これらの非常に単純なルールに従った値:

  • 値が現在の部分に問題がない場合は、データをロードするだけです
  • 必要な値が上か下かである場合、この部分は現在の部分になり、新しい部分だけが必要になります。

すなわち

上部:0-100現在の部分:101-201下部:202-302

必要な値は下部内にあり、問題はありません。電流が低くなり、上部が電流に変わり、新しい下部のみをリロードする必要があります。明らかに、必要な値にキャッシュで使用できない行インデックスがある場合、これは再ロードされます。

public class Cache
{
    private Dictionary<PagePart, Page> _pages;
    public Dictionary<PagePart, Page> Pages
    {
        get { return this._pages; }
        set { this._pages = value; }
    }

    private String _tableName;
    public String TableName
    {
        get { return this._tableName; }
        set { this._tableName = value; }
    }

    private SQLiteConnection _connection;
    public SQLiteConnection Connection
    {
        get { return this._connection; }
        set { this._connection = value; }
    }

    public Cache(String tableName, SQLiteConnection connection)
    {
        this.Connection = connection;
        this.TableName = tableName;

        this.Pages = new Dictionary<PagePart, Page>(PageNumber);

        IndexRange indexRangeUpper = new IndexRange(0, PageSize);
        IndexRange indexRangeCurrent = new IndexRange(PageSize + 1, 2 * PageSize);
        IndexRange indexRangeLower = new IndexRange(2 * PageSize + 1, 3 * PageSize);

        DataTable dataTableUpper = this.GetDataTableFromTable(indexRangeUpper);
        DataTable dataTableCurrent = this.GetDataTableFromTable(indexRangeCurrent);
        DataTable dataTableLower = this.GetDataTableFromTable(indexRangeLower);

        Page pageUpper = new Page(indexRangeUpper, dataTableUpper);
        Page pageCurrent = new Page(indexRangeCurrent, dataTableCurrent);
        Page pageLower = new Page(indexRangeLower, dataTableLower);

        Pages.Add(PagePart.Upper, pageUpper);
        Pages.Add(PagePart.Current, pageCurrent);
        Pages.Add(PagePart.Lower, pageLower);
    }

    private IndexRange GetTableIndexRange()
    {
        String commandText = String.Format("SELECT MAX(RowId) FROM {0}", this.TableName);
        SQLiteCommand command = new SQLiteCommand(commandText, this.Connection);

        this.Connection.Open();
        command.CommandText = commandText;
        String maxRowIdString = command.ExecuteScalar().ToString();
        this.Connection.Close();

        Int32 maxRowId = Int32.Parse(maxRowIdString);

        return new IndexRange(0, maxRowId);
    }

    public Object GetCellValue(Int32 rowIndex, Int32 columnIndex)
    {
        Int32 indexLowerStart = Pages[PagePart.Lower].Range.StartIndex;
        Int32 indexLowerEnd = Pages[PagePart.Lower].Range.EndIndex;

        Int32 indexCurrentStart = Pages[PagePart.Current].Range.StartIndex;
        Int32 indexCurrentEnd = Pages[PagePart.Current].Range.EndIndex;

        Int32 indexUpperStart = Pages[PagePart.Upper].Range.StartIndex;
        Int32 indexUpperEnd = Pages[PagePart.Upper].Range.EndIndex;

        IndexRange indexRangeTable = this.GetTableIndexRange();
        Int32 indexTableStart = indexRangeTable.StartIndex;
        Int32 indexTableEnd = indexRangeTable.EndIndex;

        // Using the cache...
        if ((indexUpperStart <= rowIndex) && (rowIndex <= indexLowerEnd))
        {
            if ((indexLowerStart <= rowIndex) && (rowIndex <= indexLowerEnd))
            {
                if (indexTableEnd > indexLowerEnd)
                {
                    this.Pages[PagePart.Upper] = this.Pages[PagePart.Current];
                    this.Pages[PagePart.Current] = this.Pages[PagePart.Lower];

                    IndexRange indexRangeLower = new IndexRange(this.Pages[PagePart.Current].Range.EndIndex + 1, this.Pages[PagePart.Current].Range.EndIndex + PageSize);
                    DataTable dataTableLower = this.GetDataTableFromTable(indexRangeLower);

                    Page pageLower = new Page(indexRangeLower, dataTableLower);

                    this.Pages[PagePart.Lower] = pageLower;

                    Int32 pageSize = this.Pages[PagePart.Current].Data.Rows.Count;
                    return this.Pages[PagePart.Current].Data.Rows[rowIndex % pageSize][columnIndex];
                }
                else
                {
                    Int32 pageSize = this.Pages[PagePart.Lower].Data.Rows.Count;
                    return this.Pages[PagePart.Lower].Data.Rows[rowIndex % pageSize][columnIndex];
                }
            }

            if ((indexCurrentStart <= rowIndex) && (rowIndex <= indexCurrentEnd))
            {
                Int32 pageSize = this.Pages[PagePart.Current].Data.Rows.Count;
                return this.Pages[PagePart.Current].Data.Rows[rowIndex % pageSize][columnIndex];
            }

            if ((indexUpperStart <= rowIndex) && (rowIndex <= indexUpperEnd))
            {
                if (indexTableStart < indexUpperStart)
                {
                    this.Pages[PagePart.Lower] = this.Pages[PagePart.Current];
                    this.Pages[PagePart.Current] = this.Pages[PagePart.Upper];

                    IndexRange indexRangeUpper = new IndexRange(this.Pages[PagePart.Current].Range.StartIndex - 1, this.Pages[PagePart.Current].Range.EndIndex - PageSize);
                    DataTable dataTableUpper = this.GetDataTableFromTable(indexRangeUpper);

                    Page pageUpper = new Page(indexRangeUpper, dataTableUpper);

                    this.Pages[PagePart.Upper] = pageUpper;

                    Int32 pageSize = this.Pages[PagePart.Current].Data.Rows.Count;
                    return this.Pages[PagePart.Current].Data.Rows[rowIndex % pageSize][columnIndex];
                }
                else
                {
                    Int32 pageSize = this.Pages[PagePart.Upper].Data.Rows.Count;
                    return this.Pages[PagePart.Upper].Data.Rows[rowIndex % pageSize][columnIndex];
                }
            }

            return null;
        }
        // Need to reload the cache...
        else
        {
            IndexRange indexRangeCurrent = new IndexRange(rowIndex - (PageSize / 2), rowIndex + (PageSize / 2));
            IndexRange indexRangeLower = new IndexRange(indexRangeCurrent.EndIndex + 1, indexRangeCurrent.EndIndex + PageSize);
            IndexRange indexRangeUpper = new IndexRange(indexRangeCurrent.StartIndex - 1, indexRangeCurrent.StartIndex - PageSize );

            DataTable dataTableUpper = this.GetDataTableFromTable(indexRangeUpper);
            DataTable dataTableCurrent = this.GetDataTableFromTable(indexRangeCurrent);
            DataTable dataTableLower = this.GetDataTableFromTable(indexRangeLower);

            Page pageUpper = new Page(indexRangeUpper, dataTableUpper);
            Page pageCurrent = new Page(indexRangeCurrent, dataTableCurrent);
            Page pageLower = new Page(indexRangeLower, dataTableLower);

            Pages[PagePart.Upper] = pageUpper;
            Pages[PagePart.Current] = pageCurrent;
            Pages[PagePart.Lower] = pageLower;

            Int32 pageSize = this.Pages[PagePart.Current].Data.Rows.Count;
            return this.Pages[PagePart.Current].Data.Rows[rowIndex % pageSize][columnIndex];
        }
    }

    private DataTable GetDataTableFromTable(IndexRange indexRange)
    {
        if (this.Connection != null)
        {
            String commandText = String.Format("SELECT * FROM {0} WHERE RowId BETWEEN {1} AND {2}", this.TableName, indexRange.StartIndex, indexRange.EndIndex);
            SQLiteCommand command = new SQLiteCommand(commandText, this.Connection);
            SQLiteDataAdapter dataAdapter = new SQLiteDataAdapter(command);

            DataTable dataTable = new DataTable(this.TableName, this.TableName);
            dataAdapter.Fill(dataTable);

            return dataTable;
        }
        else
        {
            return null;
        }
    }

    private const Int32 PageNumber = 3;
    private const Int32 PageSize = 128;

    public class Page
    {
        public Page(IndexRange range, DataTable data)
        {
            this.Range = range;
            this.Data = data;
        }

        private IndexRange _range;
        public IndexRange Range
        {
            get { return this._range; }
            set { this._range = value; }
        }

        private DataTable _data;
        public DataTable Data
        {
            get { return this._data; }
            set { this._data = value; }
        }
    }
    public enum PagePart
    {
        Upper,
        Current,
        Lower,
    }

    public class IndexRange
    {
        private Int32 _startIndex;
        public Int32 StartIndex
        {
            get { return this._startIndex; }
            set { this._startIndex = value; }
        }

        private Int32 _endIndex;
        public Int32 EndIndex
        {
            get { return this._endIndex; }
            set { this._endIndex = value; }
        }

        public IndexRange(Int32 startIndex, Int32 stopIndex)
        {
            this.StartIndex = startIndex;
            this.EndIndex = stopIndex;
        }
    }
}

でもねぇ…絵がとても遅い…どうしたらいいの…?

4

1 に答える 1

2

これが私自身の質問に答える方法です。上下にスクロールしてもオフセットに関する混乱を避けるために主キーを使用しました(うん、怠惰な方法なので、それほど効率的ではありませんが、キャッシュがないだけではありません)まったく、非常に使いやすい[つまり、3つのキャッシュを使用せずに、最終的には、ジョブを適切に実行するために1つだけを作成します]):

public class Cache
{
    protected SQLiteConnection Connection { get; set; }
    protected String TableName { get; set; }
    protected const Int32 PageSize = 512;

    protected CachePage PageCurrent { get; set; }
    protected IndexRange IndexRangeTable { get; set; }

    public Cache(SQLiteConnection connection, String tableName)
    {
        SQLiteConnection.ClearAllPools();

        this.Connection = connection;
        this.TableName = tableName;

        IndexRange indexRangeCurrent = new IndexRange(0, PageSize - 1);
        DataTable dataTableCurrent = this.GetDataTableFromTable(indexRangeCurrent);

        this.PageCurrent = new CachePage(indexRangeCurrent, dataTableCurrent);
    }

    public Object GetCellValue(Int32 rowIndex, Int32 columnIndex)
    {
        DataRow dataRowFound = this.PageCurrent.Data.Rows.Find(rowIndex);

        if (dataRowFound != null)
        {
            return dataRowFound[columnIndex];
        }
        else
        {
            this.ShiftPageToIndex(rowIndex);

            return GetCellValue(rowIndex, columnIndex);
        }
    }

    private void ShiftPageToIndex(Int32 index)
    {
        this.PageCurrent.Range.Start = index;
        this.PageCurrent.Range.Stop = index + PageSize;
        this.PageCurrent.Data = this.GetDataTableFromTable(this.PageCurrent.Range);

        this.PageCurrent.Range.Start = index;
        this.PageCurrent.Range.Stop = index + this.PageCurrent.Data.Rows.Count;
    }

    private IndexRange GetTableIndexRange()
    {
        String commandText = String.Format("SELECT MAX(RowId) FROM {0}", this.TableName);
        SQLiteCommand command = new SQLiteCommand(commandText, this.Connection);

        this.Connection.Open();
        command.CommandText = commandText;
        String maxRowIdString = command.ExecuteScalar().ToString();
        this.Connection.Close();

        Int32 maxRowId = Int32.Parse(maxRowIdString);

        return new IndexRange(0, maxRowId);
    }
    private DataTable GetDataTableFromTable(IndexRange indexRange)
    {
        if (this.Connection != null)
        {
            String commandText = String.Format("SELECT * FROM {0} WHERE RowId BETWEEN {1} AND {2}", this.TableName, indexRange.Start, indexRange.Stop);
            SQLiteCommand command = new SQLiteCommand(commandText, this.Connection);
            SQLiteDataAdapter dataAdapter = new SQLiteDataAdapter(command);

            DataTable dataTable = new DataTable(this.TableName, this.TableName);

            dataAdapter.Fill(dataTable);

            dataTable.Columns.Add("RowId", typeof(Int64));

            for (Int32 i = 0; i < dataTable.Rows.Count; i++)
            {
                dataTable.Rows[i]["RowId"] = i + indexRange.Start;
            }

            dataTable.PrimaryKey = new DataColumn[] { dataTable.Columns["Rowid"] };

            return dataTable;
        }
        else
        {
            return null;
        }
    }

}
public class CachePage
{
    public CachePage(IndexRange range, DataTable data)
    {
        this.Range = range;
        this.Data = data;
    }

    public IndexRange Range {get; set;}
    public DataTable Data {get; set;}
} 
public class IndexRange
{
    public Int32 Start {get; set;}
    public Int32 Stop { get; set; }

    public IndexRange(Int32 start, Int32 stop)
    {
        this.Start = start;
        this.Stop = stop;
    }
}

//ダミーのDataGridViewから呼び出す方法は?

private void dataGridViewMain_CellValueNeeded(Object sender, DataGridViewCellValueEventArgs e)
    {
        if (this.Cache != null)
        {
            e.Value = this.Cache.GetCellValue(e.RowIndex, e.ColumnIndex);
        }
    }

だから、それはかなりクールで、大騒ぎせずに、そしてそれほど考えずにうまく機能します...

このスニペットは、主キー(実際には実際にはフィールドではありません)の代わりにRowIdフィールドを使用してSQLiteデータベーステーブルビューアを作成するために使用できます。

もう1つのより良い解決策は、... ascまたはdescでデータを順番に取得し、モジュロ演算子を使用してそのようなデータを探すことですが、私はそのタスクにはあまりにも怠惰です。SQLite dllは、既存のDataColumnをPrimaryKeyとして使用するよりも、データの順序を取得する方がまだ高速だと思います...しかし、私が言ったように、今のところかなり怠惰です...

于 2013-05-04T10:10:47.630 に答える