У меня очень странная проблема. Итак, предыстория заключается в том, что у нас есть сопоставление между Word ContentControl
и пользовательским объектом, который мы используем для хранения некоторой информации, связанной с содержимым внутри этого элемента управления. Мы используем SortedList<ContentControl, OurCustomObject>
для поддержки этого сопоставления. Часть SortedList полезна для поиска следующего/предыдущего элемента управления содержимым, а также для быстрого доступа к объекту, связанному с элементом управления содержимым.
Чтобы настроить это, мы делаем что-то вроде следующего:
var dictOfObjs = Globals.ThisAddIn.Application.ActiveDocument.ContentControls
.Cast<ContentControl>()
.ToDictionary(key => key, elem => new OurCustomObject(elem));
var comparer = Comparer<ContentControl>
.Create((x, y) => x.Range.Start.CompareTo(y.Range.Start));
var list = new SortedList<ContentControl, OurCustomObject>(dictOfObjs, storedcomparer);
Это, казалось, работало довольно хорошо, но недавно я попробовал это на документе с примерно 5000 элементами управления содержимым, и сканирование замедлилось до абсолютного обхода (3+ минуты для создания экземпляра SortedList).
Так что это достаточно странно, но еще большая странность была впереди. Я добавил логирование, чтобы понять, что происходит, и обнаружил, что логирование начала каждого ContentControl
перед их использованием в списке ускорило его примерно в 60 раз. (Да, ДОБАВЛЕНИЕ логирования ускорило процесс!). Вот гораздо более быстрый код:
var dictOfObjs = Globals.ThisAddIn.Application.ActiveDocument.ContentControls
.Cast<ContentControl>()
.ToDictionary(key => key, elem => new OurCustomObject(elem));
foreach (var pair in dictOfObjs)
{
_logger.Debug("Start: " + pair.Key.Range.Start);
}
var comparer = Comparer<ContentControl>
.Create((x, y) => x.Range.Start.CompareTo(y.Range.Start));
var list = new SortedList<ContentControl, OurCustomObject>(dictOfObjs, storedcomparer);
Конструктор SortedList вызывает Array.Sort<TKey, TValue>(keys, values, comparer);
для ключей и значений словаря. Я не могу понять, почему предварительный доступ к объектам Range в цикле ускорит его. Может быть, что-то делать с порядком, в котором они доступны? Цикл foreach будет обращаться к ним в том порядке, в котором они появляются в документе, в то время как Array.Sort будет прыгать повсюду.
Изменить: когда я говорю SortedList, я имею в виду System.Collections.Generic.SortedList<TKey, TValue>
. Вот код конструктора, который я использую:
public SortedList(IDictionary<TKey, TValue> dictionary, IComparer<TKey> comparer)
: this((dictionary != null ? dictionary.Count : 0), comparer) {
if (dictionary==null)
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.dictionary);
dictionary.Keys.CopyTo(keys, 0);
dictionary.Values.CopyTo(values, 0);
Array.Sort<TKey, TValue>(keys, values, comparer);
_size = dictionary.Count;
}
Array.Sort
. Насколько я понимаю,Array.Sort
будет использовать компаратор для сортировки CC на основе начала их диапазона. Я просто не понимаю, почему доступ кRange.Start
внезапно становится таким медленным, когда в документе достаточное количество CC, и все же его можно ускорить, предварительно перебирая их. Очень запутанно для моего бедного мозга! - person Zout   schedule 05.04.2016