Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Ajax_v_deystvii.pdf
Скачиваний:
38
Добавлен:
05.03.2016
Размер:
5.83 Mб
Скачать

Глава 8. Производительность приложения 305

дои машине, мы должны уметь заглянуть глубже спецификации DOM или стандарта ЕСМА-262, определяющего JavaScript, и учитывать реальные особенности конкретных браузеров. Если мы не будем знать "нижний уровень" иерархии программных средств, вряд ли нам удастся создать конкурентоспособный продукт. *

Если приложению требуется несколько секунд, чтобы обработать щелчок На кнопке, или несколько минут для пересылки содержимого формы, — это плохое приложение, независимо от того, насколько элегантными были решения разработчика. Если каждый раз, когда пользователь интересуется текущим временем, система запрашивает 20 Мбайт памяти, а потом освобождает лишь 15 Мбайт, пользователи вскоре откажутся от нее.

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

Производительность JavaScript-кода имеет большое значение для разработчиков Ajax-приложений, поскольку они вторгаются в ту область, которая еще не была исследована программистами. Объем JavaScript-кода в Ajaxпрограммах намного больше, чем в классическом Web-приложении. Время жизни объектов JavaScript в Ajax-программах больше по сравнению с классическими приложениями, потому что полное обновление Web-страниц происходит гораздо реже.

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

8.2. Скорость выполнения JavaScript-программ

В мире, в котором мы живем, ценится скорость. Срок окончания проектов истекает "еще вчера". (Если вы живете в другом мире, пришлите нам письмо, а еще лучше иммиграционную карту.) Быстродействующая программа предпочтительнее более медленной, при условии, конечно, что они обе выполняют поставленную задачу. Нас как разработчиков кода должно волновать, насколько быстро он работает и как улучшить его.

Существует правило, согласно которому скорость выполнения программы определяется скоростью самой медленной подсистемы. Мы можем измерить время работы всей программы, но результат вряд ли будет полезен. Гораздо лучше будет, если мы сможем выяснить быстродействие отдельных подсистем. Измерение скорости выполнения отдельных фрагментов кода обычно называют профилированием. Создание высококачественного кода, как и создание любого произведения искусства, никогда не завершается, а лишь останавливается на важных этапах. (Плохой код отличается от хорошего тем, что его создание остановилось на этапе, который был абсолютно не интересен.) Путем оптимизации всегда можно хоть немного ускорить выполнение

. U v l o in. ^изоание профессиональных Ajax-приложений

программы. Ограничивающим фактором при этом является не уровень ма. стерства, а имеющееся в наличии время. Выяснив с помощью инструмента профилирования "узкие места'1 нашего кода, мы можем сконцентрировать свои усилия для достижения наилучшего результата. Если же мы попытаемся оптимизировать код в процессе его написания, то вряд ли гарантированно получим хороший результат. "Узкие места" редко бывают именно там, где мы ожидаем их появления.

В данном разделе мы научимся измерять различными способами время выполнения кода. Кроме того, мы создадим простой профилировщик на JavaScript и попробуем поработать с уже существующим инструментом подобного назначения. Далее мы рассмотрим несколько простых программ и применим к ним профилировщик, чтобы выяснить наилучший способ их оптимизации.

8.2.1. Определение времени выполнения приложения

Самый простой инструмент измерения времени, имеющийся в нашем распоряжении, — это системный таймер, доступный из программы на JavaScript посредством объекта Date. Если мы создадим экземпляр Date, вызвав конструктор без параметров, то получим текущее время. Если одно значение Date вычесть из другого, разность будет представлена в миллисекундах. Пример использования объекта Date приведен в листинге 8.1.

Листинг 8.1. Измерение времени выполнения кода посредством Date

function myTimeConsumingFunctionf){ var beginning=new Date{);

// Важные и длительные вычисления

var ending=new Datef);

var duration=ending-beginning;

 

alert("this function took

"+duration

 

+"ms to do something interesting!");

_ J

 

 

Мы создаем объекты Date до начала и по окончании фрагмента кода, а затем определяем время выполнения этого фрагмента, вычисляя разность двух значений времени. В данном примере для представления информации о времени выполнения используется функция a l e r t (), но такое решение подходит лишь для простейших случаев. Обычно информацию о времени выполнения записывают в файл протокола, однако модель безопасности JavaScript запрещает доступ к локальной файловой системе. Самый простой подход, доступный в Aj axприложении, — это запись данных о профиле в память и последующее воспроизведение их в форме отчета.

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

Глава 3. Производительность приложения 307

В листинге 8.2 представлена простая библиотека, реализующая секундомер, которую можно использовать для профилирования кода. В процессе Быполнения тестируемой программы информация о профиле хранится в памяти, а затем воспроизводится в виде отчета.

var stopwatch-new Object();

//Массив зарегистрированных таймеров stopwatch.watches=new Arrayt);

//Точка входа для клиентского кода stopwatch.getWatch=function(id,startNow){

var watch-stopwatch.watches[id] ; if {!watch){

watch=new stopwatch.Stopwatch(id);

}

if (startNow){ watch.start();

}

return watch;

}

// Конструхтор объекта, выполняющего функции секундомера stopwatch.StopWatch=function(id){

