Подлипенский Павел

Блог о технологиях и деньгах

FireUnit: Javascript Unit Testing Extension

clock декабря 19, 2008 09:19 by author Подлипенский Павел

Юнит-тесты это то, на что никогда нет времени. А если есть время, то нет желания. И наконец, если есть время и желание, то обязательно найдется какой-нибудь засранец, который найдет, как забрать твое время и отобьет всякое желание что-либо делать вообще… Да, заказчику тяжело объяснить что такое юнит-тесты и зачем они нужны. Но тем не менее, хочу сделать небольшой обзор нового плагина для Firefox, который позволяет  юнит-тестить ваш джаваскрипт. Если быть совсем точным, то это расширение для Firebug – излюбленного инструмента javascript-разработчиков.

Итак, те кто не  владеет теорией юнит-тестирования идут учить матчасть, а с остальными мы рассмотрим простейшие юнит тесты:
// Простой true/false юнит-тест
fireunit.ok( true, "Тест пройдет успешно!" );
fireunit.ok( false, "Тест будет провален!" );
// Сравниваем две строки и показываем разницу
// если строки отличаются между собой
fireunit.compare(
"Ленивый программист чистит зубной щеткой унитаз.",
"Ленивый программист уронил зубную щетку в унитаз",
"Это одинаковые строки?"
);
// Сравниваем две строки, используя регулярные выражения
fireunit.reCompare(
/Ленивый программист .* унитаз./,
"Ленивый программист уронил зубную щетку в унитаз.",
"Сравниваем две строки, используя регулярные выражения."
);
// Отображаем результаты тестов
fireunit.testDone();

После запуска теста, получаем следующие результаты:

image 

Довольно полезной оказалась фича эмулирования событий браузера:

// FireUnit позволяет эмулировать события браузера
var input = document.getElementsByTagName("input")[0];
fireunit.mouseDown( input );
fireunit.click( input );
fireunit.focus( input );
fireunit.key( input, "a" );

Теперь нет необходимости, сломя голову носится по странице и кликать на все, что кликается или кликалось раньше…

Еще одно удобство, которое не может не тронуть сердце программиста – это разбиение тестов на файлы и запуск тестов батчем(скопом – по русски):

// Запускаем все тесты, расположенные в двух разных файлах
fireunit.runTests("test2.html", "test3.html");
// Эту строку надо положить в конце каждого файла, чтобы тесты 
// в следующем файле запустились. Тесты запускаются последовательно.
fireunit.testDone();

И вот результат:

image

Правда надо сказать о некоторых недороботках в этом плагине. При запуске нескольких тестов из разных файлов, необходимо чтобы эти файлы находились в той же директории, что и файл исполняющий batch-тест. Для запуска тестов у вас должен быть локально развернут Http-сервер. Если вам предыдущее предложение ни о чем не сказало, то смените профессию попробуйте добавить fireunit.forceHttp(), чтобы удостовериться в готовности вашего сервера обработать http-запросы.

if (fireunit.forceHttp())
{
fireunit.runTests( 
"example1.html",
"example2.html" 
);
}

В целом FireUnit довольно удобная в джаваскрипте штука. Пользуйтесь на здоровье.

Полезные ссылки

Примеры использования FireUnit

Блог разработчика

Скачать FireUnit

Текущий рейтинг: 5.0 (3 голосов)

  • Currently 5/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5


Этический вопрос: Ошибки в конструкторе…

clock декабря 11, 2008 12:03 by author Подлипенский Павел
Я, как вы знаете, пишу не только статьи в свой блог, но и код. Программный код. Поэтому, меня зачастую интересуют вопросы, которые нормальным людям в голову и не лезут. Суть сегодняшней дилеммы очень простая – хорошо ли генерировать исключительные ситуации из конструктора? Это скорее философский или этический вопрос, нежели сложная техническая задача. И все же.

Конструкторы не возвращают каких-либо значений, поэтому было бы здорово сгенерировать ошибку, в случае, если объект не может быть создан. И таким образом, оповестить всех что этот объект использовать дальше нельзя. Уже слышу протестующие крики в свой адрес: “…Конструкторы предназначены для простых операций типа инициализации объекта…” или “…Лучше после попытки создания объекта проверить его на null…”.

А я не согласен с этим!

Когда мы передаем в конструктор некие параметры, мы обязаны проверить их на валидность. А как вы сообщите об “испорченном” параметре? С помощью null? Что? Опять протесты?

“…А давайте оставим конструктор пустым и добавим метод Initialize, который и вернет нам необходимый код состояния инициализации объекта…”

