<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>CTAPbIu_MABP&#039;s BLOG &#187; perfomance</title>
	<atom:link href="http://mabp.kiev.ua/tag/perfomance/feed/" rel="self" type="application/rss+xml" />
	<link>http://mabp.kiev.ua</link>
	<description>Не вижу проблем, кроме лени! &#169; Старый Мавр</description>
	<lastBuildDate>Tue, 07 Sep 2010 20:05:40 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.9.1</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>Доклад с coffee&#039;n&#039;code (дополненная)</title>
		<link>http://mabp.kiev.ua/2009/08/10/presentation-from-coffee-n-code/</link>
		<comments>http://mabp.kiev.ua/2009/08/10/presentation-from-coffee-n-code/#comments</comments>
		<pubDate>Mon, 10 Aug 2009 17:14:14 +0000</pubDate>
		<dc:creator>CTAPbIu_MABP</dc:creator>
				<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[Программирование]]></category>
		<category><![CDATA[jquery]]></category>
		<category><![CDATA[perfomance]]></category>
		<category><![CDATA[test]]></category>

		<guid isPermaLink="false">http://mabp.kiev.ua/?p=1102</guid>
		<description><![CDATA[Вот это я должен был рассказывать на код и кофе, выкладываю дабы реабилитироваться после того как забыл слайды.


1. Предисловие
Я бы хотел начать эту статью с цитаты Джона Ресига (John Resig), основного идеолога jQuery и корпорации Mozilla.
Я думаю, вы переоцениваете полезность jQuery. Используя его пользователи теперь ограничены селекторами которые могут использовать (они могут использовать, только то, [...]]]></description>
			<content:encoded><![CDATA[<p>Вот это я должен был рассказывать на код и кофе, выкладываю дабы реабилитироваться после того как забыл слайды.</p>
<span id="more-1102">
</span>
<h3>1. Предисловие</h3>
<p>Я бы хотел начать эту статью с цитаты Джона Ресига (John Resig), основного идеолога jQuery и корпорации Mozilla.</p>
<blockquote>Я думаю, вы переоцениваете полезность jQuery. Используя его пользователи теперь ограничены селекторами которые могут использовать (они могут использовать, только то, что предоставляет браузер и полагаться на милость кросс-браузерных багов) и это большая проблема. Не говоря уже о том что jQuery призывает к смешиванию html разметки, css и javascript.</blockquote>
<p>В этой статье я хочу показать что в умелых руках и хуй - балалайка.</p>
<p>Итак начнем настраивать наш... инструмент с самого начала.</p>
<h3>2. Selectors</h3>
<p>Начну, пожалуй, с самого начала с функции $, она принимает два параметра: первый - селектор, второй - контекст. Хотя контекст обычно опускают я в последствии покажу как им грамотна пользоваться.</p>
<h4>2.1. Простой селект</h4>
<p>Самый простой вариант это выбор по id, имени тега и имени класса.</p>
<pre>
<code class="javascript">

$("#id")
$("tag")
$(".class")

</code>
</pre>
<p>Я не случайно расположил их именно в такой последовательности, они идут по сложности алгоритма выборки. В первом случаи вызов функции эквивалентен вызову:</p>
<pre>
<code class="javascript">

document.getElementById("id");

</code>
</pre>
<p>Поскольку предполагается что id уникальный то поиск проходит очень быстро, и если на странице есть два элемента с таким id то найден будет только первый. Хот я в IE и тут накосячили и до 7 версии включительно в случаи отсутствия элемента с таким id он вернет элемент у которого совпадает атрибут name.</p>
<p>Во втором случаи тоже всё относительно просто:</p>
<pre>
<code class="javascript">

document.getElementsByTagName("tag");

</code>
</pre>
<p>Получили все ноды с таким именем из документа и все готово. И на удивление никаких косяков, если не учитывать что при запросе getElementsByTagName("*") IE вернет и комментарии тоже.</p>
<p>В третьем случаи если есть возможность работу перехватывает:</p>
<pre>
<code class="javascript">

document.getElementsByClassName("class");

</code>
</pre>
<p>(Табличку поддерживаемости этой функции смотрите на <a href="http://www.quirksmode.org/dom/w3c_core.html" rel="nofollow external">quirksmode.org</a>)</p>
<p>Но эту функцию поддерживают уже не все браузеры. Для остальных применяется совсем другой алгоритм - нужно получить абсолютно все ноды, потом обойти их циклом проверяя имена классов, и если совпали то добавить в массив результата.</p>
<pre>
<code class="javascript">

var nodes = document.getElementsByTagName("*"), result = [];
for (var i=0; i&lt;nodes.length; i++){
	if(" " + (nodes[i].className || nodes[i].getAttribute("class")) + " ").indexOf("class") > -1)
		result.push(nodes[i]);
}

</code>
</pre>
<p>Какой метод использовать определяется в самом начале при подключении библиотеки.</p>
<h4>2.2. Селекст через querySelectorAll</h4>
<p>Но это все подходит только для элементарных селекторов. А на практике приходится обычно использовать намного более сложные конструкции. И для них в современных браузерах FireFox 3.0, Safari 3.2, Opera 9.5 в том числе и в IE8 появились функция querySelector и querySelectorAll. Они соответственно предназначены для поиска одной или нескольких нод по CSS3 селекторам. Если браузер клиента поддерживает эту функцию то все о чем мы говорили в прошлом пункте - отпадает и поиск происходит через querySelectorAll.</p>
<pre>
<code class="javascript">

$("#id .class tag")

</code>
</pre>
<p>В лучшем случаи селектор будет обработан именно querySelectorAll потому что он написан по правилам CSS3. Но так можно не со всеми селекторами, jQuery поддерживает ряд селекторов которые не входят в CSS3 такие, например, как :visible.</p>
<pre>
<code class="javascript">

$("#id .class tag:visible")

</code>
</pre>
<p>Такой селектор выдаст ошибку в функции querySelectorAll и селектор будет перенаправлен в поисковый движок Sizzle где строка будет разбита на простые селекторы и превратится по сути в несколько разных поисков в котором каждым следующим контекстом является предыдущий селектор.</p>
<pre>
<code class="javascript">

$(document).find("#id").find(".class").find("tag").filter(":visible")

</code>
</pre>
<p>Скорость этого метода поиска напрямую зависит от величины DOM дерева, чем оно больше - тем медленнее, но её можно значительно увеличить написав селектор раздельно.</p>
<pre>
<code class="javascript">

$("#id .class tag").filter(":visible")

</code>
</pre>
<p>При этом querySelectorAll выберет все ноды, а Sizzle разберется с ":visible"</p>
<p>По поводу псевдо-селекторов тоже кстати очень интересный вопрос: CSS3 поддерживает несколько видов псевдо-классов такие как :nth-of-type/:nth-child/:parent/:not/:checked , jQuery имеет свою реализацию этих селекторов для браузеров не поддерживающих querySelectorAll или для браузеров в которых querySelectorAll не поддерживает данный селектор (табличку поддерживаемости селекторов смотрите на <a href="http://www.quirksmode.org/css/contents.html" rel="nofollow external">quirksmode.org</a>), но эта реализация иногда отличается. Для примера возьмем псевдо-класс :nth-of-type и выберем все четные дивы а из них все нечетные.</p>
<pre>
<code class="javascript">

document.querySelectorAll("div:nth-of-type(even):nth-of-type(odd)") // Safari/FireFox:0  IE/Opera:N/A
$("div:nth-of-type(even):nth-of-type(odd)"); // Safari/FireFox:0  IE/Opera:All
$("div:even:odd"); // All: вернут 1,5,9 дивы 

</code>
</pre>
<p>Первых два примера работают одинаково и вернут либо 0, если отработала функция querySelectorAll (это касается первого примера), либо все элементы, потому что их обработал Sizzle (это особенность реализации выражения ":"), а третий вернет 1, 5, 9 и тд. элементы, а значит селекторы отрабатывали в три прохода, сначала из всего дом дерева были выбраны все дивы, потом из них были выбраны все нечётные, а потом из оставшихся были выбраны все чётные.</p>
<p>jQuery так же имеет набор псевдо-селекторов который не входят в CSS3 и обслуживаются только Sizzle'ом :visible/:animated/:input/:header. Их лучше выделять отдельно так как они могут сильно замедлить выборку. Так например было с селекторами :visible/:hidden в версии 1.2.6, для того чтобы узнать видимый элемент или нет надо было подняться до самого верха по DOM-дереву проверяя атрибуты display и visible каждого родителя. <a href="http://mabp.kiev.ua/2009/02/07/accelerates-selectors-in-jquery/">[пруфлинк]</a>
</p>
<pre>
<code class="javascript">

$("div").filter(":visible")

</code>
</pre>
<p>Псевдо-классы используемые для поиска элементов формы такие как :radio тоже имеют некоторое преимущество если не используется querySelectorAll в противном случаи CSS3 селектор input[type=radio] работает быстрее</p>
<h4>2.3. Сложенный селект</h4>
<p>Сложенный селект это когда нам надо выбрать группу из двух или более разных селекторов, например все дивы у которых класс равен A, B и C</p>
<p>Это можно сделать двумя способами</p>
<pre>
<code class="javascript">

$(".a,.b,.c")

</code>
</pre>
<p>выбрать все сразу</p>
<pre>
<code class="javascript">

$(".a").add(".b").add(".c")

</code>
</pre>
<p>или по одному.</p>
<p>При этом если задействована функция querySelectorAll то первый быстрее второго в четыре раза, а если нет то второй в два раза быстрее первого.</p>
<p>Если уже заговорили про классы их можно искать как любые другие атрибуты например если надо найти все классы имена которых начинаются на "my" можно сделать так</p>
<pre>
<code class="javascript">

$("[class^=my]")

</code>
</pre>
<p>а не городить логику с использованием add, тем более что такой способ поддерживается querySelectorAll. <a href="http://mabp.kiev.ua/2009/02/21/testing-productivity-jquery-selectors/">[пруфлинк]</a>
</p>
<h4>2.4. Неправильный селект в контексте</h4>
<p>На сайте <a href="http://www.tvidesign.co.uk/blog/improve-your-jquery-25-excellent-tips.aspx" rel="nofollow external">tvidesign.co.uk</a> в одной очень популярной статье "Improve your jQuery - 25 excellent tips" которую перевели на русский и перепечатывают где только не лень, начиная с Хабра, написано что селект лучше делать в контексте и приведен вот такой пример:</p>
<pre>
<code class="javascript">

$('#listItem' + i, $('.myList'))

</code>
</pre>
<p>Я не спорю что Jon Hobbs-Smith иммет неплохое портфолио но тем не менее он ничего не знает о jQuery. Да и вообще статьи начинающиеся на "100-500 советов" попахивают несостоятельностью автора излагать свою мысль последовательно, не говоря уже о том что все эти советы Капитан Очевидность уже давно нам дал в мануалах, факах и руководствах.</p>
<p>Рассмотрим пример подробнее: контекст это то, где ищут селектор, значит пример можно переписать в более наглядную но менее читаемую форму</p>
<pre>
<code class="javascript">

$($(".myList")).find("#listItem")

</code>
</pre>
<p>При этом контекст от первого поиска будет являться document.</p>
<pre>
<code class="javascript">

$($(".myList",document)).find("#listItem")

</code>
</pre>
<p>Еще раз перепишу согласно формуле.</p>
<pre>
<code class="javascript">

$($(document).find(".myList")).find("#listItem")

</code>
</pre>
<p>И наконец раскроем скобки</p>
<pre>
<code class="javascript">

$(document).find(".myList").find("#listItem")

</code>
</pre>
<p>Что же получается мы выполняем дорогостоящую операцию поиска по имени класса (по всему DOM-дереву в худшем случаи) для того чтобы упростить и без того самую простую операцию поиска по id. БРЕД!</p>
<h4>2.5. Правильный селект в контексте</h4>
<p>Правильно делать с точностью да наоборот. В контекст надо указывать id элемента.</p>
<pre>
<code class="javascript">

$(".class",$("#id"))

</code>
</pre>
<p>Только вот я не понимаю зачем вообще в контекст передавать jQuery объект вполне достаточно.</p>
<pre>
<code class="javascript">

$(".class","#id")

</code>
</pre>
<p>Это можно переписать как.</p>
<pre>
<code class="javascript">

$("#id").find(".class")

</code>
</pre>
<p>Можно еще больше ускорить работу, если искать вот таким способом:</p>
<pre>
<code class="javascript">

$(document.getElementById("id")).find(".class")

</code>
</pre>
<p>Но это ИМХО будет уже плохим тоном. Хотя поэкспериментировать интересно, что если вместо getElementById взять querySelectorAll</p>
<pre>
<code class="javascript">

$("div",document.querySelectorAll("#id"))

</code>
</pre>
<p>Это примерно тоже самое что и</p>
<pre>
<code class="javascript">

$("div",[document.getElementById("id")])

</code>
</pre>
<p>Не прироста производительности ни красоты кода из этого не получить, поэтому советую в контекст передавать что-то простое вроде id или при использовании псевдо-селекторов обрабатываемых Sizzle'ом передавать их в селектор а все остальное в контекст</p>
<pre>
<code class="javascript">

$(":visible","input[type=checkbox]")

</code>
</pre>
<p>Ну раз уже заговорили о псевдо-селекторах то</p>
<pre>
<code class="javascript">

$(":checkbox")

</code>
</pre>
<p>быстрее чем</p>
<pre>
<code class="javascript">

$("input[type=checkbox]")

</code>
</pre>
<p>без использования querySelectorAll и наоборот.</p>
<h4>2.6. Cложный селест</h4>
<p>Часто возникает задача найти всех потомков одного родителя, если нам известно что все потомки являются непосредственными, то есть детьми, то на этом можно сэкономить. Конечно же лучше всего было бы написать правильный селектор</p>
<pre>
<code class="javascript">

$("#id > div")

</code>
</pre>
<p>Но если выборка уже есть то будем использовать ее как контекст. Как мы уже выяснили поиск в контексте происходит при помощи функции find</p>
<pre>
<code class="javascript">

$("#id").find("> div")

</code>
</pre>
<p>Но find очень дорогая функция, она просматривает абсолютно всех потомков контекста, поэтому лучше использовать функцию children, она просматривает только непосредственных потомков.</p>
<pre>
<code class="javascript">

$("#id").children("div")

</code>
</pre>
<p>Есть еще ряд функций поиска и манипуляций которых стоит избегать без крайней необходимости это: find, closest, wrap, wrapInner, replaceWith, clone. Заметте wrapAll сюда не входит. <a href="http://mabp.kiev.ua/2009/03/29/jquery-profiling/">[пруфлинк]</a>
</p>
<h3>3. Cache</h3>
<h4>3.1. Внутреннее кеширование</h4>
<p>Кеш у jQuery крайне не развит, если не сказать отсутствует, поэтому кешируется только предыдущий элемент выбранный в цепочке. Это можно наглядно рассмотреть на двух примерах.</p>
<p>Ситуация такая, вы работаете со списком у вас если один из элементов li для того чтобы получить все элементы включая текущий надо выбрать всех братьев (все у кого родитель это родитель текущего) этого элемента и добавляем его самого</p>
<pre>
<code class="javascript">

$("#id").siblings().add("#id")

</code>
</pre>
<p>Так как он - прошлый элемент, с которым работали в цепочке вызовов, мы можем взять его из кеша.</p>
<pre>
<code class="javascript">

$("#id").siblings().andSelf()

</code>
</pre>
<p>Конечно в данном конкретном случаи быстрее было бы сделать</p>
<pre>
<code class="javascript">

$("#id").parent().children()

</code>
</pre>
<p>Потому что siblings это и есть выбор всех детей родителя. Но я думаю что принцип использования этот пример иллюстрирует нормально.</p>
<p>Второй пример использования кеша это простой возврат к предыдущей выборке, вместо того чтобы размазывать код на три строчки</p>
<pre>
<code class="javascript">

var elt = $("#id");
elt.children().css({/**/})
elt.click();

</code>
</pre>
<p>Можно после работы с детьми вернуться обратно к родителю и работать с ним дальше</p>
<pre>
<code class="javascript">

$("#id").children().css({/**/}).end().click()

</code>
</pre>
<h4>3.2. Кеширование селекторов</h4>
<p>Поскольку кеш так слабо развит, селекторы нужно кешировать вручную. Возьмем например вот такой код</p>
<pre>
<code class="javascript">

for(var i=0;i<1000;i++)
	$("ul").append("&lt;li&gt;"+i+"&lt;/li&gt;")

</code>
</pre>
<p>Все работает и выглядит красиво, но это можно оптимизировать если вынести выборку за пределы цикла добавление новых элементов будет проходить быстрее</p>
<pre>
<code class="javascript">

var elts = $("ul");
for(var i=0;i<1000;i++)
	elts.append("&lt;li&gt;"+i+"&lt;/li&gt;")

</code>
</pre>
<h4>3.3. Буферизация</h4>
<p>Но и это еще не все, этот код можно заставить работать еще быстрее. Каждый раз делая append мы заставляем обновиться DOM-дерево и заставляем браузер перерисовать страницу. Этого можно избежать придерживая вставку в DOM-дерево.</p>
<pre>
<code class="javascript">

var str = "";
for(var i=0;i<1000;i++)
	str += "&lt;li&gt;"+i+"&lt;/li&gt;"
$("ul").html(str);

</code>
</pre>
<p>Дело в том что функции для работы с DOM-деревом у jQuery самые "тяжелые" <a href="http://mabp.kiev.ua/2009/03/29/jquery-profiling/">[пруфлинк]</a>. Это просто объясняется. Все html ноды на которые повешены события через jQuery имею в себе атрибут с объектом jQuery. При удалении этих нод нужно следить чтобы не было утечек памяти и удаляет эти атрибуты перед удалением ноды. В результате функции html и text вызывают функции полной очистки и только потом вставки нового содержимого.</p>
<pre>
<code class="javascript">

jQuery(DOMElement).empty().append(text)

</code>
</pre>
<p>Функция empty выбирает все ноды и по очереди удаяет</p>
<pre>
<code class="javascript">

jQuery(DOMElement).children().remove()

</code>
</pre>
<p>А функция remove уже заботится чтобы из элементов были удалены все дополнительные данные и события</p>
<p>Джон Ресиг (John Resig) утверждал что знает способ быстро удалить все это и что улучшит эти методы, но что-то воз и ныне там. Поэтому будем ждать улучшенных функций уже в jQuery 1.4 </p>
<h4>3.4. Создание "на лету"</h4>
<p>Прошлый пример на самом деле был нужен, для того что бы я подобрался поближе к интересненькому. Часто приходится создавать какие-то вспомогательные дивы, естественно меня заинтересовал самый эргономичный способ это сделать. Казалось бы в чем проблема кинул кусок html кода в и jQuery сама все сделала. Возьмем самый простой и банальный пример надо создать пустой див.</p>
<pre>
<code class="javascript">

$("&lt;div&gt;&lt;/div&gt;")

</code>
</pre>
<p>или</p>
<pre>
<code class="javascript">

$("&lt;div/&gt;")

</code>
</pre>
<p>Второй вариант в 5 раз быстрее первого. Но это естественно не все, что если нам надо создать не пустой див, а содержащий текст, из прошлых заметок станет ясно что функция text тяжелая и выгоды от нее не будет и стоит создавать див как есть.</p>
<pre>
<code class="javascript">

$("&lt;div&gt;text&lt;/div&gt;")

</code>
</pre>
<p>А не создавать а потом добавлять текст</p>
<pre>
<code class="javascript">

$("&lt;div/&gt;").text("text")

</code>
</pre>
<p>Но это не каcается создания атрибутов, для них используются намного более "легкие" функции attr/css/addClass <a href="http://mabp.kiev.ua/2009/03/29/jquery-profiling/">[пруфлинк]</a>, вот тут то и имеет смысл вместо</p>
<pre>
<code class="javascript">

$("&lt;div style='background:red;'/&gt;")

</code>
</pre>
<p>писать</p>
<pre>
<code class="javascript">

$("&lt;div/&gt;").css({background:'red'});

</code>
</pre>
<p>это даст небольшой, но выигрыш.</p>
<h3>4. Events</h3>
<h4>4.1. Множественные события</h4>
<p>Иногда возникает необходимость повесить одно и тоже действие на несколько событий, это приводит к созданию новых функций либо к копи-пасте. Этого легко можно избежать. Например нужно задать размер дива при загрузке и изменять его при изменении размеров окна.</p>
<pre>
<code class="javascript">

$(window).bind("resize load",null,function(){
	$("#id").css({width:document.clientWidth})
});

</code>
</pre>
<p>Только при этом не забываем что IE8 ведет себя не корректно и при загрузке страницы сначала происходит событие resize а только потом load.</p>
<p>Тоже самое корректно и в обратную сторону.</p>
<pre>
<code class="javascript">

$(window).unbind("resize load");

</code>
</pre>
<p>Но это не работает в версии 1.2.6, точнее это работает только с именованными функциями а с анонимными не работает, их надо удалять по одной.</p>.<br />
<h4>4.2. Одно событие на много элементов</h4>
<p>Если случается повесить события на длинный список.</p>
<pre>
<code class="javascript">

var ul = $("&lt;ul/&gt;");
for(var i=0,j=1000;i&lt;j;i++)
	$("&lt;li&gt;"+i+"&lt;/li&gt;").click(function(e){
		alert(this.innerHTML);
	}).appendTo(ul);


ul.appendTo("body");

</code>
</pre>
<p>То в результате мы будем иметь 1000 одинаковых обработчиков событий, вряд ли это добавит скорости вашей странице, поэтому можно воспользоваться маленькой хитростью и повесить всего один обработчик на родительский элемент.</p>
<pre>
<code class="javascript">

var str = "";
for(var i=0,j=1000;i&lt;j;i++)
	str += "&lt;li&gt;"+i+"&lt;/li&gt;";
$("&lt;ul/&gt;")
	.append(str)
	.click(function(e){
		alert(e.target.innerHTML);
	})
	.appendTo("body");

</code>
</pre>]]></content:encoded>
			<wfw:commentRss>http://mabp.kiev.ua/2009/08/10/presentation-from-coffee-n-code/feed/</wfw:commentRss>
		<slash:comments>37</slash:comments>
		</item>
		<item>
		<title>jQuery profiling</title>
		<link>http://mabp.kiev.ua/2009/03/29/jquery-profiling/</link>
		<comments>http://mabp.kiev.ua/2009/03/29/jquery-profiling/#comments</comments>
		<pubDate>Sun, 29 Mar 2009 17:34:47 +0000</pubDate>
		<dc:creator>CTAPbIu_MABP</dc:creator>
				<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[Программирование]]></category>
		<category><![CDATA[FireFox]]></category>
		<category><![CDATA[jquery]]></category>
		<category><![CDATA[perfomance]]></category>

		<guid isPermaLink="false">http://mabp.kiev.ua/?p=966</guid>
		<description><![CDATA[С тех пор как как я написал про то как ускорить селекторы в jQuery меня все беспокоит скорость работы разных частей библиотеки. 21 февраля я проводил тестирование производительности селекторов, а 23 примерно таким же профайлингом занимался John в статье JavaScript Function Call Profiling. Я тогда еще подумал что все что я сделал можно выкинуть, потому [...]]]></description>
			<content:encoded><![CDATA[<p>С тех пор как как я написал про то как <a href="http://mabp.kiev.ua/2009/02/07/speed-up-jquery-selectors/">ускорить селекторы в jQuery</a> меня все беспокоит скорость работы разных частей библиотеки. 21 февраля я проводил <a href="http://mabp.kiev.ua/2009/02/21/jquery-selectors-benchmark/">тестирование производительности селекторов</a>, а 23 примерно таким же профайлингом занимался John в статье <a href="http://ejohn.org/blog/function-call-profiling/" rel="nofollow external">JavaScript Function Call Profiling</a>. Я тогда еще подумал что все что я сделал можно выкинуть, потому что он написал лучше и подробнее.</p>
<span id="more-966">
</span>
<p>Но он затронул далеко не все методы, а только основные. Именно этим я сейчас и собираюсь заняться, я собираюсь пройтись по всей документации выписать методы и посмотреть какие методы и сколько времени работают. В общем работа не сложная но нудная. Скрипт замеров уже есть плюс <a href="http://fireunit.org/" rel="nofollow external">FireUnit</a> мне поможет. Записывать буду имя тестируемого метода, количество вызовов внутри него, соотношение количества внутренних вызовов к количеству выбранных элементов (O), среднее время. Если O меньше 2n, то оно не записывается и считается примерно равным одному n , а n == 40, потому что в примере участвует 40 дивов. Для примера была взята страницы выдачи результатов Google и чуть-чуть подправлена. Да и еще есть такие методы которые работаю только с первым элементом коллекции, в основном получают значение его параметров, для них вноситься отдельная правка.</p>
<p>Пример отрабатывает долго поэтому я вынес его в отдельный файл, смотрите чтоб не подвесил ваш браузер. Хотел сделать еще сравнение по версиям типа 1.3.2 vs 1.2.6 но последний даже не смог полностью отработать - виснет, поэтому предлагаю тем кому действительно интересно сохранить страницу поправить версию и запустить локально, закомментировав половину кода. И там еще какие-то глюки с replaceAll, вроде как из-за него виснет. Но могу сказать заранее, что хоть в более новой версии показатель О выше (больше вызовов функций на каждый элемент), но работает она в среднем быстрее.</p>
<p>И последние перед тем как я дам ссылку на пример его нужно смотреть в <a href="http://www.mozilla.com/en-US/" rel="nofollow external">FireFox</a> c установленным <a href="http://getfirebug.com/" rel="nofollow external">FireBug</a> и <a href="http://fireunit.org/" rel="nofollow external">FireUnit</a>. А вот и <a href="http://mabp.kiev.ua/content/source/jquery_test.html" rel="nofollow external">ссылка на пример</a>.</p>
<p>Для тех кто по каким-то религиозным причинам не хочет ставить себе всё это публикую результат теста проведенного на моей машине.</p>
<style>

.myTbl td {border:1px solid #CCCCCC;}
.myTbl h2 {border-bottom:none; }

</style>
<table class="myTbl">
<tbody>
<tr>
<td colspan="4">
<h2>Attributes/Attr</h2>
</td>
</tr>
<tr>
<td>divs.attr("align");</td>
<td>6</td>
<td>O(6n)</td>
<td>0.075</td>
</tr>
<tr>
<td>divs.attr({align: "center"});</td>
<td>284</td>
<td>O(7n)</td>
<td>2.381</td>
</tr>
<tr>
<td>divs.attr("align", "center");</td>
<td>284</td>
<td>O(7n)</td>
<td>2.627</td>
</tr>
<tr>
<td>divs.attr("align", function () {});</td>
<td>324</td>
<td>O(8n)</td>
<td>2.089</td>
</tr>
<tr>
<td>divs.removeAttr("align");</td>
<td>204</td>
<td>O(5n)</td>
<td>2.29</td>
</tr>
<tr>
<td colspan="4">
<h2>Attributes/Class</h2>
</td>
</tr>
<tr>
<td>divs.addClass("test");</td>
<td>223</td>
<td>O(6n)</td>
<td>1.706</td>
</tr>
<tr>
<td>divs.addClass("test");</td>
<td>244</td>
<td>O(6n)</td>
<td>1.255</td>
</tr>
<tr>
<td>divs.hasClass("test");</td>
<td>11</td>
<td/>
<td>0.286</td>
</tr>
<tr>
<td>divs.removeClass("test");</td>
<td>304</td>
<td>O(8n)</td>
<td>2.188</td>
</tr>
<tr>
<td>divs.removeClass("test");</td>
<td>247</td>
<td>O(6n)</td>
<td>1.795</td>
</tr>
<tr>
<td>divs.toggleClass("test");</td>
<td>303</td>
<td>O(8n)</td>
<td>2.271</td>
</tr>
<tr>
<td>divs.toggleClass("test");</td>
<td>384</td>
<td>O(10n)</td>
<td>2.739</td>
</tr>
<tr>
<td>divs.toggleClass("test", true);</td>
<td>223</td>
<td>O(6n)</td>
<td>1.792</td>
</tr>
<tr>
<td>divs.toggleClass("test", true);</td>
<td>244</td>
<td>O(6n)</td>
<td>1.277</td>
</tr>
<tr>
<td>divs.toggleClass("test", false);</td>
<td>247</td>
<td>O(6n)</td>
<td>1.81</td>
</tr>
<tr>
<td>divs.toggleClass("test", false);</td>
<td>304</td>
<td>O(8n)</td>
<td>2.26</td>
</tr>
<tr>
<td colspan="4">
<h2>Attributes/HTML</h2>
</td>
</tr>
<tr>
<td>divs.html();</td>
<td>2</td>
<td>O(2n)</td>
<td>2.221</td>
</tr>
<tr>
<td>divs.html("&lt;p&gt;test&lt;/p&gt;");</td>
<td>9907</td>
<td>O(6n+n<sup>2</sup>)</td>
<td>71.238</td>
</tr>
<tr>
<td colspan="4">
<h2>Attributes/Text</h2>
</td>
</tr>
<tr>
<td>divs.text();</td>
<td>6500</td>
<td>O(4n+n<sup>2</sup>)</td>
<td>35.785</td>
</tr>
<tr>
<td>divs.text("test");</td>
<td>9902</td>
<td>O(6n+n<sup>2</sup>)</td>
<td>71.749</td>
</tr>
<tr>
<td colspan="4">
<h2>Attributes/Value</h2>
</td>
</tr>
<tr>
<td>divs.val();</td>
<td>4</td>
<td>O(4n)</td>
<td>0.04</td>
</tr>
<tr>
<td>divs.val("myValue");</td>
<td>124</td>
<td>O(3n)</td>
<td>1.046</td>
</tr>
<tr>
<td>divs.val(["myValue"]);</td>
<td>124</td>
<td>O(3n)</td>
<td>1.082</td>
</tr>
<tr>
<td colspan="4">
<h2>Traversing/Filtering</h2>
</td>
</tr>
<tr>
<td>divs.eq(0);</td>
<td>9</td>
<td/>
<td>0.05</td>
</tr>
<tr>
<td>divs.hasClass("test");</td>
<td>11</td>
<td/>
<td>0.25</td>
</tr>
<tr>
<td>divs.filter("div");</td>
<td>100</td>
<td>O(3n)</td>
<td>0.606</td>
</tr>
<tr>
<td>divs.filter(function () {});</td>
<td>90</td>
<td>O(2n)</td>
<td>0.248</td>
</tr>
<tr>
<td>divs.is("div");</td>
<td>52</td>
<td/>
<td>0.345</td>
</tr>
<tr>
<td>divs.is(".test");</td>
<td>10</td>
<td/>
<td>0.228</td>
</tr>
<tr>
<td>divs.map(function () {});</td>
<td>89</td>
<td>O(2n)</td>
<td>0.24</td>
</tr>
<tr>
<td>divs.not("div");</td>
<td>104</td>
<td>O(3n)</td>
<td>0.651</td>
</tr>
<tr>
<td>divs.not(".test");</td>
<td>20</td>
<td/>
<td>0.329</td>
</tr>
<tr>
<td>divs.slice(0, 1);</td>
<td>8</td>
<td/>
<td>0.048</td>
</tr>
<tr>
<td colspan="4">
<h2>Traversing/Finding</h2>
</td>
</tr>
<tr>
<td>divs.add("span");</td>
<td>106</td>
<td>O(3n)</td>
<td>1.369</td>
</tr>
<tr>
<td>divs.children("span");</td>
<td>216</td>
<td>O(5n)</td>
<td>3.238</td>
</tr>
<tr>
<td>divs.closest("div");</td>
<td>690</td>
<td>O(17n)</td>
<td>5.565</td>
</tr>
<tr>
<td>divs.closest("span");</td>
<td>4612</td>
<td>O(3n+n<sup>2</sup>)</td>
<td>38.784</td>
</tr>
<tr>
<td>divs.contents();</td>
<td>316</td>
<td>O(8n)</td>
<td>3.952</td>
</tr>
<tr>
<td>divs.find("span");</td>
<td>462</td>
<td>O(12n)</td>
<td>5.147</td>
</tr>
<tr>
<td>divs.next("span");</td>
<td>121</td>
<td>O(3n)</td>
<td>1.145</td>
</tr>
<tr>
<td>divs.nextAll("span");</td>
<td>161</td>
<td>O(4n)</td>
<td>1.761</td>
</tr>
<tr>
<td>divs.offsetParent("span");</td>
<td>4</td>
<td>O(4n)</td>
<td>0.311</td>
</tr>
<tr>
<td>divs.parent("span");</td>
<td>100</td>
<td>O(3n)</td>
<td>0.746</td>
</tr>
<tr>
<td>divs.parents("span");</td>
<td>383</td>
<td>O(10n)</td>
<td>3.348</td>
</tr>
<tr>
<td>divs.prev("span");</td>
<td>136</td>
<td>O(3n)</td>
<td>1.445</td>
</tr>
<tr>
<td>divs.prevAll("span");</td>
<td>190</td>
<td>O(5n)</td>
<td>2.478</td>
</tr>
<tr>
<td>divs.siblings("span");</td>
<td>251</td>
<td>O(6n)</td>
<td>3.403</td>
</tr>
<tr>
<td colspan="4">
<h2>Traversing/Changing</h2>
</td>
</tr>
<tr>
<td>divs.andSelf();</td>
<td>55</td>
<td/>
<td>0.425</td>
</tr>
<tr>
<td>divs.end();</td>
<td>2</td>
<td/>
<td>0.006</td>
</tr>
<tr>
<td colspan="4">
<h2>Manipulation/Changing Contents</h2>
</td>
</tr>
<tr>
<td>divs.html();</td>
<td>2</td>
<td>O(2n)</td>
<td>2.196</td>
</tr>
<tr>
<td>divs.html("&lt;p&gt;test&lt;/p&gt;");</td>
<td>9907</td>
<td>O(6n+n<sup>2</sup>)</td>
<td>72.032</td>
</tr>
<tr>
<td>divs.text();</td>
<td>6500</td>
<td>O(4n+n<sup>2</sup>)</td>
<td>36.241</td>
</tr>
<tr>
<td>divs.text("test");</td>
<td>9902</td>
<td>O(6n+n<sup>2</sup>)</td>
<td>71.661</td>
</tr>
<tr>
<td colspan="4">
<h2>Manipulation/Inserting Inside</h2>
</td>
</tr>
<tr>
<td>divs.append("&lt;p&gt;test&lt;/p&gt;");</td>
<td>133</td>
<td>O(3n)</td>
<td>1.675</td>
</tr>
<tr>
<td>divs.appendTo("#form");</td>
<td>176</td>
<td>O(4n)</td>
<td>3.429</td>
</tr>
<tr>
<td>divs.appendTo("form");</td>
<td>191</td>
<td>O(5n)</td>
<td>3.506</td>
</tr>
<tr>
<td>divs.appendTo(".form");</td>
<td>192</td>
<td>O(5n)</td>
<td>3.534</td>
</tr>
<tr>
<td>divs.appendTo("&lt;form/&gt;");</td>
<td>184</td>
<td>O(5n)</td>
<td>3.149</td>
</tr>
<tr>
<td>divs.prepend("&lt;p&gt;test&lt;/p&gt;");</td>
<td>133</td>
<td>O(3n)</td>
<td>2.155</td>
</tr>
<tr>
<td>divs.prependTo("form");</td>
<td>191</td>
<td>O(5n)</td>
<td>3.509</td>
</tr>
<tr>
<td colspan="4">
<h2>Manipulation/Inserting Outside</h2>
</td>
</tr>
<tr>
<td>divs.after("form");</td>
<td>91</td>
<td>O(2n)</td>
<td>1.903</td>
</tr>
<tr>
<td>divs.after("&lt;form/&gt;");</td>
<td>94</td>
<td>O(2n)</td>
<td>2.078</td>
</tr>
<tr>
<td>divs.before("form");</td>
<td>91</td>
<td>O(2n)</td>
<td>1.457</td>
</tr>
<tr>
<td>divs.before("&lt;form/&gt;");</td>
<td>94</td>
<td>O(2n)</td>
<td>1.611</td>
</tr>
<tr>
<td>divs.insertAfter("form");</td>
<td>190</td>
<td>O(5n)</td>
<td>3.553</td>
</tr>
<tr>
<td>divs.insertBefore("form");</td>
<td>190</td>
<td>O(5n)</td>
<td>3.528</td>
</tr>
<tr>
<td colspan="4">
<h2>Manipulation/Inserting Around</h2>
</td>
</tr>
<tr>
<td>divs.wrap("&lt;span/&gt;");</td>
<td>2644</td>
<td>O(2n+n<sup>2</sup>)</td>
<td>22.655</td>
</tr>
<tr>
<td>divs.wrap(document.createElement("span"));</td>
<td>2524</td>
<td>O(2n+n<sup>2</sup>)</td>
<td>20.983</td>
</tr>
<tr>
<td>divs.wrapAll("&lt;span/&gt;");</td>
<td>181</td>
<td>O(5n)</td>
<td>7.886</td>
</tr>
<tr>
<td>divs.wrapAll(document.createElement("span"));</td>
<td>178</td>
<td>O(4n)</td>
<td>7.85</td>
</tr>
<tr>
<td>divs.wrapInner("&lt;span/&gt;");</td>
<td>3245</td>
<td>O(2n+n<sup>2</sup>)</td>
<td>31.313</td>
</tr>
<tr>
<td>divs.wrapInner(document.createElement("span"));</td>
<td>3140</td>
<td>O(2n+n<sup>2</sup>)</td>
<td>29.689</td>
</tr>
<tr>
<td colspan="4">
<h2>Manipulation/Replacing</h2>
</td>
</tr>
<tr>
<td>divs.replaceWith("&lt;span/&gt;");</td>
<td>7636</td>
<td>O(5n+n<sup>2</sup>)</td>
<td>60.003</td>
</tr>
<tr>
<td>divs.replaceAll("span");</td>
<td>35</td>
<td/>
<td>1.854</td>
</tr>
<tr>
<td colspan="4">
<h2>Manipulation/Removing</h2>
</td>
</tr>
<tr>
<td>divs.empty();</td>
<td>9774</td>
<td>O(6n+n<sup>2</sup>)</td>
<td>72.261</td>
</tr>
<tr>
<td>divs.remove();</td>
<td>6982</td>
<td>O(4n+n<sup>2</sup>)</td>
<td>55.583</td>
</tr>
<tr>
<td colspan="4">
<h2>Manipulation/Copying</h2>
</td>
</tr>
<tr>
<td>divs.clone();</td>
<td>90</td>
<td>O(2n)</td>
<td>3.304</td>
</tr>
<tr>
<td>divs.clone(true);</td>
<td>5309</td>
<td>O(3n+n<sup>2</sup>)</td>
<td>52.121</td>
</tr>
<tr>
<td colspan="4">
<h2>CSS/CSS</h2>
</td>
</tr>
<tr>
<td>divs.css("color");</td>
<td>4</td>
<td>O(4n)</td>
<td>0.136</td>
</tr>
<tr>
<td>divs.css("color", "red");</td>
<td>205</td>
<td>O(5n)</td>
<td>2.119</td>
</tr>
<tr>
<td>divs.css({color: "red", border: "1px solid red"});</td>
<td>365</td>
<td>O(9n)</td>
<td>4.112</td>
</tr>
<tr>
<td colspan="4">
<h2>CSS/Positioning</h2>
</td>
</tr>
<tr>
<td>divs.offset();</td>
<td>2</td>
<td>O(2n)</td>
<td>0.337</td>
</tr>
<tr>
<td>divs.position();</td>
<td>14</td>
<td/>
<td>0.51</td>
</tr>
<tr>
<td>divs.scrollTop();</td>
<td>2</td>
<td>O(2n)</td>
<td>0.112</td>
</tr>
<tr>
<td>divs.scrollTop(10);</td>
<td>44</td>
<td/>
<td>0.475</td>
</tr>
<tr>
<td>divs.scrollLeft();</td>
<td>2</td>
<td>O(2n)</td>
<td>0.112</td>
</tr>
<tr>
<td>divs.scrollLeft(10);</td>
<td>44</td>
<td/>
<td>0.468</td>
</tr>
<tr>
<td colspan="4">
<h2>CSS/Height and Width</h2>
</td>
</tr>
<tr>
<td>divs.height();</td>
<td>12</td>
<td>O(12n)</td>
<td>24.01</td>
</tr>
<tr>
<td>divs.height(10);</td>
<td>206</td>
<td>O(5n)</td>
<td>2.271</td>
</tr>
<tr>
<td>divs.width();</td>
<td>12</td>
<td>O(12n)</td>
<td>10.789</td>
</tr>
<tr>
<td>divs.width(10);</td>
<td>206</td>
<td>O(5n)</td>
<td>2.189</td>
</tr>
<tr>
<td>divs.innerHeight();</td>
<td>10</td>
<td>O(10n)</td>
<td>10.782</td>
</tr>
<tr>
<td>divs.innerWidth();</td>
<td>10</td>
<td>O(10n)</td>
<td>10.533</td>
</tr>
<tr>
<td>divs.outerHeight();</td>
<td>5</td>
<td>O(5n)</td>
<td>10.195</td>
</tr>
<tr>
<td>divs.outerHeight(true);</td>
<td>10</td>
<td>O(10n)</td>
<td>10.483</td>
</tr>
<tr>
<td>divs.outerWidth();</td>
<td>5</td>
<td>O(5n)</td>
<td>10.216</td>
</tr>
<tr>
<td>divs.outerWidth(true);</td>
<td>10</td>
<td>O(10n)</td>
<td>10.342</td>
</tr>
<tr>
<td colspan="4">
<h2>Effects/Basics</h2>
</td>
</tr>
<tr>
<td>divs.show();</td>
<td>162</td>
<td>O(4n)</td>
<td>8.007</td>
</tr>
<tr>
<td>divs.show();</td>
<td>253</td>
<td>O(6n)</td>
<td>2.991</td>
</tr>
<tr>
<td>divs.hide();</td>
<td>162</td>
<td>O(4n)</td>
<td>4.168</td>
</tr>
<tr>
<td>divs.hide();</td>
<td>42</td>
<td/>
<td>0.393</td>
</tr>
<tr>
<td>divs.toggle();</td>
<td>845</td>
<td>O(21n)</td>
<td>23.782</td>
</tr>
<tr>
<td>divs.toggle(true);</td>
<td>325</td>
<td>O(8n)</td>
<td>10.757</td>
</tr>
<tr>
<td>divs.toggle(false);</td>
<td>325</td>
<td>O(8n)</td>
<td>5.31</td>
</tr>
<tr>
<td colspan="4">
<h2>Effects/Sliding</h2>
</td>
</tr>
<tr>
<td>divs.slideUp();</td>
<td>1328</td>
<td>O(33n)</td>
<td>8.703</td>
</tr>
<tr>
<td>divs.slideUp();</td>
<td>3679</td>
<td>O(2n+n<sup>2</sup>)</td>
<td>101.01</td>
</tr>
<tr>
<td>divs.slideDown();</td>
<td>2280</td>
<td>O(n<sup>2</sup>)</td>
<td>62.462</td>
</tr>
<tr>
<td>divs.slideDown();</td>
<td>2200</td>
<td>O(n<sup>2</sup>)</td>
<td>61.412</td>
</tr>
<tr>
<td>divs.slideToggle();</td>
<td>4927</td>
<td>O(3n+n<sup>2</sup>)</td>
<td>156.157</td>
</tr>
<tr>
<td colspan="4">
<h2>Effects/Fading</h2>
</td>
</tr>
<tr>
<td>divs.fadeIn();</td>
<td>1448</td>
<td>O(36n)</td>
<td>22.658</td>
</tr>
<tr>
<td>divs.fadeIn();</td>
<td>1368</td>
<td>O(34n)</td>
<td>21.313</td>
</tr>
<tr>
<td>divs.fadeOut();</td>
<td>1328</td>
<td>O(33n)</td>
<td>9.04</td>
</tr>
<tr>
<td>divs.fadeOut();</td>
<td>1288</td>
<td>O(32n)</td>
<td>15.798</td>
</tr>
<tr>
<td>divs.fadeTo(0, 0.5);</td>
<td>1968</td>
<td>O(n<sup>2</sup>)</td>
<td>17.539</td>
</tr>
</tbody>
</table>]]></content:encoded>
			<wfw:commentRss>http://mabp.kiev.ua/2009/03/29/jquery-profiling/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Тестирование производительности селекторов jQuery</title>
		<link>http://mabp.kiev.ua/2009/02/21/testing-productivity-jquery-selectors/</link>
		<comments>http://mabp.kiev.ua/2009/02/21/testing-productivity-jquery-selectors/#comments</comments>
		<pubDate>Sat, 21 Feb 2009 15:11:59 +0000</pubDate>
		<dc:creator>CTAPbIu_MABP</dc:creator>
				<category><![CDATA[CSS]]></category>
		<category><![CDATA[HTML]]></category>
		<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[Программирование]]></category>
		<category><![CDATA[jquery]]></category>
		<category><![CDATA[perfomance]]></category>

		<guid isPermaLink="false">http://mabp.kiev.ua/?p=785</guid>
		<description><![CDATA[Всем известно что jQuery может все. Но далеко не всем известно что jQuery это может несколькими способами. Вот именно об этом я и хочу сегодня поговорить, а заодно проверить какой способ быстрее. Подвергну тестам вышедшую сегодня в свет jQuery 1.3.2, вставленную в страницу результатов Google, общий вес html файла чуть более чем 25кб, все возможные [...]]]></description>
			<content:encoded><![CDATA[<p>Всем известно что <a href="http://mabp.kiev.ua/tag/jquery/">jQuery</a> может все. Но далеко не всем известно что <a href="http://mabp.kiev.ua/tag/jquery/">jQuery</a> это может несколькими способами. Вот именно об этом я и хочу сегодня поговорить, а заодно проверить какой способ быстрее. Подвергну тестам вышедшую сегодня в свет <a href="http://mabp.kiev.ua/tag/jquery/">jQuery 1.3.2</a>, вставленную в страницу результатов Google, общий вес html файла чуть более чем 25кб, все возможные тэги в наличии. Еще хотелось бы акцентировать внимание на том что я не буду использовать псевдо-селектор :not() , так как смысловой нагрузки он не несет, хотя дает еще один способ выбрать элемент.</p>
<span id="more-785">
</span>
<p>Первым я протестирую контекст, проверять буду вот таким скриптом</p>
<pre>
<code class="javascript">

var start = new Date();
for(var i=0;i&lt;1000;i++)
	$....
var stop = new Date();
console.log(stop-start)

</code>
</pre>
<p>Я не будем добавлять ничего нового к коду Google, а только попробуем собрать коллекцию li.</p>
<style>

.myTbl td {border:1px solid #CCCCCC;}

</style>
<table class="myTbl">
<tr>
<td>$("li", document)</td>
<td>120</td>
</tr>
<tr>
<td>$("li", "ol")</td>
<td>220</td>
</tr>
</table>
<p>Вывод из первого эксперимента - сам по себе контекст это еще один поиск по DOM дереву, поэтому задавать контекст стоит только если второй запрос ограничит первый на столько что сэкономленное время будет больше чем время второго запроса, такого эффекта можно добиться например указав id родителя (не обязательного прямого) в контексте.</p>
<p>Второй пример покажет как лучше всего получить всех прямых потомков.</p>
<table class="myTbl">
<tr>
<td>$("ol>*")</td>
<td>1800+</td>
</tr>
<tr>
<td>$("ol>li")</td>
<td>215</td>
</tr>
<tr>
<td>$("ol").children("li")</td>
<td>330</td>
</tr>
</table>
<p>Не используйте звездочку - это не оправдано в 99.9% случаев (а в этом примере * возвращает еще и затесавшийся случайно элемент link) и не стоит без особой надобности использовать метод children - он тормозит.</p>
<p>В третьем примере я протестирую получение первого прямого потомка.</p>
<table class="myTbl">
<tr>
<td>$("ol>li:first")</td>
<td>375</td>
</tr>
<tr>
<td>$("ol>li:eq(0)")</td>
<td>380</td>
</tr>
<tr>
<td>$("ol>li").eq(0)</td>
<td>240</td>
</tr>
<tr>
<td>$("ol:first-child")</td>
<td>140</td>
</tr>
<tr>
<td>$("ol:nth-child(1)")</td>
<td>165</td>
</tr>
<tr>
<td>$("ol").children("li")</td>
<td>330</td>
</tr>
<tr>
<td>$("ol").find(":first-child")</td>
<td>1500+</td>
</tr>
<tr>
<td>$("ol").find(":nth-child(1)")</td>
<td>2600+</td>
</tr>
</table>
<p>Не могу найти разумного объяснения почему так тормозит find, но наверное на это есть какие-то причины. А еще интересна разница в использовании eq(0) как селектора и как метода - дольше в полтора раза.</p>
<p>Думаю стоит отвлечся от потомков и протестировать атрибуты. Вытащим input у которых type=radio</p>
<table class="myTbl">
<tr>
<td>$(":radio")</td>
<td>1500+</td>
</tr>
<tr>
<td>$("[type=radio]")</td>
<td>2400+</td>
</tr>
<tr>
<td>$("input:radio")</td>
<td>200</td>
</tr>
<tr>
<td>$("input[type=radio]")</td>
<td>240</td>
</tr>
<tr>
<td>$("input").filter("[type=radio]")</td>
<td>320</td>
</tr>
</table>
<p>Похоже фильтром тоже не стоит злоупотреблять, а псевдо-селекторы придуманы не только для удобства в написании</p>
<p>Ну и конечно надо доколупаться до классов а то было бы не честно</p>
<table class="myTbl">
<tr>
<td>$("[class^=gac]")<br />
<td>2700+<br />
</tr>
<tr>
<td>$(".gac_a, .gac_c, .gac_d, .gac_e, .gac_m")<br />
<td>1200+<br />
</tr>
<tr>
<td>$(".gac_a").add(".gac_c").add(".gac_d").add(".gac_e").add(".gac_m")<br />
<td>1100+</td>
</tr>
</table>
<p>Магия...</p>]]></content:encoded>
			<wfw:commentRss>http://mabp.kiev.ua/2009/02/21/testing-productivity-jquery-selectors/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Ускоряем вставку в DOM дерево</title>
		<link>http://mabp.kiev.ua/2009/02/19/speed-up-dom-injection/</link>
		<comments>http://mabp.kiev.ua/2009/02/19/speed-up-dom-injection/#comments</comments>
		<pubDate>Thu, 19 Feb 2009 21:31:33 +0000</pubDate>
		<dc:creator>CTAPbIu_MABP</dc:creator>
				<category><![CDATA[CSS]]></category>
		<category><![CDATA[HTML]]></category>
		<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[Программирование]]></category>
		<category><![CDATA[jquery]]></category>
		<category><![CDATA[perfomance]]></category>

		<guid isPermaLink="false">http://mabp.kiev.ua/?p=745</guid>
		<description><![CDATA[После написания прошлой статьи в котором рассказывал, как можно ускорить селекторы, объясняя внутреннее устройство jQuery, я решил написать еще один пост, в котором хочу объяснить как и почему можно ускорить вставку в DOM дерево. Я хочу углубиться в теорию того как jQuery обрабатывает переданный ей фрагмент html разметки на примере создания простого дива.


Пример первый - [...]]]></description>
			<content:encoded><![CDATA[<p>После написания <a href="http://mabp.kiev.ua/2009/02/07/speed-up-jquery-selectors/">прошлой статьи</a> в котором рассказывал, как можно ускорить селекторы, объясняя внутреннее устройство <a href="http://mabp.kiev.ua/tag/jquery/">jQuery</a>, я решил написать еще один пост, в котором хочу объяснить как и почему можно ускорить вставку в DOM дерево. Я хочу углубиться в теорию того как <a href="http://mabp.kiev.ua/tag/jquery/">jQuery</a> обрабатывает переданный ей фрагмент html разметки на примере создания простого дива.</p>
<span id="more-745">
</span>
<p>Пример первый - простой, я пробую создать пустой див. В обычном <a href="http://mabp.kiev.ua/tag/javascript/">JavaScript</a> это выглядело бы как одна строка</p>
<pre>
<code class="javascript">

document.createElement("div");

</code>
</pre>
<p>но сначала нужно убедиться что мы хотим создать именно пустой див</p>
<pre>
<code class="javascript">

// вызываем jQuery с селектором (а точнее с фрагментом html кода), без контекста
$("&lt;div/&gt;");

</code>
</pre>
<p>дальше идет общая часть для обоих примеров, мы получаем наш текст в переменную string и пытаемся определить что же получили</p>
<pre>
<code class="javascript">

// проверяем что мы получили html а не селектор
var html = /^[^&lt;]*(&lt;(.|\s)+&#038;gt)[^&gt;]*$|^#([\w-]+)$/.exec(string)
if(html &#038;&#038; (html[1] || !context)){
	// это для красоты
	html = html[1];
	// проверяем что наш html просто один тэг
	var tag = /^&lt;(\w+)\s*\/?&gt;$/.exec(html);
	if(tag[0])
		// создаем и возвращаем новый элемент
		return document.createElement(tag[0]);
}

</code>
</pre>
<p>Было бы крайне интересно, если бы можно было передавать еще и контекст в котором нужно создать элемент, тогда можно было бы не писать .appendTo(), но к сожалению это слишком сложно и накладывает кучу ограничений.</p>
<p>Второй пример - сложный, нам не просто надо создать див но надо добавить к нему свойство style и текст</p>
<pre>
<code class="javascript">

// вызываем jQuery
$("&lt;div style='position:absolute;'&gt;text&lt;/div&gt;");

</code>
</pre>
<p>Вторая проверка из прошлого фрагмента кода не пройдена и мы должны уточнить, что же надо создать, попутно создаем вспомогательный див который будет служить нам оберткой</p>
<pre>
<code class="javascript">

var div = context.createElement("div");

</code>
</pre>
<p>Приводим html к виду xhtml, это значит что если бы у нас была строка<br/>
<br />
&lt;div style='position:absolute;'/&gt;<br/>
<br />
то она превратилась бы в <br/>
<br />
&lt;div style='position:absolute;'&gt;&lt;/div&gt;<br/>
<br />
это не относиться к перечисленым тэгам</p>
<pre>
<code class="javascript">

html = html.replace(/(&lt;(\w+)[^&gt;]*?)\/&gt;/g, function(all, front, tag){
	return tag.match(/^(abbr|br|col|img|input|link|meta|param|hr|area|embed)$/i) ?
		all :
		front + "&gt;&lt;/" + tag + "&gt;";
});

</code>
</pre>
<p>Затем нужно проверить целостность html'a если мы пытаемся создать элементы которые изначально должны быть вложены в другие, то их следует сначала вложить а потом создать.</p>
<pre>
<code class="javascript">

var wrap =
	!tags.indexOf("&lt;opt") &#038;&#038;
	[ 1, "&lt;select multiple='multiple'&gt;", "&lt;/select&gt;" ] ||


	!tags.indexOf("&lt;leg") &#038;&#038;
	[ 1, "&lt;fieldset&gt;", "&lt;/fieldset&gt;" ] ||


	tags.match(/^&lt;(thead|tbody|tfoot|colg|cap)/) &#038;&#038;
	[ 1, "&lt;table&gt;", "&lt;/table&gt;" ] ||


	!tags.indexOf("&lt;tr") &#038;&#038;
	[ 2, "&lt;table&gt;&lt;tbody&gt;", "&lt;/tbody&gt;&lt;/table&gt;" ] ||


	(!tags.indexOf("&lt;td") || !tags.indexOf("&lt;th")) &#038;&#038;
	[ 3, "&lt;table&gt;&lt;tbody&gt;&lt;tr&gt;", "&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;" ] ||


	!tags.indexOf("&lt;col") &#038;&#038;
	[ 2, "&lt;table&gt;&lt;tbody&gt;&lt;/tbody&gt;&lt;colgroup&gt;", "&lt;/colgroup&gt;&lt;/table&gt;" ] ||


	// IE не может сериализировать &lt;link&gt; и &lt;script&gt; тэги нормально
	!jQuery.support.htmlSerialize &#038;&#038;
	[ 1, "div&lt;div&gt;", "&lt;/div&gt;" ] ||


	[ 0, "", "" ];


div.innerHTML = wrap[1] + elem + wrap[2];

</code>
</pre>
<p>Ну и естественно обертки нам не нужны, мы их отбрасываем</p>
<pre>
<code class="javascript">

while ( wrap[0]-- )
	div = div.lastChild;

</code>
</pre>
<p>Если у нас было несколько элементов верхнего уровня <br/>
<br />
&lt;div&gt;text1&lt;/div&gt;&lt;div&gt;text2&lt;/div&gt;<br/>
<br />
мы бы это все еще разбили в массив, но так как мы этого не знаем и для однородности отдачи мы все равно это делаем</p>
<pre>
<code class="javascript">

array = Array.prototype.push.apply([], div.lastChild);

</code>
</pre>
<p>Статья изначально предполагалась для объяснения, почему для манипуляций с единичными элементами DOM лучше создавать пустые элементы, а потом методами <a href="http://mabp.kiev.ua/tag/jquery/">jQuery</a>  добавлять им свойства. </p>
<pre>
<code class="javascript">

$("&lt;div/&gt;").text("text")
// лучше чем ?
$("&lt;div&gt;text&lt;/div&gt;")

</code>
</pre>
<p>Но в ходе написания статьи было обнаружены интересные подробности: оказывается первый вариант выполняется, чуть более чем, вдвое дольше второго</p>
<pre>
<code class="javascript">

var start = new Date();
for(var i=0;i<1000;i++)
	$("&lt;div/&gt;").text("text")
var stop = new Date();
alert(stop-start) // 280


var start = new Date();
for(var i=0;i<1000;i++)
	$("&lt;div&gt;text&lt;/div&gt;")
var stop = new Date();
alert(stop-start) // 125

</code>
</pre>
<p>Я очень сильно удивился и посмотрел код метода .text() он попутно успевает вызвать метод .empty(), который очень широким жестом удаляет всех потомков текущей ноды. Теория начала с треском проваливаться, но не все методы используют .empty() и я составил табличку, какой метод, сколько времени выполняеться и картина снова стала меня радовать.</p>
<style>

.myTbl td {border:1px solid #CCCCCC;}

</style>
<table class="myTbl">
<tr>
<td>$("&lt;div&gt;text&lt;/div&gt;")</td>
<td>125</td>
</tr>
<tr>
<td>$("&lt;div/&gt;").text("text")</td>
<td>280</td>
</tr>
<tr>
<td>$("&lt;div/&gt;").html("text")</td>
<td>350</td>
</tr>
<tr>
<td>$("&lt;div attribute='value'/&gt;")</td>
<td>140</td>
</tr>
<tr>
<td>$("&lt;div/&gt;").css({property:value})</td>
<td>100</td>
</tr>
<tr>
<td>$("&lt;div/&gt;").attr({attribute:value})</td>
<td>95</td>
</tr>
<tr>
<td>$("&lt;div/&gt;").addClass("myClass")</td>
<td>75</td>
</tr>
<tr>
<td>$("&lt;input/&gt;").val("myValue")</td>
<td>70</td>
</tr>
</table>
<p>Хочу обратить ваше внимание на то что .css() это частный случай для .attr() поэтому работает чуть дольше, но лучше отображает суть происходящего.</p>
<p>Эксперимент доказал, что если требуеться создавать пустой элемент (по большей части это касаеться дивов), то всетаки лучше параметры передавать потом в методах. Это экономит до 50% времени. </p>
<p>ЗЫ: Я уже знаю что в каментах будут писать о том, что это экономия на спичках, но эта статья из большой и чистой теории и я никого не принуждаю так делать - делайте как удобнее.</p>]]></content:encoded>
			<wfw:commentRss>http://mabp.kiev.ua/2009/02/19/speed-up-dom-injection/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Обновил автокомплит</title>
		<link>http://mabp.kiev.ua/2009/02/18/autocomplete-update/</link>
		<comments>http://mabp.kiev.ua/2009/02/18/autocomplete-update/#comments</comments>
		<pubDate>Wed, 18 Feb 2009 20:16:01 +0000</pubDate>
		<dc:creator>CTAPbIu_MABP</dc:creator>
				<category><![CDATA[CSS]]></category>
		<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[Программирование]]></category>
		<category><![CDATA[autocomplete]]></category>
		<category><![CDATA[perfomance]]></category>

		<guid isPermaLink="false">http://mabp.kiev.ua/?p=737</guid>
		<description><![CDATA[Вчера весь вечер сидел дописывал в autocomplete вещи которые больше нельзя было игнорировать. А сегодня вашему вниманию предлагается changelog:



Полностью убрано определение браузеров, потому что оно не просто бесполезно а по большому счету мешает и сбивает с толку в случаи с Chrome которого jQuery определяет как Safari , а на самом деле он ведет себя как [...]]]></description>
			<content:encoded><![CDATA[<p>Вчера весь вечер сидел дописывал в <a href="http://mabp.kiev.ua/2008/04/08/autocomplete/" title="autocomplete">autocomplete</a> вещи которые больше нельзя было игнорировать. А сегодня вашему вниманию предлагается changelog:</p>
<span id="more-737">
</span>
<ul>
<li>Полностью убрано определение браузеров, потому что оно не просто бесполезно а по большому счету мешает и сбивает с толку в случаи с <a href="http://mabp.kiev.ua/tag/chrome/">Chrome</a> которого <a href="http://mabp.kiev.ua/tag/jquery/">jQuery</a> определяет как <a href="http://mabp.kiev.ua/tag/safari/">Safari</a> , а на самом деле он ведет себя как <a href="http://mabp.kiev.ua/tag/firefox/">FireFox</a> . Теперь все везде работает за счет хаков <a href="http://mabp.kiev.ua/category/programming/css/">CSS</a> и сглаживания <a href="http://mabp.kiev.ua/tag/jquery/">jQuery</a> .</li>
<li>Сделал давно прошеную фишку, чтобы можно было передавать не только видимое значение (text) но и реальное значение (value). Это работает не только с option, но также с объектами и массивами. Для объекта будет возвращаться ключ, а для массива будет возвращаться порядковый номер начиная с нуля.</li>
<li>Для того чтобы передача истинного значения выглядела аккуратно пришлось еще добавить режим select-only который не даст пользователю написать свой вариант в <a href="http://mabp.kiev.ua/2008/04/08/autocomplete/" title="autocomplete">autocomplete</a>, но как по мне это не правильно, потому что так все возвращается обратно к функциональности select-box'a.</li>
<li>По скольку до объекта <a href="http://mabp.kiev.ua/2008/04/08/autocomplete/" title="autocomplete">autocomplete</a> напрямую кроме как из событий добраться нельзя я сделал событие onSetup в котором можно произвести произвольные манипуляции с <a href="http://mabp.kiev.ua/2008/04/08/autocomplete/" title="autocomplete">autocomplete</a>  после того как он будет создан. Это позволит управлять элементами назначая им новые обработчики или стили.</li>
<li>Еще убран хак для <a href="http://mabp.kiev.ua/tag/safari/">Safari</a> так как у меня на винде в найтбилдах он больше не появляется, если он не исчез - пишите и я верну.</li>
</ul>
<p>В остальных местах еще немного порефакторил. Но это влияет только на производительность. Где-то в глубине рефакторинга решил использовать метод live для всех элементов выпадающего списка, но потом понял что для этого придется отказаться от поддержки <a href="http://mabp.kiev.ua/tag/jquery/">jQuery</a> версии 1.2.6, а мне этого не хотелось и подумав еще я сумел сделать код еще проще.</p>
<br />
]]></content:encoded>
			<wfw:commentRss>http://mabp.kiev.ua/2009/02/18/autocomplete-update/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Ускоряем селекторы в jQuery</title>
		<link>http://mabp.kiev.ua/2009/02/07/accelerates-selectors-in-jquery/</link>
		<comments>http://mabp.kiev.ua/2009/02/07/accelerates-selectors-in-jquery/#comments</comments>
		<pubDate>Sat, 07 Feb 2009 14:16:16 +0000</pubDate>
		<dc:creator>CTAPbIu_MABP</dc:creator>
				<category><![CDATA[CSS]]></category>
		<category><![CDATA[HTML]]></category>
		<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[Программирование]]></category>
		<category><![CDATA[jquery]]></category>
		<category><![CDATA[perfomance]]></category>

		<guid isPermaLink="false">http://mabp.kiev.ua/?p=663</guid>
		<description><![CDATA[Когда я вчера начинал писать эту статью я хотел написать что-то типа "селекторы для продвинутых" небольшое руководство по сложным выборкам, но как то так получилось, что я отклонился от темы в сторону объяснения внутренних механизмов jQuery и получилось что-то средние между "селекторами для продвинутых" и "перфомансом селекторов", что тоже не плохо. Объяснять как работают селекторы [...]]]></description>
			<content:encoded><![CDATA[<p>Когда я вчера начинал писать эту статью я хотел написать что-то типа "селекторы для продвинутых" небольшое руководство по сложным выборкам, но как то так получилось, что я отклонился от темы в сторону объяснения внутренних механизмов <a href="http://mabp.kiev.ua/tag/jquery/">jQuery</a> и получилось что-то средние между "селекторами для продвинутых" и "перфомансом селекторов", что тоже не плохо. Объяснять как работают селекторы я буду на простейшем примере, который лучше смотреть в <a href="http://mabp.kiev.ua/tag/firefox/">FireFox 3.1</a> или <a href="http://mabp.kiev.ua/tag/ie/">IE8</a>:</p>
<span id="more-663">
</span>
<pre>
<code class="html">

&lt;style&gt;
.myClass{
	color:red;
}
&lt;/style&gt;
&lt;select name="myName"&gt;
	&lt;option value="101"&gt;101&lt;/option&gt;
	&lt;option value="102"&gt;102&lt;/option&gt;
	&lt;option value="103"&gt;103&lt;/option&gt;
	&lt;option value="104"&gt;104&lt;/option&gt;
	&lt;option value="105"&gt;105&lt;/option&gt;
	&lt;option value="106" class="myClass"&gt;106&lt;/option&gt;
	&lt;option value="107" class="myClass"&gt;107&lt;/option&gt;
	&lt;option value="108" disabled="disabled"&gt;108&lt;/option&gt;
	&lt;option value="109" disabled="disabled"&gt;109&lt;/option&gt;
	&lt;option value="110" disabled="disabled" class="myClass"&gt;110&lt;/option&gt;
&lt;/select&gt;

</code>
</pre>
<style>

.myClass{
	color:red;
}

</style>
<br />
<select name="myName">
<option value="101">101</option>
<option value="102">102</option>
<option value="103">103</option>
<option value="104">104</option>
<option value="105">105</option>
<option value="106" class="myClass">106</option>
<option value="107" class="myClass">107</option>
<option value="108" disabled="disabled">108</option>
<option value="109" disabled="disabled">109</option>
<option value="110" disabled="disabled" class="myClass">110</option>
</select>
<p>Теперь напишем селектор который выберет все элементы которые нам видны, которые имеют класс myClass и которые неактивны.</p>
<pre>
<code class="javascript">

jQuery().ready(function($){
	// выбираем все option
	var o = $("select[name=myName] option");


	// фильтруем
	o.filter(":visible.myClass:disabled");
 });

</code>
</pre>
<p>Работает, но это не оптимальный селектор, даже если он занимает минимум символов при написании, он работает медленно. Надо понимать как это все работает и какая операция быстрее. Итак попробуем разобрать: если бы все было прекрасно и браузер поддерживал функцию document.querySelectorAll, <a href="http://mabp.kiev.ua/tag/jquery/">jQuery</a> бы передал ей селектор, но у нас не та ситуация, я специально создал такие условия чтобы querySelectorAll ничего не ускорил, ну или мы используем какой-то старый браузер который не поддерживает этот метод. Итак у нас три части селектора :visible + .myClass + :disabled , (и хотя querySelectorAll понимает второй селектор, <a href="http://mabp.kiev.ua/tag/jquery/">jQuery</a> все равно придется обрабатывать первый и третий) примерный код выглядел бы так, только намного сложнее.</p>
<pre>
<code class="javascript">

// Этот код работает идентично тому как работает jQuery


// выбираем все option без jQuery
var o = document.querySelectorAll("select[name=myName] option"),
tmp1 = [], tmp2 = [], result = [];




function isVisible(obj){
	if (obj == document) 
		return true;
	if(!obj)
		return false;
	if(obj.style.display !== "none" &#038;&#038; obj.style.visibility !== "hidden")
		return isVisible(obj.parentNode);
}


// фильтруем
for (var i=0; i&lt;o.length; i++){
	if(isVisible(o[i]))
		tmp1.push(o[i]);
} // 10


for (var i=0; i&lt;tmp1.length; i++){
	var cls = tmp1[i].className.split('/\s+/')
	for (var c in cls)
		if (cls[c] == "myClass")
			tmp2.push(tmp1[i]);
} // 3


for (var i=0; i&lt;tmp2.length; i++){
	if(tmp2[i].getAttribute("disabled"))
		result.push(tmp2[i]);
} // 1


alert(result.length); // 1

</code>
</pre>
<p>Конечно есть механизмы оптимизации, но есть и много другого кода поэтому от него отстранимся и будем решать нашу задачу. Задача очень простая надо посчитать общее количество циклов и трудозатраты на каждом цикле.</p>
<p>Итак, считаем:</p>
<ul>
<li> первый цикл обойдет все 10 элементов и трудазатраты у него возрастают пропорционально величине DOM дерева (дадим ему модификатор 3)</li>
<li>второй менее трудозатратен и его величина пропорциональна количеству классов у элемента (модификатор 2)</li>
<li>и наконец третий цикл совсем простой с минимальным количеством трудозатрат (модификатор 1).</li>
</ul>
<br />
Посчитаем общую сложность 10*3+10*2+3*1=53. Попробуем поменять местами поставив самые простые на перед - 10*1+3*2+1*3=19, получается в 2,5 раза меньше.</p>
<p>Отсюда вывод, что в примере селекторы лучше всего поменять местами в противоположном порядке - :disabled.myClass:visible.</p>
<p>Конечно пример про :visible натянут тут они все видимы но в реальной ситуации когда выбираются все абзацы текста, все может быть по другому.</p>
<p>В общем это я все клоню к тому, что для того чтобы селекторы быстро работали надо вначале писать самые простые, отсекающие как можно больше элементов, а потом сложные чтобы они фильтровали как можно меньше элементов.</p>
<p>
<strong>21.02 UPD:</strong> Несколько часов назад вышла jquery-1.3.2.js . В release note сказано что отремонтированы селекторы :visible/:hidden и действительно сейчас они полностью переписаны и не зависят от величины дом дерева.</p>
<pre>
<code class="javascript">

vae element = document.getElementById("id")
if(!element.offsetWidth &#038;&#038; !elem.offsetHeight)
	// hidden
else
	// visible

</code>
</pre>
<p>А это значит про предыдущие расчеты нужно немного подкорректировать и теперь самым 'тяжелым' является селектор класса.</p>]]></content:encoded>
			<wfw:commentRss>http://mabp.kiev.ua/2009/02/07/accelerates-selectors-in-jquery/feed/</wfw:commentRss>
		<slash:comments>23</slash:comments>
		</item>
		<item>
		<title>Автокэш</title>
		<link>http://mabp.kiev.ua/2008/05/08/autocache/</link>
		<comments>http://mabp.kiev.ua/2008/05/08/autocache/#comments</comments>
		<pubDate>Thu, 08 May 2008 16:08:29 +0000</pubDate>
		<dc:creator>CTAPbIu_MABP</dc:creator>
				<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[Программирование]]></category>
		<category><![CDATA[algorithms]]></category>
		<category><![CDATA[autocomplete]]></category>
		<category><![CDATA[perfomance]]></category>

		<guid isPermaLink="false">http://mabp.localhost/?p=209</guid>
		<description><![CDATA[Я тут недавно получил комментарий от человека со странным ником, он сказал мне, что мой autocomplete конечно всем хорош, но имеет один существенный недостаток. Получив данные от сервера через AJAX, при уточнении критерия поиска он снова обращается к серверу, вместо того чтобы фильтровать уже полученные данные.


Приведу пример. Мы ищем города начинающиеся на 'К' и получаем [...]]]></description>
			<content:encoded><![CDATA[<p>Я тут недавно получил комментарий от человека со странным ником, он сказал мне, что мой <a href="http://mabp.kiev.ua/2008/04/08/autocomplete/" title="autocomplete">autocomplete</a> конечно всем хорош, но имеет один существенный недостаток. Получив данные от сервера через AJAX, при уточнении критерия поиска он снова обращается к серверу, вместо того чтобы фильтровать уже полученные данные.</p>
<span id="more-209">
</span>
<p>Приведу пример. Мы ищем города начинающиеся на 'К' и получаем от сервера ответ ['кардуба','кардижопа','казупий','кипир'] :) При вводе второй буквы мы уже не обращаемся к серверу, а фильтруем то, что получили прошлый раз, например вторая буква 'А' тогда 'КА' = ['кардуба','кардижопа','казупий']. При вводе третьей буквы все повторяется 'КАР' =  ['кардуба','кардижопа'].</p>
<p>Я вижу два выхода из этой ситуации. </p>
<p>Первый - это простой обход всего массива каждый раз при вводе новой буквы, недостаток этого метода в том что при 1000 городах это будет занимать уже ощутимое время.</p>
<p>Второй способ полон геморроя и собственно его я и буду дальше описывать. Для его реализации понадобиться намного больше память, но намного меньше времени, я не могу сказать, что лучше память или время, но могу сказать, что второй способ хотя бы напряг мои мозги. Так вод вам понадобиться: вазелин офисный, пол литра, руки прямые, две штуки  и терпение, много терпения, хотя нет, руки, пожалуй, должны сгибаться в локтях....</p>
<p>Итак что собственно делаем. Вводим первую букву 'К', получаем от сервера ответ и сохраняем в переменной cache в таком виде.</p>
<pre>
<code class="javascript">

cache =  {
	'К':['кардуба','кардижопа','казупий','кипир']
};

</code>
</pre>
<p>Вводим вторую букву 'А', теперь у нас строка 'КА', выбираем все ключи нашего кэша и начиная с самого длинного начинаем искать вхождение ключа в строку, если находим берем массив-значение и фильтруем а потом сохраняем результат с новым ключом, в результате. </p>
<pre>
<code class="javascript">

cache = {
	'К':['кардуба','кардижопа','казупий','кипир'],
	'КА':['кардуба','кардижопа','казупий']
};

</code>
</pre>
<p>Еще раз та же процедура вводим 'Р', строка 'КАР', берем ключи объекта, разворачиваем в обратном порядке (чтобы сначала шли самые длинные), и ищем их вхождение в строку, если нашлось то снова фильтруем и сохраняем.</p>
<pre>
<code class="javascript">

cache = {
	'К':['кардуба','кардижопа','казупий','кипир'], 
	'КА':['кардуба','кардижопа','казупий'], 
	'КАР':['кардуба','кардижопа']
};

</code>
</pre>
<p>Как видите одни и те же города находятся в массиве много раз, это есть не очень экономно с точки зрения памяти но зато к ним очень быстро можно получить доступ. Реализация этого метода тоже весьма муторная и я был бы не против сильно её заоптимайзить но вот что-то никак...</p>
<pre>
<code class="javascript">



var cache = {'':['кардуба','кардижопа','казупий','кипир','другой город','еще село']},
	mask = ['к','ка','кар'];


function check(mask){
	// пытаемся быстро выйти
	if (cache[mask])
		return cache[mask]; 


	// инициализируем переменные
	var map = [], array = [];


	// получаем массив ключей
	for(var item in cache)
		array.push(item);
	// разворачиваем самыми длинными вначало
	array = array.reverse();


	// ищем...
	for(var item in array)
		// если вхождение ключа в строку имеет нулевую позицию
		if(!mask.indexOf(array[item])){
			// фильтруем
			for(var word in cache[array[item]])
				// если вхождение строки в элемент иммеет нулевую позицию
				if (!cache[array[item]][word].indexOf(mask))
					// сохраняем
					map.push(cache[array[item]][word]);
			break;
		}
	alert(map);
	cache[mask] = map
}


for (m in mask)
	check(mask[m]);





</code>
</pre>
<p>Вот такой вот алгоритм получился... Но это все было бы очень хорошо если бы наш бэкэнд (я имею ввиду сайт Киевстара) не возвращал только первых 500 найденных записей на каждый запрос, то есть если всего найдено 700 записей, то будут показаны только 500, причем первых найденных базой а не первых в алфавитном порядке.</p>
<p>Такое кэширование я конечно включу в следующий релиз <a href="http://mabp.kiev.ua/2008/04/08/autocomplete/" title="autocomplete">autocomplete</a>, но применять его сам вряд ли буду.</p>
<br />
]]></content:encoded>
			<wfw:commentRss>http://mabp.kiev.ua/2008/05/08/autocache/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Наконец-то заработал поиск</title>
		<link>http://mabp.kiev.ua/2008/04/26/search_engine_works_now/</link>
		<comments>http://mabp.kiev.ua/2008/04/26/search_engine_works_now/#comments</comments>
		<pubDate>Sat, 26 Apr 2008 15:47:10 +0000</pubDate>
		<dc:creator>CTAPbIu_MABP</dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[SQL]]></category>
		<category><![CDATA[Программирование]]></category>
		<category><![CDATA[perfomance]]></category>

		<guid isPermaLink="false">http://mabp.localhost/?p=196</guid>
		<description><![CDATA[





Статья более не актуальна, так как относилась к старому, самописному движку.



Вот я и сделал наконец поиск по сайту, вообще еще много чего надо сделать, а то я даже страницы добавляю через MySQL. Сейчас поиск ищет по заголовку, описанию и собственно телу страницы, самое приятное в нем то, что в нем есть ранжирование результатов. Если кому-то [...]]]></description>
			<content:encoded><![CDATA[<table style="background-color:#EDEFF0;width:100%;">
<tbody>
<tr>
<th>
<img src="/wp-content/themes/inove/img/warning_red.png"/>
</th>
<th style="text-align: center;">Статья более не актуальна, так как относилась к старому, самописному движку.</th>
</tr>
</tbody>
</table>
<p>Вот я и сделал наконец поиск по сайту, вообще еще много чего надо сделать, а то я даже страницы добавляю через MySQL. Сейчас поиск ищет по заголовку, описанию и собственно телу страницы, самое приятное в нем то, что в нем есть ранжирование результатов. Если кому-то интересно как такое сделать то читайте дальше.</p>
<span id="more-196">
</span>
<p>MATCH() ... AGAINST() первое, что приходит в голову, когда говорят о поиске с релевантностью, забудьте об этом бреде!!! Во-первых, это накладывает очень два ограничений: первое и главное - надо делать полнотекстовые индексы, без них никак, второе - индексы можно сделать только на таблицах MyISAM. Во вторых это просто глючно, потому что при поиске небольших фраз по большой базе их релевантность близка к абсолютному нулю, или же если слово присутствует в более 50% строк, то оно не учитывается.</p>
<pre>
<code class="sql">

SELECT 
MATCH('Content') AGAINST ('keyword1 keyword2') as Relevance 
FROM table 
WHERE MATCH ('Content') AGAINST('+keyword1 +keyword2' IN BOOLEAN MODE) 
HAVING Relevance > 0.2 
ORDER BY Relevance DESC

</code>
</pre>
<p>Это так сказать классический способ поиска, взятый с сайта самого мускула, но ИМХО это большая ошибка, тут первый MATCH выдает релевантность, а второй который IN BOOLEAN MODE используется для поиска обоих слов сразу в одной строке, если есть только одно то он выдаст 0. Если слов больше двух то это начинает давать сильную погрешность, выдавая только те ряды, где есть все слова фразы. Более того, куча MATCH сильно тормозит запрос.</p>
<p>Поэтому лучше ввести весовые коэффициенты в запрос. Коэффициенты вы раздаете сами, поэтому их общее число всегда может быть в пределах 100 и к нему можно смело добавлять знак %. Итак у нас есть три колонки title, description и text. Если в каждом из трех полей есть искомое словосочетание то релевантность 100%, при этом каждое поле имеет свой вес: заголовок - самый большой(40%), потом описание(20%) а потом уже текст(10%). Я считаю, что полное словосочетание это 70% релевантности и если есть все слова из него, то это еще 30%. немного запутал, сейчас объясню. Есть словосочетание "мама мыла раму", если оно дословно есть во всех полях то это 70%, а если в каждом из них есть "раму мыла мама", то это только 30%. В результате мы получаем либо 100% (70% все вместе и 30% каждое по отдельности) совпадение со строкой либо только 30% (каждое по отдельности) совпадение, ну это лично мое мнение.</p>
<p>Итак алгоритм будет выгладить следующим образом:</p>
<pre>
<code class="php">

$text = "мама мыла раму";
$query = "SELECT *, 
IF (title like '%".$text."%', 40, 0) + IF (title LIKE '%".str_replace(" ", "%', 5.71, 0) + IF (title LIKE '%", $text)."%', 5.71, 0) + 
IF (description like '%".$text."%', 20, 0) + IF (description LIKE '%".str_replace(" ", "%', 2.86, 0) + IF (description LIKE '%", $text)."%', 2.86, 0) +
IF (text like '%".$text."%', 10, 0) + IF (text LIKE '%".str_replace(" ", "%', 1.43, 0) + IF (text LIKE '%", $text)."%', 1.43, 0) AS rel
FROM pages 
WHERE 
(title LIKE '%".str_replace(" ", "%' OR title LIKE '%", $text)."%') OR
(description LIKE '%".str_replace(" ", "%' OR description LIKE '%", $text)."%') OR
(text LIKE '%".str_replace(" ", "%' OR text LIKE '%", $text)."%') 
ORDER BY rel DESC";

</code>
</pre>
<p>Если подставить значения будет выглядеть менее запутано но все равно непонятно </p>
<pre>
<code class="sql">

SELECT *, 
IF (title LIKE '%мама мыла раму%', 40, 0) + IF (title LIKE '%мама%', 5.71, 0) + IF (title LIKE '%мыла%', 5.71, 0) + IF (title LIKE '%раму%', 5.71, 0)  + 
IF (description LIKE '%мама мыла раму%', 20, 0) + IF (description LIKE '%мама%', 2.86, 0) + IF (description LIKE '%мыла%', 2.86, 0) + IF (description LIKE '%раму%', 2.86, 0)  + 
IF (text LIKE '%мама мыла раму%', 10, 0) + IF (text LIKE '%мама%', 1.43, 0) + IF (text LIKE '%мыла%', 1.43, 0) + IF (text LIKE '%раму%', 1.43, 0) AS rel
FROM pages 
WHERE 
(title LIKE '%мама%' OR title LIKE '%мыла%' OR title LIKE '%раму%') OR
(description LIKE '%мама%' OR description LIKE '%мыла%' OR description LIKE '%раму%') OR
(text LIKE '%мама%' OR text LIKE '%мыла%' OR text LIKE '%раму%')
ORDER BY rel DESC

</code>
</pre>
<p>Вот такой немаленький запросик получился))) Теперь попробуем разобрать что значат все эти непонятные коэффициенты 5.71, 2.86 и 1.43. Для этого я нарисую маленькую табличку.</p>
<table border="1" align="center" style="border-collapse: collapse;">
<tr>
<th>\</th>
<th>Все</th>
<th>мама</th>
<th>мыла</th>
<th>раму</th>
</tr>
<tr>
<td>title</td>
<td>40</td>
<td>5.71</td>
<td>5.71</td>
<td>5.71</td>
</tr>
<tr>
<td>description</td>
<td>20</td>
<td>2.86</td>
<td>2.86</td>
<td>2.86</td>
</tr>
<tr>
<td>text</td>
<td>10</td>
<td>1.43</td>
<td>1.43</td>
<td>1.43</td>
</tr>
</table>
<p>Сумма первого столбика получается как раз 70%, сумма всех остальных ячеек 30% в результате искомые 100% релевантности. Сумма каждого следующего столбика 10%. А сумма каждой ячейки высчитывается по формуле (вес поля) * (общее число процентов 30) / (общее число коэффициентов 7) / (количество слов), если подставить поле title то выйдет 4*30/7/3 = 5.71. В общем, я думаю, поняли, кто не понял, возьмите калькулятор!</p>
<p>В том что такой запрос отрабатывает весьма шустро вы можете перейдя на страницу поиска, там выводиться время потраченное на запрос. </p>
<p>Кстати в моем поиске еще наложена логика отключения полей, если отключено одно поле то коэффициенты становятся 3 и 4 , а если отключено два поля то оставшееся имеет коэффициент 7. И еще не забудьте удалить пробелы в начале и конце строки, а также двойные пробелы в середине. Все!</p>]]></content:encoded>
			<wfw:commentRss>http://mabp.kiev.ua/2008/04/26/search_engine_works_now/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
	</channel>
</rss>
