Как ускорить инициализацию этой пользовательской таблицы? - PullRequest
0 голосов
/ 03 марта 2011

Резюме

Этот вопрос является продолжением этого вопроса:
Как реализовать самоназначение столбца из его индекса?

Протестировав код, приведенный в ответах на этот вопрос, приведенный выше, я наконец столкнулся с серьезной проблемой производительности.

Проблемы с производительностью

Проблема производительности возникает при инициализации листа, то есть когда я инициализирую ячейки листа.

    ''' <summary>
    ''' Initialize an instance of the Company.Project.Sheet class.
    ''' </summary>
    ''' <param name="nativeSheet">The native worksheet from which to initialize.</param>
    Friend Sub New(ByVal nativeSheet As Microsoft.Office.Interop.Excel.Worksheet)
        _nativeSheet = nativeSheet
        Dim cells As IDictionary(Of String, ICell) = New Dictionary(Of String, ICell)()

        'These iterations hurt the performance of the API...'
        For rowIndex As Integer = 1 To _nativeSheet.Rows.Count Step 1
            For colIndex As Integer = 1 To _nativeSheet.Columns.Count Step 1
                Dim c As ICell = New Cell(_nativeSheet.Cells(rowIndex, colIndex))
                cellules.Add(c.Name, c)
            Next
        Next

        _cellules = New ReadOnlyDictionary(Of String, ICell)(cells)
    End Sub
  • ReadOnlyDictionary (Of TKey, TValue) :
    A custom read-only dictionary that simply wraps a IDictionary(Of TKey, TValue) to prevent modifications.

Обсуждение

Я работаю таким образом, поскольку каждая ячейка в базовой электронной таблице инициализируется от инициализации рабочей таблицы до конца, то есть, когда рабочая таблица удаляется или завершается. Следовательно, таким же образом я хочу инициализировать ячейки листа, но я также хочу сохранить повышение производительности при использовании индексированных ячеек над именованными («A1») ячейками, сохраняя при этом простоту использования для пользователя API для обращайтесь к ячейке с ее именем, именно так я собираюсь использовать словарь, поэтому, когда я обращаюсь к ячейке «A1», я получаю доступ к этому ключу в своем словаре и адресую ячейку (1, 1) соответственно.

  • Кроме того, я знаю еще более быстрый способ чтения с рабочего листа с помощью свойства Worksheet.UsedRange, которое возвращает все использованные ячейки в 2D-матрицу.

    Если бы в любом случае было то же самое или примерно одинаковое для набора ячеек, с которым я мог бы инициализировать несколько экземпляров своего класса Cell, это было бы здорово и эффективно!

  • Я также думал об инициализации, как только ячейки матрицы 100 × 100 в памяти, при сопоставлении их со своим словарем, так как редко будут использоваться ячейки всего листа. Таким образом, я все еще думаю о способе, которым я должен был бы получить доступ к еще не инициализированной ячейке, скажем, Клеткам (120, 120). В идеале, я думаю, программа должна была бы инициализировать все ячейки между максимально первоначально инициализированной ячейкой (100, 100) и до ячейки (120, 120). Я достаточно ясно здесь? Не стесняйтесь просить разъяснений! =)

  • Другой вариант может состоять в том, что я только инициализирую имена ячеек в словаре и сохраняю там индекс строки и столбца в памяти, а не инициализирую экземпляр Cell с его nativeCell, скажем Range. Вот код моего класса Cell, чтобы проиллюстрировать, что я имею в виду.

    ''» '' 'Представляет ячейку на листе. «»» «»» Друг класс ячейки Реализует ICell

    Private _nativeCell As Microsoft.Office.Interop.Excel.Range
    Private _name As String
    
    ''' <summary>
    ''' Initializes a new instance of the Company.Project.Cell class.
    ''' </summary>
    ''' <param name="nativeCell">The Microsoft.Office.Interop.Excel.Range to wrap.</param>
    Friend Sub New(ByVal nativeCell As Microsoft.Office.Interop.Excel.Range)
        _nativeCell = nativeCell
    End Sub
    
    Public ReadOnly Property NativeCell() As Microsoft.Office.Interop.Excel.Range Implements ICellule.NativeCell
        Get
            Return _nativeCell 
        End Get
    End Property
    
    Public ReadOnly Property Column() As Integer Implements ICell.Column
        Get
            Return _nativeCell.Column
        End Get
    End Property
    
    Public ReadOnly Property Row() As Integer Implements ICell.Row
        Get
            Return _nativeCell.Row
        End Get
    End Property
    
    Public ReadOnly Property Name() As String Implements ICellule.Name
        Get
            If (String.IsNullOrEmpty(_name) OrElse _name.Trim().Length = 0) Then _
                _name = GetColumnName()
    
            Return _nom
        End Get
    End Property
    
    Public Property Value() As Object Implements ICellule.Value
        Get
            Return _nativeCell.Value2
        End Get
        Set(ByVal value As Object)
            _nativeCell.Value2 = value
        End Set
    End Property
    
    Public ReadOnly Property FormattedValue() As String Implements ICellule.FormattedValue
        Get
            Return _nativeCell.Text
        End Get
    End Property
    
    Public ReadOnly Property NumericValue() As Double? Implements ICellule.NumericValue
        Get
            Return Value
        End Get
    End Property
    

Вопросы

  1. Какие у меня есть другие варианты?

  2. Есть ли другие способы пройти?

  3. Есть ли способ сделать реальный подход жизнеспособным с точки зрения производительности?

К вашему сведению, эта проблема устарела при тестировании, поэтому тестирование никогда не завершалось в течение приемлемого промежутка времени, который на самом деле занимает столетия ...

Любые мысли приветствуются! Я открыт для других решений или подходов, которые помогут мне достичь этой цели при решении проблемы с производительностью.

Спасибо вам всем! =) * * Тысяча восемьдесят-два

РЕДАКТИРОВАТЬ # 1

Благодаря Максиму Геевандову , его решение решает проблему, которую я затронул в этом вопросе.

Кроме этого, из этого решения возникла еще одна проблема: SystemOutOfMemoryException, и она будет рассмотрена в другом вопросе.

Моя искренняя благодарность Максиму Геевандову.

1 Ответ

1 голос
/ 03 марта 2011

Вы можете попытаться получить все ячейки в используемом диапазоне за один переход, избегая при этом вызова Cells(rowIndex, colIndex) на каждой итерации итерации (я полагаю, что Cells скрывает вызов взаимодействия, что может повлиять на производительность).

Dim usedRange As Range = nativeSheet.UsedRange
Dim cells(,) As Object = DirectCast(usedRange.get_Value( _
    XlRangeValueDataType.xlRangeValueDefault), Object(,))
[... do your row/col iterations ...]

Некоторые советы по производительности, на которых я основывал эти предположения, приведены в следующей статье: C # Excel Interop Use .В частности, проверьте часть теста:

=== Тест взаимодействия Excel в C # ===

Ячейки []: 30,0 секунд

get_Range (),Ячейки []: 15,0 секунд

UsedRange, get_Value (): 1,5 секунды [самый быстрый]

...