Хм, а конструкторы тогда зачем? Для тех, кто все еще сомневается в моих доводах, советую прочесть презентацию Bjarne Stroustrupsa (он настолько крутой, что придумал С++) об “Standard-Library Exception Safety”. И там, все вышесказанное, размазано по 36 страницам. Поэтому генерировать исключительные ситуации в конструкторе – это нормально (особенно, если вы исповедуете RAII).

В C#.NET есть еще одна приятная особенность – finalizer вызовится даже в случае ошибки в конструкторе и ресурсы будут освобождены корректно. Кстати и в самом .NET Framework многие базовые классы генерируют исключительные ситуации из конструктора, например Guid, DateTime, Queue, FileStream и другие.

А вы генерируете ошибки в конструкторах?

Текущий рейтинг: 5.0 (4 голосов)

  • Currently 5/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5


Быстрые веб-страницы: оптимизируем JQuery

clock ноября 24, 2008 09:20 by author Подлипенский Павел

Начнем с того, что фреймворк JQuery очень быстрая штука. Да, конечно найдутся такие, кто скажет: "Короче, начал юзать джэйквери и у меня все повисло. Ну, я типа решил, что надо писать все самому...". Что ж, идиотов всегда хватало и мы не будем их переубеждать. Тем не менее, существуют некоторые практики и советы по использованию этого мощного (в руках homosapiens) инструмента.

Используйте ID, вместо классов для выборки элемента. Если у вас большой дом(не путать с дорогущим сооружением по соседству), то выборка $(.one-element') приведет к просмотру всего дерева для поиска этого элемента. Помогите ему и себе, предоставьте как можно больше информации о месторасположении объекта: ('.block1 > .block2 > .block3 …), укажите стартовую точку для поиска $('.foo', startObject) или используйте выборку по айдишнику $('#block45').

Читайте подробнее и на английском:

http://www.learningjquery.com/2006/12/quick-tip-optimizing-dom-traversal

http://blog.multitweet.com/2007/09/13/jquery-performance/

http://www.componenthouse.com/article-19

https://www.adaptavist.com/display/%7Egfraser/2008/01/12/Some+quick+tips+for+jQuery+performance

Иногда filter работает быстрее, чем find.

Читайте подробнее и на английском:

http://groups.google.com/group/jquery-en/browse_thread/thread/533451087251c952/9bb31c108c089c4f

http://www.learningjquery.com/2006/12/how-to-get-anything-you-want-part-2

Естественно это зависит от браузера, но объединение нескольких строк в одну таким способом: string1 = string2 + string3 может работать медленнее, чем если бы вы создали массив строк, а затем сделали join. В редких случаях имеет смысл использовать и третье решение - конкатенацию строк.

js-compare

Читайте подробнее и на английском:

http://www.nabble.com/document.write-and-DOm-creation---performance-optimization--td18390436s27240.html

http://www.sitepen.com/blog/2008/06/09/string-performance-getting-good-performance-from-internet-explorer/

Для тех, кто еще не в курсе - javascript это не многопоточный язык. Всю правду, с откровенными фотографиями и отпечатками пальцев можно найти здесь. Также рекомендую почитать о способах иммитации многопоточности в джаваскрипте. Все это означает только одно - пока вы добавляете новые элементы в дом, браузер не сможет их отобразить. Поэтому, стоит разбивать операции вставки новых элементов на небольшие группы, давая браузеру отрисовывать их в промежутках между вставками. Сделать это можно с помощью банального setTimeout.

Недавно открыл для себя, что iterators в джаваскрипте работают медленее, чем generators при больших объемах данных. Будьте бдительны, товарищи!

Старайтесь кэшировать элементы и их коллекции, где это только возможно

   1: var myCachedObjects = $(".find-me");
   2: var anotherObjects = myCachedObjects.filter(".another-object");
   3: var favouriteObjects = myCachedObjects.filter(".favourite-object");

Ухх, за эти выходные мое javascript-мировоззрение перевернулось: xml парсится в четыре раза медленнее, чем json, if(isTrue) работает быстрее, чем if(isTrue == true) и многое другое. Не хочу превращать этот пост в энциклопедию, поэтому добро пожаловать в полезные ссылки.

Полезные ссылки

jQuery: Performance analysis of selectors

IE + JavaScript Performance Recommendations

Evaluate Low Level JavaScript Performance Characteristics

Some more JavaScript performance tips

Текущий рейтинг: 4.9 (7 голосов)

  • Currently 4,857143/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5


Быстрые веб-страницы: Какой формат картинки использовать?

clock ноября 5, 2008 12:47 by author Подлипенский Павел