this.id-id; stopwatch.watches[idl-this; this.events-new Array(); this.objViewSpec-[

{name: "count", type: "simple"}, {name: "total", type; "simple"},

{name: "events", type: "array", inline:true}

]?

)

stopwatch.Stopwatch.prototype.startefunction(){ this.current=new TimedEvent(};

}

stopwatch.Stopwatch.prototype.stop=function(){ if (this.current){

this.current.stop();

this.events.append(this.current);

this.count++;

this.total+=this.current.duration;

this.current—null;

}

)

// Конструктор обработчика события измерения времени stopwatch.TimedEvent=function(){

this.start=new Date(); this.objViewSpec=[

{name: "start", type: "simple"}, {name: "duration", type: "simple"}

];

}

stopwatch.TimedEvent.prototype.stop=function(){ var stop=new Date О; this.duration=stop-this.start;

1

308 Часть fff Создание профессиональных Ajax-приложений

Рис. 8.1. Граф объектов библиотеки-секундомера. Каждая категория представляется объектом, который содержит предысторию событий. Все категории доступны посредством объекта s t o p w a t c h . w a t c h e s , существующего в единственном экземпляре

// Генератор отчета о

профиле

s t o p w a t c h . r e p o r t = f v m c t i o n ( d i v ) {

v a r

r e a l D i v = x G e t E l e m e n t B y I d ( d i v ) ;

v a r

report=new

o b j v i e w e r . O b j e c t V i e w e r ( s t o p w a t c h . w a t c h e s , r e a l D i v ) I

Наша система-секундомер поддерживает одну или несколько категорий, в каждой из которых существует одно активизированное событие и список предыдущих событий измерения времени. Когда клиентский код вызывает функцию stopwatch, s t a r t (), передавая ей в качестве параметра идентификатор, система создает для этой категории новый объект stopwatch либо повторно использует существующий объект. Затем клиент может многократно задавать команды секундомера s t a r t () и stop(). При каждом вызове функции stop() генерируется объект TimedEvent, в котором отмечается время запуска и длительность события. Если запустить секундомер несколько раз без промежуточных остановок, все обращения к s t a r t ( ) , кроме последнего, будут проигнорированы.

Результатом работы секундомера является граф объектов категорий stopwatch, каждая из которых содержит предысторию измеряемых событий. Пример подобного графа показан на рис. 8.1.

По окончании сбора данных визуализируется граф объектов. Функция render {) использует для автоматической генерации отчета библиотеку Object Viewer (см. главу 5). В качестве упражнения мы предлагаем читателям реализовать вывод данных в формате CSV, что позволит включать их в файл.

В листинге 8.3 приведен пример применения кода секундомера к некоторой функции, выполнение которой занимает длительное время

Глава 8 Производительность приложения

J U S

ЛИСТИНГ 8.3. Измерение времени с помощью бибпиотеки-секундомера

function myTimeConsuniingFvmctionO (

var vatch=stopwatch.getWatch("my time consuming function",true);

// Важные и длительные вычисления

 

watch.stop();

 

}

]

щ

Код секундомера можно достаточно просто включить в программу, подлежащую тестированию. Затем следует определить категории для различных целей. В данном примере мы назвали категорию по имени функции.

Прежде чем переходить к новому разделу, проиллюстрируем изученный материал на конкретном примере. Оценим время выполнения программы Mousemat, рассмотренной нами в главе 4. В данной программе за перемещениями мыши следят два процесса. Один из них выводит информацию о текущих координатах в строке состояния браузера, а другой отображает точку, координаты которой соответствуют текущему расположению курсора мыши. Оба процесса представляют нам полезную информацию, но создают большую нагрузку на центральный процессор. Нам надо выяснить, какой из этих процессов потребляет больше процессорного времени.

С помощью библиотеки-секундомер а мы можем достаточно просто реализовать функции профилирования для данной программы. В листинге 8.4 показана модифицированная Web-страница, содержащая новый элемент DIV, в котором отображается отчет профилировщика. Кроме того, в текст сценария включены обращения к секундомеру.

Листинг 8.4. Документ mousemat. html со средствами профилирования

<htmlxhead>

<link rel='stylesheet' type='text/ess1 href='raousemat.ess1 /> <link rel=lstylesheet1 type='text/ess1 href='objviewer.ess1 /> <script type='text/javascript1 src='x/x_core.js'x/script> <script type='text/javascript' src=1extras-array.js*></script> <script type='text/javascript' src='styling.js'></script> <script type='text/javascript' src='objviewer . js'x/script> <script type='text/javascript' src='stopwatch.js'X/script> <script type='text/javascript' src='eventRouter.js'x/script> <script type='text/javascript'>

var cursor=null; window.onload=function(){

var watch=stopwatch.getWatch("window onload",true); var mat=docu-ment .getElementById( 'mouseraat' ) ; cursor=document,getElementById('cursor' ) ;

var mouseRouter=new jsEvent.EventRouter(mat,"onmousemove"); mouseRouter.addListener(writestatus); mouseRouter.addListener(drawThumbnail);

watch.stop();

}

function writeStatus(e)t

var watch=stopwatch.getWatch("write status",true); window.status=e.clientX+","+e.clientY;

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]