2013-03-12 2 views
2

Я пытаюсь оптимизировать свой чрезмерно медленный DataGridView, который использует базу данных SQLite для получения данных с использованием виртуального режима и кеша, я уже использую трюк двойной буферизации и удаляю авторазборку для столбцов и строк. Однако, несмотря на чтение этого: http://msdn.microsoft.com/en-us/library/ha5xt0d9.aspx и что: http://msdn.microsoft.com/en-us/library/ha5xt0d9.aspxОптимизация DataGridView

Моя сетка супер медленно, на самом деле запросы к БД довольно быстро, но кажется, что живопись DataGridView супер медленно, даже если данные уже загружен через кэш ...

Но, может быть, мой класс Cache не , что хорошо, поэтому мне интересно, сделал ли я что-то неправильно. Принцип довольно прост: кэш разделен на 3 части: верхний, текущий (также можно назвать «средний» и «нижний»), каждый из них разделяется индексами (начало & конец), если данные уже загружены, кеш будет давать значение в соответствии с этими очень простыми правилами:

  • , если значение не находится в текущей части не проблема, просто загрузить данные
  • если значение нужно, это становится ли в верхних или нижних, без каких-либо проблем, эта часть тока требуется одна и только новая часть.

т.е.

Верхняя часть: 0 - 100 Текущая часть: 101 - 201 Нижняя часть: 202 - 302

Потребность значение находится в пределах нижней части, никаких проблем, ток становится Lower , Upper переходит в Current, и только новая нижняя часть должна быть перезагружена. Очевидно, что если требуемое значение имеет индекс строки, недоступный для кэша, он перезагружается.

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; 
     } 
    } 
} 

Но gee ... картина настолько медленная ... что я могу сделать ...?

+0

Заполните DataTable, который представляет ваш ожидаемый DataGridView. Затем используйте свойство DataSource DataGridView, чтобы связать его с DataTable. Это ускорит многое. – CathalMF

+0

Свойство DataSource в порядке, если у так называемого DataSource есть допустимое время загрузки, я имею дело со сотнями тысяч записей (в основном, файл базы данных SQLite имеет более одного GigaByte), он принимает (когда ui не делает начните замораживать раньше) примерно 15 минут, чтобы загрузить весь набор данных в DataSource. Вот почему я решил использовать виртуальный режим, чтобы загрузить только текущее представление требуемой информации. – Perret

+0

Любые ответы ребята ??? – Perret

ответ

2

Вот какой способ ответить на мой вопрос, я просто использовал первичный ключ, чтобы избежать путаницы в отношении смещения, прокручиваю ли я вверх или вниз (да ленивый путь, так что это не так ... эффективно, но еще больше чем просто без кэша на всех, и очень проста в использовании [т.е. без использования 3 кэши, но в конце концов он приходит с только один для выполнения должным образом работу]):

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); 
     } 
    } 

Так что это очень здорово и хорошо работает без суеты и, не думая, что много ...

Этот фрагмент может быть использован для создания просмотра таблицы базы данных SQLite, используя поле RowId вместо первичного ключа (который на самом деле не является полем).

Еще одним лучшим решением было бы получить данные по порядку ... asc или desc, а затем искать такие данные с помощью оператора modulo, но я слишком ленив для этой задачи. Я думаю, что dll SQLite все еще быстрее, чтобы получить порядок в данных, чем использовать ранее существовавший DataColumn в качестве PrimaryKey ... но, как я уже сказал, я довольно ленив на данный момент ...

Смежные вопросы