Я уже писал об оптимизации картинок на веб-странице. И, тем не менее, хочу вернуться к этой теме еще раз, чтобы поговорить о графических форматах. Мы вскользь рассмотрим форматы GIF и JPEG, а затем уделим побольше внимания PNG формату, как наиболее распространенному в мире веб.

GIF

Формат GIF способен хранить сжатые без потерь изображения в формате до 256 цветов с палитрой. Изображение в формате GIF хранится построчно, поддерживается только формат с индексированой палитрой цветов. GIF более не является запатентованной технологией, поэтому можете использовать его без риска попасть за решетку(хотя когда нас, советских граждан, сажали за нарушения прав интелектуальной собственности?). Более подробно об истории этого формата вы сможете прочесть в статье The GIF Controversy: A Software Developer's Perspective.

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

GIF поддерживает анимацию, которая, в темном Web 1.0 веке, насыщала просторы интернета в виде моргающих, переливающихся и подпрыгивающих картинок. Это раздражало. Хотя в светлом настоящем Web 2.0 мы тоже можем наблюдать анимацию “Loading…”, пока ожидаем ответа AJAX запроса…

Формат GIF также поддерживает прозрачность(так называемую булевую или логическую прозрачность). Т.е. каждый пиксель может быть либо абсолютно прозрачным, либо абсолютно непрозрачным. Третьего не дано.

JPEG 

JPEG не имеет ограничения в 256 цветов, как GIF формат. Формат JPEG может содержать миллионы цветов и обладает при этом отличным сжатием. Наиболее распространённые расширения для таких файлов .jpeg, .jfif, .jpg, или .jpe. Алгоритм JPEG является алгоритмом сжатия с потерей качества, поэтому некорректно считать что JPEG хранит данные как 8 бит на канал (24 бит на пиксель). Поэтому, для редактирования изображения, промежуточные результаты лучше сохранять в других форматах. Хотя такие операции как вырезание части изображения, поворот или изменение мета информации будут происходить без потери качества.

JPEG не поддерживает прозрачность.

PNG

Формат PNG хранит графическую информацию в сжатом виде. Причем, это сжатие производится без потерь, в отличие от JPEG. Форматов PNG несколько, но мы рассмотрим только два:

  • PNG8
  • TrueColor PNG

PNG8 это формат с палитрой, наподобие GIF. Этот формат также известен как “PNG с палитрой” или “индексируемый PNG”. В сравнении с GIF форматом, PNG8 зачастую имеет меньший размер(при том же качестве графики) и поддерживает alpha-прозрачность(в отличие от GIF). В тоже время, PNG8 не поддерживает анимацию.

Второй тип PNG – TrueColor PNG(или PNG24, PNG32) – может содержать миллионы цветов, как и JPEG. Являясь форматом, хранящим графику без потерь и поддерживающим alpha-прозрачность, PNG имеет больший размер по сравнению с JPEG. Это делает PNG32 идеальным кандидатом на роль промежуточного формата во время редактирования JPEG или в случае когда каждый пиксель играет важную роль, а размер наоборот – неинтересен.

Internet Explorer и PNG прозрачность

Оба формата PNG поддерживают alpha-прозрачность. Но некоторые браузеры(не будем тыкать пальцем –> IE6) имеют свой взгляд на это свойство PNG8-формата. IE6 и раньше отображают alpha-прозрачность как логическую прозрачность – т.е. каждый пиксель либо полностью прозрачен, либо полностью закрашен.

png8-transparency

Прозрачность PNG24 и PNG32 в IE6( и раньше) отображается хуже. Прозрачность приобретает почему-то серый цвет…(источник W3C)

png-transparency

IE7 полностью поддерживает alpha-прозрачность для обоих PNG8 и TrueColor PNG форматов. Для более ранних версий вам необходимо добавлять фикс в CSS в виде AlphaImageLoader фильтра.

Так что же использовать?

В большинстве случаев в вебе имеет смысл использовать PNG8 (несмотря на его плохую совместимость с IE). Но для отдельного ряда случаем имеет смысл переключить свой выбор на TrueColor PNG:

  • Когда 256 цветов PNG8 будет недостаточно лучше использовать TrueColor PNG(для картинок < 10Кб) или JPG (для картинок > 10Кб). Зачастую человек не отличает качество графики с 200 цветов и 1000 цветов. Поэтому вы можете попробовать сконвертировать картинку в PNG8 и посмотреть, удовлетворит ли вас качество. Бывают такие изображения, что удаление хотябы двух цветов, может значительно ухудшить ее зрительное восприятие. Поэтому тут нельзя дать совет на все случаи жизни.
  • Когда большая часть картинки полупрозрачна лучше использовать TrueColor PNG – размер будет меньше.

