Программирование мобильных устройств на платформе .NET Compact Framework - Иво Салмре
Шрифт:
Интервал:
Закладка:
■ Второй из проверенных нами вариантов оптимизации, заключающийся в отказе от распределения памяти для нескольких строковых объектов и использовании вместо этого строковых индексов, оказал на производительность приложения значительное воздействие. Благодаря внесению изменений в проект вспомогательного класса и задержке создания строкового класса до тех пор, пока это действительно не потребуется, мы непосредственно увеличили общую производительность алгоритма более чем на треть. Кроме того, как и в рассмотренном выше случае, нам удалось значительно уменьшить количество "мусора", образующегося в результате работы нашего алгоритма. Следствием этого явилось уменьшение общего объема "мусора", а также более ровное и быстрое выполнение приложения.
■ Закономерности изменения производительности в ряду вариантов оптимизации для эмулятора и физических устройств Pocket PC в основном совпадают, но между абсолютными показателями производительности алгоритма для этих двух случае наблюдаются разительные отличия. В нашем примере (распределение памяти для строк) результатом оптимизации явилось одинаковое улучшение производительности как на эмуляторе, так и на физических устройствах, но самый оптимальный вариант алгоритма выполнялся на эмуляторе со скоростью 934 итерации в секунду, а на физическом устройстве — 122 итерации в секунду. Таким образом, данный алгоритм выполняется на эмуляторе в 7,6 раз быстрее по сравнению с физическим устройством. Если этот алгоритм является критическим, и мы собираемся применять его ко многим тысячам единиц данных, то мы должны позаботиться об организации обратной связи с пользователями приложения на время проведения вычислений. В этой связи может потребоваться привлечение фоновых потоков выполнения для обработки данных или использование меньших объемов данных в каждый момент времени. Единственный способ получения реальных результатов оценки производительности — это выполнение приложения на реальных устройствах с использованием реальных объемов данных.
Уделяйте особое внимание тому, как используются строки в ваших алгоритмах
Чрезвычайная широта применения и необычайная полезность строк при создании программного обеспечения делают этот специальный тип данных уникальным. Часто строки представляют обычный текст, но нередко они используются и для передачи машинных данных, например строк запросов баз данных. Короче говоря, строки вездесущи, а байтовый тип данных применяется в большинстве приложений там, где надо, и там, где не надо. Современные языки программирования позволяют очень легко работать со строками, создавать их, разбивать, копировать и объединять. Рассмотрим, например, следующие простые операторы.
string str1 = "foo"; //Размещает в памяти строковый объект
string str2 = "bar"; //Размещает в памяти строковый объект
string str3 = str1 + str2; //Размещает в памяти строковый объект
string str3 = str3 + str3; //Размещает в памяти строковый объект
Каждый из этих операторов создает типы и размещает данные в памяти, и при этом нам даже не приходится вызывать никаких функций! Многие разработчики пользуются строками настолько бездумно, что легко могут допустить их некорректное использование. Не оптимизированная обработка строк является одной из наиболее вероятных причин плохой производительности. Ниже представлены некоторые рекомендации и правила, которыми следует руководствоваться при работе со строками. (При этом подразумевается, что вы работаете в .NET Framework/.NET Compact Framework, однако аналогичные правила будут действовать и в других средах. Более детальные разъяснения вы найдете в соответствующем справочном руководстве для своей среды.)
■ Строки неизменчивы (постоянны). Этот странный термин неизменчивый (immutable) просто означает, что текстовые данные строки не могут быть изменены в памяти. Те операции в коде, которые, как вам кажется, изменяют данные строки, на самом деле создают новую строку. Постоянство обладает некоторыми весьма привлекательными свойствами. Например, поскольку строковые данные сами по себе являются статическими, несколько переменных могут указывать на одни и те же данные; благодаря этому присвоение одной строковой переменной значения другой сводится к простому копированию "указателя" вместо глубокого копирования всех данных, которые ему соответствуют. Отрицательной стороной неизменчивости является невозможность изменения данных. Если вы хотите изменить, добавить или отсечь данные, то эти изменения будут отражаться в новой копии строки.
■ Когда на строковые данные не ссылается ни одна "активная" ("live") переменная, они становятся "мусором". Рассмотрим пример:
string str1 = "foo"; //"foo" — статические данные, скомпилированные
//в двоичные данные вашего приложения
string str2 = str1 + str1; //только что была создана новая строка, явля-
//ющаяся результатом конкатенации двух строк
str2 = "bar"; //Поскольку отсутствуют другие переменные, указываю-
//щие на те данные, на которые указывала переменная str2,
//эти данные становятся мусором, и память должна быть
//очищена от них.
■ Если вы хотите сослаться на некоторую часть строки, то во многих случаях это проще всего сделать, используя целочисленные индексы в строке. Поскольку строки — это данные, представленные массивами символов, то использование индексов для получения этих данных не составляет труда. Существует множество функций, позволяющих осуществлять поиск и просмотр данных внутри строк (но только не изменять эти данные!).
■ Если вы создаете новые строки внутри циклов, настоятельно рекомендуется рассмотреть возможность использования объекта StringBuilder. Все виды строк создаются на основе других переменных, обрабатываемых в циклах. Типичным примером динамического создания строк может служить цикл, генерирующий текстовый отчет, каждая строка которого содержит следующие данные:
//Неэффективный код, выполняющийся внутри цикла...
{
myString = myString +"CustomerID: " +
System.Convert.ToString(customer[idx].id) +
", Name: " + System.Convert.ToString(customer[idx].name);
}
Вместо того чтобы конкатенировать строки и создавать новую строку, для создания отчета можно было бы использовать класс StringBuilder. Класс StringBuilder очень удобно использовать для работы с массивами переменной размерности с целью создания строк. Он позволяет эффективно изменять длину или содержимое массива и, что самое важное, создавать новые строки на основе символьных массивов. Обязательно изучите класс StringBuilder, поскольку умение использовать его имеет решающее значение для написания эффективных алгоритмов, генерирующих строковые данные.
■ Измеряйте объективные количественные показатели своих алгоритмов. Занимаясь написанием алгоритма обработки строк, тестируйте его быстродействие! Испробуйте несколько различных подходов. Вы очень быстро научитесь распознавать, какой алгоритм будет эффективным, а какой — нет.
Пример эффективного создания строк
В листинге 8.9 представлены два аналогичных алгоритма, которые приводят к одному и тому же результату. В обоих алгоритмах осуществляется инкрементирование счетчика, и каждый раз, когда значение счетчика увеличивается, его строковое представление добавляется в расширяемый фрагмент текста. Оба алгоритма выполняют одинаковое количество итераций и характеризуются одинаковой степенью сложности написания. И, тем не менее, один из них работает гораздо быстрее другого.
Листинг 8.9. Сравнение эффективности использования строк и класса StringBuilder в алгоритмахПримечание. В этом примере используется класс PerformanceSampling, определенный ранее в данной книге.
const int COUNT_UNTIL = 300;
const int LOOP_ITERATIONS = 40;
//---------------------------------------------------------
//HE ОЧЕНЬ ЭФФЕКТИВНЫЙ АЛГОРИТМ!
//
//Для имитации создания типичного набора строк используются
//обычные строки
//---------------------------------------------------------
private void button1_Click(object sender, System.EventArgs e) {
//Вызвать сборщик мусора, чтобы тест //начинался с чистого состояния.
//ПРИБЕГАЙТЕ К ЭТОЙ МЕРЕ ТОЛЬКО В ЦЕЛЯХ ТЕСТИРОВАНИЯ! Вызовы
//сборщика мусора в программах вручную будут приводить
//к снижению общей производительности приложений!
System.GC.Collect();
int numberToStore = 0;
PerformanceSampling.StartSample(0, "StringAllocaitons");