В большинстве случаев JPEG используют для хранения фотографий – они как правило имеют большой размер(> 10Кб) и требуют хорошего качества.

Формат GIF следует использовать в том случае, если цветовой диапазон исходных изображений не превышает 256 цветов, либо количество цветов может быть уменьшено без существенного ухудшения качества. Это, как правило, изображения с большими площадями однородных одноцветных областей, изображения, полученные конвертированием из векторных форматов, изображения с текстом.

Полезные ссылки

Быстрые веб-страницы: Уменьшаем количество файлов загружаемых браузером

Быстрые веб-страницы: Формирование страницы из нескольких источников

Быстрые веб-страницы: Сжатие и Очистка веб-страниц

Краткое описание формата GIF

Википедия: GIF

Формат файла JPEG

JPEG — Википедия

Myths and Facts about JPG

GIF vs. JPG - Which Is Best?

When and how to use internet image formats

Transparent PNGs in Internet Explorer 6

Текущий рейтинг: 5.0 (4 голосов)

  • Currently 5/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5


Быстрые веб-страницы: Уменьшаем количество файлов загружаемых браузером

clock октября 29, 2008 09:00 by author Подлипенский Павел

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

AppTurns - число файлов ресурсов, требуемых страницей. Они включают файлы CSS, JS, изображений и любые другие файлы, извлекаемые обозревателем в процессе визуализации страницы. Уравнение учитывает страницу HTML отдельно, добавляя время приема-передачи (round-trip time – RTT) перед выражением AppTurns.

Использование множества CSS- и JS-файлов небольшого размера, является хорошей практикой с точки зрения удобства в разработке веб-приложения. Но плохо, с точки зрения производительности вашего приложения. Каждый запрос к серверу требует определенных ресурсов как браузера, так и самого сервера: ожидание в очереди в браузере, время на инициализацию и отправку запроса, передача соответствующих заголовков и cookies, время на обработку запроса и формирования ответа. Время, необходимое для обработки/формирования запроса, называется latency(серенькая область на рисунках ниже). Каждый ресурс, помещенный в отдельный файл, будет бесцеремонно занимать это драгоценное для клиента время.

podlipensky.com without combiner

Поэтому имеет смысл объединять однотипные файлы ресурсов в один. Таким образом, мы экономим на этот самом latency каждого файла ресурсов.

podlipensky.com with combiner

Так как мы говорим о CSS или JS коде, то объединение будет представлять собой конкатенацию строк. Проще всего написать HttpHandler, который бы смог объединять файлы ресурсов в runtime режиме, оставляя разработчику возможность оперировать множеством файлов. При этом не стоит забывать о таких способах оптимизации как GZIP и кэширование ваших ресурсов. Код такого HttpHandler'a может быть таким:

   1: public void ProcessRequest (HttpContext context) {
   2:         
   3:         HttpRequest request = context.Request;
   4:         
   5:         // Читаем параметры: имя ключа в конфиге(содержит список файлов для объединения), тип конетнат и версию конента. 
   6:         //Все эти поля обязательны, т.к. они используются как ключ для кэширования.
   7:         string setName = request["s"] ?? string.Empty;
   8:         string contentType = request["t"] ?? string.Empty;
   9:         string version = request["v"] ?? string.Empty;
  10:  
  11:         // Определяем, позволяет ли браузер сжатие контента
  12:         bool isCompressed = DO_GZIP && this.CanGZip(context.Request);
  13:  
  14:         // Ответ формируем в UTF8 кодировке
  15:         UTF8Encoding encoding = new UTF8Encoding();
  16:  
  17:         // Если ресурс есть в кэше, то берем из кэша.
  18:         // В противном случае, считываем файлы, объединяем и кладем в кэш
  19:         if (!this.WriteFromCache(context, setName, version, isCompressed, contentType))
  20:         {
  21:             using (MemoryStream memoryStream = new MemoryStream(5000))
  22:             {
  23:                 // Определяем тип потока, в который будем писать результат
  24:                 using (Stream writer = isCompressed ?
  25:                     (Stream)(new GZipStream(memoryStream, CompressionMode.Compress)) :
  26:                     memoryStream)
  27:                 {
  28:  
  29:                     // Загружаем файлы, основываясь на ключе в <appSettings>
  30:                     string setDefinition = 
  31:                         System.Configuration.ConfigurationManager.AppSettings[setName] ?? "";
  32:                     string[] fileNames = setDefinition.Split(new char[] { ',' }, 
  33:                         StringSplitOptions.RemoveEmptyEntries);
  34:                     string delimeter = "\r\n";
  35:                     foreach (string fileName in fileNames)
  36:                     {   
  37:                         //Добавляем разделитель, чтобы в двух соседних файлах не произошло объединения ключевых слов.
  38:                         byte[] bytes = encoding.GetBytes(delimeter);
  39:                         writer.Write(bytes, 0, bytes.Length);
  40:                         
  41:                         string text = GetFileText(context, fileName.Trim(), encoding);
  42:                         byte[] fileBytes = encoding.GetBytes(text);
  43:                         writer.Write(fileBytes, 0, fileBytes.Length);
  44:                     }
  45:  
  46:                     writer.Close();
  47:                 }
  48:  
  49:                 //Кэшируем результат
  50:                 byte[] responseBytes = memoryStream.ToArray();
  51:                 context.Cache.Insert(GetCacheKey(setName, version, isCompressed),
  52:                     responseBytes, null, System.Web.Caching.Cache.NoAbsoluteExpiration,
  53:                     CACHE_DURATION);
  54:  
  55:                 // Генерируем ответ
  56:                 this.WriteBytes(responseBytes, context, isCompressed, contentType);
  57:             }
  58:         }
  59:     }

Функция GetFileText читает содержимое файла и возвращает его в виде строки (ввиду простоты функции, я не стал приводить листинг ее кода).

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

Image Map‘ы объединяют несколько картинок в одну большую. Общий объем такой картинки примерно равен сумме объемов маленьких картинок, но уменьшение количества запросов к серверу сокращает общее время загрузки страницы. Image Map будет работать, если картинки на странице находятся рядом друг с другом, например в случае полосы навигации. Определение координат для Image Map’ов — занятие довольно утомительное и зачастую приводит к ошибкам (вообще-то для этого есть специальный софт, однако ничего не могу сказать про качество его работы).

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

Inline-картинки используют URL-схему data: для встраивания картинки в саму страницу. Это, однако, увеличит размер HTML-документа. Встраивайте inline-картинки в ваши (кешированные) таблицы стилей — и вы добьетесь уменьшения запросов к серверу, а размер HTML останется прежним.

Полезные ссылки

Быстрые веб-страницы: Формирование страницы из нескольких источников

Быстрые веб-страницы: Сжатие и Очистка веб-страниц

Performance Research, Part 6: Less is More — Serving Files Faster by Combining Them

Combine JavaScript with ASP.NET Web forms

CSS Sprites: What They Are, Why They’re Cool, and How To Use Them

 

Текущий рейтинг: 5.0 (6 голосов)

  • Currently 5/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5


Быстрые веб-страницы: Формирование страницы из нескольких источников

clock октября 20, 2008 09:10 by author Подлипенский Павел

Недавно мы рассмотрели механизмы оптимизации размера страницы. Следующим нашим объектом оптимизации будет пропускная способность сервера.

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

Зачастую, мы ложим статический контент (картинки, javascript, css-код) в тот же самый веб-проект или веб-сайт. Это приводит к тому, что эти ресурсы скачиваются браузером с одного домена. Такой подход может привести к следующим проблемам:

  • Все ресурсы создают подключения к вашему домену. Поэтому другие, более важные запросы (например запросы к веб-сервису) не могут быть осуществлены, т.к. браузер может создать только два подключения к одному домену.
  • Если вы используете ASP.NET Forms Authentication, то гигансткий Forms Authentication cookie будет посылаться с каждым запросом на ваш домен. Эти cookie будут отправляться при запросе картинки, css-кода или javascript файла, хотя все эти ресурсы, зачастую, не нуждаются в cookie.
  • Такой подход также увеличит размер IIS лога, т.к. он сохраняет cookie для каждого запроса вашего статического контента. Больше того, если вы используете Google Analytics, то к вашим cookie прибавятся еще и четыре больших cookie от компании Google.

Давайте рассмотрим первую проблему более детально

image_thumb

Этот график показывает, что одновременно закачиваются только два файла. Все это происходит только для одного домена. Обратите внимание на загрузку скрипта. Браузер ничего не закачивает параллельно со скриптом. Это объясняется особенностью браузеров в обработке тэга <script>. Таким образом, закачка следующих ресурсов начнется только после завершения закачки и выполнения скрипта.

Но если браузеру удастся открыть еще одно подключение, то закачка той же страницы будет уже выглядеть по-другому

image_thumb_3

Как видите, время загрузки уменшилось примерно на 40%. Помимо графики, мы можем загружать подобным образом CSS и Javascript-код.

Другая проблема, возникающая при загрузке статического контента с того же домена – это огромные Forms Authentication и Google Analytics cookie:

podl

 

 

Таким образом дополнительные 2-3 килобайта будут сопровождать каждый запрос к вашему домену. А оно вам надо?

Решить данну проблему можно двумя способами. Первый, и самый неудобный – располагать статический контент в другом домене и сразу ссылаться на него, при разработке проекта. Второй, более приемлемый – написать свой HttpFilter. Этот фильтр будет просматривать содрежимое сраницы, перед отправкой контента пользователю, выявлять href/src атрибуты и прибавляет к ним некий префикс. Скажем если у меня на странице есть следующий код:

   1: <img src="\images\myphoto.jpg" alt="my photo" />

То фильтр может его трансформировать в следующее:

   1: <img src="http://images.podlipensky.com/myphoto.jpg" alt="my photo" />

После чего вам необходимо будет создать Url Rewritting модуль (или воспользоваться встроенным в IIS 7), который будет изменять url на свой прежний эквивалент. Для высоконагруженных систем, особенно если они оперируют в основном статическим контентом (например видео- или фото- галлереи) полезно действительно размещать этот контент на других серверах. В этом случае можно еще подключить новую фичу IIS 7 – Application Request Routing. Либо воспользоваться Content Delivery Network сервисом.

Впрочем, все вышеописанные методы не новы, и давно используются в коммерческих проектах. Пример подобных оптимизаций, с хорошо описанными исходными кодами, вы сможете найти у Omar AL Zabir’a(который любезно разрешил мне воспользоваться диаграммами, приведенными выше).

Полезные ссылки

Smart caching for websites and blogs

IIS URL Rewriting and ASP.NET routing

Omar AL Zabir blog on ASP.NET Ajax and .NET 3.5

Текущий рейтинг: 5.0 (5 голосов)

  • Currently 5/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5


Быстрые веб-страницы: Сжатие и Очистка веб-страниц

clock октября 13, 2008 08:33 by author Подлипенский Павел

Несмотря на стремительное развитие интернета, проблема оптимизации веб-приложений стоит также остро, как и во времена dialup-подключений по советской вермишели.

В сентябре 2006 года Питер Севчик (Peter Sevcik) и Ребекка Ветцель (Rebecca Wetzel) из компании NetForecast опубликовали документ, именуемый "Field Guide to Application Delivery Systems" («Полевое руководство по системам доставки приложений»). Основное внимание в документе было уделено улучшению производительности приложений в глобальных сетях (wide area network – WAN), и в нем было приведено уравнение

yrav , где

  • R - время ответа. Общее время с запроса страницы пользователем (обычно переходом по ссылке и т.п.) до полной визуализации страницы на его компьютере. Обычно измеряется в секундах.
  • Полезная нагрузка - общее число байтов, отправленных обозревателю, включая разметку и все ресурсы (такие как файлы CSS, JS и изображений).
  • Пропускная способность - скорость передачи данных обозревателю и от него. Может быть асимметрична и представлять несколько скоростей, если данная страница создается из нескольких источников. Обычно усредняется для создания единого выражения пропускной способности в байтах в секунду.
  • AppTurns - число файлов ресурсов, требуемых страницей. Они включают файлы CSS, JS, изображений и любые другие файлы, извлекаемые обозревателем в процессе визуализации страницы. Уравнение учитывает страницу HTML отдельно, добавляя время приема-передачи (round-trip time – RTT) перед выражением AppTurns.
  • RTT - время, необходимое для приема и передачи данных, вне зависимости от числа переданных байтов. Каждый запрос тратит минимум одно RTT для самой страницы. Обычно измеряется в миллисекундах.
  • Cs - время вычислений на сервере. Время, уходящее у кода на запуск, извлечение данных из базы данных и составление ответа, отправляемого обозревателю. Измеряется в миллисекундах.
  • Cc - время вычислений на клиенте. Время, уходящее у обозревателя на визуализацию HTML на экране, исполнение JavaScript, применение правил CSS, итд.

Этот пост будет началом серии статей по оптимизации производительности вашего веб-приложения.

Рассмотрим первый параметр, влияющий на время загрузки страницы – полезная нагрузка или объем полезных данных.

Архивирование(сжатие) контента страницы

Первое, что мы можем сделать - это сжать наш html, css и js код с помощью GZip или Deflate(~40% лучше, чем gzip) сжатия. Не все браузеры принимают сжатый контент, поэтому необходимо проверять, содержит ли запрос заголовок Accept-Encoding со значением “gzip” или “deflate”:

   1: private bool CanGZip(HttpRequest request) 
   2: { 
   3:         string acceptEncoding = request.Headers["Accept-Encoding"]; 
   4:         if (!string.IsNullOrEmpty(acceptEncoding) && 
   5:              (acceptEncoding.Contains("gzip") || acceptEncoding.Contains("deflate"))) 
   6:             return true; 
   7:         return false; 
   8: }

Программно сжать контент позволяет пространство имен System.IO.Compression

   1: public static byte[] Compressor(byte[] buffer, string encodingType) 
   2:      { 
   3:          using (MemoryStream memStream = new MemoryStream()) 
   4:          { 
   5:              Stream compress = null; 
   6:  
   7:              // Choose the compression type and make the compression 
   8:              if (String.Equals(encodingType, "gzip", StringComparison.Ordinal)) 
   9:              { 
  10:                  compress = new GZipStream(memStream, CompressionMode.Compress); 
  11:              } 
  12:              else if (String.Equals(encodingType, "deflate", StringComparison.Ordinal)) 
  13:              { 
  14:                  compress = new DeflateStream(memStream, CompressionMode.Compress); 
  15:              } 
  16:              else 
  17:              { 
  18:                  // No compression 
  19:                  return buffer; 
  20:              } 
  21:  
  22:              compress.Write(buffer, 0, buffer.Length); 
  23:              compress.Dispose(); 
  24:  
  25:              return memStream.ToArray(); 
  26:          } 
  27:      } 

После чего вам остается лишь добавить сжатый поток в ответ пользователю:

   1: HttpContext.Response.OutputStream.Write(compressedData, 0, compressedData.Length);

Если же у вас есть доступ к IIS консоли, то вы можете настроить сжатие на уровне IIS(версии 7):

iis_compression1 

iis_compr2

Очистка JS и CSS кода

Также мы можем очистить наш css и js код от лишних символов.

Например, следующий css код

   1: body {
   2:   font-size:14px;
   3:   font-family:Arial;
   4: }
   5: h1 {
   6:   font-size:14px;
   7:   font-family:Arial;
   8: }

можно преобразовать в следующую строку

   1: body,h1{font:14px arial}
 

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

Для javascript кода наибольшей популярностью пользуются два инструмента JSMin (автор: Douglas Crockford) и Packer (автор: Dean Edward). Алгоритмы упаковки обоих инструментов обрабатывают(obfuscate) любой js код, но только при условии, что вы корректно завершаете все javascript-выражения символом “;” (включая объявление функций).

Для css кода вы можете воспользоваться утилитой под названием CSSTidy или одним из множества подобных онлайн-сервисов.

Оптимизация графики

Допустим в вашей базе данных, на жестком диске или любом другом хранилище расположена ваша любимая фотография размером 200x400 пикселей (24 Kb). Но вам необходимо отобразить ее на странице в виде изображения размером 100х200 пикселей. В таком случае, подобный код

   1: <img src="/images/mybestphoto.png" width="100" height="200" alt="My Best Photo" />

решит вашу проблему, но при этом размер вашей фотографии останется прежним - 24 Kb. Хотя, если бы вы сжали(изменили размер) ее в фотопопе, то "вес" файла существенно бы уменьшился.

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

   1: public void SaveResizedImage(string imagePath, int width, int height)
   2: {
   3:         int newResizeimageId;
   4:         // Читаем графический файл с жесткого диска
   5:         FileStream fs = File.OpenRead(imagePath);
   6:         byte[] data = new byte[fs.Length];
   7:         fs.Read(data, 0, data.Length);
   8:         MemoryStream ms = new MemoryStream(data);
   9:         Bitmap bmp = new Bitmap(ms);
  10:         System.Drawing.Image imgPhoto = (Image)bmp;
  11:         // Настраиваем Encoder, для преобразования графики
  12:         Encoder qualityEncoder = Encoder.Quality;
  13:         EncoderParameters parameterList = new EncoderParameters(1);
  14:         EncoderParameter qualityParameter = new EncoderParameter(qualityEncoder, 50L);
  15:         parameterList.Param[0] = qualityParameter;
  16:         // По расширению файла определяем кодек
  17:         ImageCodecInfo codec = GetCodecFromExtension(Path.GetExtension(imagePath)); //GetCodecFromExtension в этом листинге не приведена.
  18:         // Создаем пустую канву. Измененная графика будет записана в эту канву.
  19:         Bitmap bmPhoto = new Bitmap(width, height);
  20:         bmPhoto.SetResolution(10, 10);
  21:         Graphics grPhoto = Graphics.FromImage(bmPhoto);
  22:         grPhoto.SmoothingMode = SmoothingMode.HighSpeed;
  23:         grPhoto.InterpolationMode = InterpolationMode.High;
  24:         grPhoto.PixelOffsetMode = PixelOffsetMode.HighSpeed;
  25:         grPhoto.DrawImage(imgPhoto, new Rectangle(0, 0, width, height), 0, 0, bmp.Width, bmp.Height, GraphicsUnit.Pixel);
  26:         // Сохраняем файл в память, а затем убеждаемся, что все ресурсы освобождены корректно.
  27:         MemoryStream mm = new MemoryStream();
  28:         bmPhoto.Save(mm, codec, parameterList);
  29:         imgPhoto.Dispose();
  30:         bmPhoto.Dispose();
  31:         grPhoto.Dispose();
  32:         //Сохраняем массив байт измененной графики куда необходимо. 
  33:         newResizeimageId = SaveImageToDb2(mm.GetBuffer());
  34:         mm.Dispose();
  35: }

Хочу обратить ваше внимание на строчку

   1: bmPhoto.SetResolution(10, 10);

Где мы можем изменить качество нашей графики, установив новое разрешение. Результаты могут быть примерно такими

avatar avatar_0 avatar_2 avatar_5 avatar_8
2.58 kB 2.86 kB (+10%) 1.98 kB
(-23%)
1.55 kB
(-39%)
672 bytes
(-74%)

Обратите внимание на то, что после небольшого ухудшения качества, размер изображения наоборот - возрос. Дальнейшее же уменьшения разрешения привело к уменьшению размера файла. Также учтите, что, чем больше первоначальный размер изображения, тем эффективней будет сжатие без визуальных потерь качества. Ухудшать качество небольших картинок, как видите, смысла нет.

Отключаем ViewState

Возможно вы подумали, о нехитром свойстве страницы EnableViewState="false". Отключение ViewState с помощью вышеупомянутого свойства действительно уменьшит размер скрытого поля, но не избавит вас от него. В отдельных случаях полезно и вовсе избавиться от ViewState. Сделать это можно, переопределив метод SavePageStateToPersistenceMedium, и “забыв” вызвать в нем базовый метод.

   1: protected override void SavePageStateToPersistenceMedium(object state) 
   2: { 
   3:         //base.SavePageStateToPersistenceMedium(state); 
   4: }

Избавляемся от длинных ID, сгенерированных ASP.NET

К сожалению, не существует способа избавиться от длинных ID в debug-версии вашего веб-сайта. Но когда ваш проект уже готов и скомпилирован, то вы можете обработать сборки, обфускатором. При этом, следует указать обфускатору заменять все имена контролов на более короткие. Таким образом вместо ctl00_body_WidgetPanelsLayout_WidgetContainer65_WidgetBodyPanel, ваша панель будет иметь id равное, например А. Будьте осторожны с подобной оптимизацией – ваш javascript код может не работать, если функционал привязан к заранее заданным ID. Хотя, если вы использовали что-то похожее на следующий код

   1: $get('<%= TextBox1.ClientID %>')

то обфускатор не нарушит вашего функционала.

Полезные ссылки

Reduce the weight of stylesheets by 35% at runtime

HTTP compression in ASP.NET 2.0

Compression and performance - GZip vs. Deflate

Compress your pages, css, js and WebResources.axd files for better performance

Online Tool compares Packer, JSMin, Dojo, and YUI Compressor

Speed Up Your Site: Optimize your CSS

CSS Optimization: Make Your Sites Load Faster for Free

Текущий рейтинг: 5.0 (7 голосов)

  • Currently 5/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5


Search


LinkedIn Profile

Tags

Posts

  • Pingback from 241.akemet.com Cb300 Second Hand Address, Cb3000 Video Price Marine Engines
    241.akemet.com

  • http://tvsh2004.narod.ru/gm03.html
    test

  • конечно это очень дорого, у нас ведь вся страна пользуется только лицензионной windows...
    Славян

  • Алексей: С удовольствием!
    Подлипенский Павел

  • Присоединяйтесь к ЖЖ-коммьюнити http://community.livejournal.com/ua_extjs
    Алексей

  • Поправка насчет генерации самого хтмл-кода для ответа веб метода. Предлагаю сделать проще, не создавая объекта страницы и без тега <form> [WebMethod] public string GetControlHTML(string controlLocation) { HtmlTextWriter tw = new HtmlTextWriter(new StringWriter()); var uc = (UserControl)(new UserControl()).LoadControl(controlLocation); uc.RenderControl(tw); return tw.InnerWriter.ToString(); }
    Anthony

Categories

Calendar

<<  Сентябрь 2010  >>
воповтсрчепясу
2930311234
567891011
12131415161718
19202122232425
262728293012
3456789

Archive

© Copyright 2010

Sign in

Ó÷àñòíèê ïëàíåòû Developers.org.ua

Bookmark and Share

Web Developement Blogs - Blog Catalog Blog Directory