Случайны выбор дневника Раскрыть/свернуть полный список возможностей


Найдено 4226 сообщений
Cообщения с меткой

оптимизация - Самое интересное в блогах

Следующие 30  »
kiev2376393

NVIDIA ищет пути оптимизации ВР-изображения

Пятница, 29 Июля 2016 г. 19:26 (ссылка)

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

Читать далее...
Метки:   Комментарии (0)КомментироватьВ цитатник или сообщество
dsgroup

DNC — масло для восстановления волос

Среда, 20 Июля 2016 г. 10:12 (ссылка)

bigimg (197x700, 84Kb)
Особенно нравится это средство тем женщинам, которые имеют окрашенные волосы.
Особенно нравится это средство тем женщинам, которые имеют окрашенные волосы.

Метки:   Комментарии (0)КомментироватьВ цитатник или сообщество
rss_rss_hh_new

JIT-компилятор оптимизирует не круто, а очень круто

Понедельник, 18 Июля 2016 г. 22:12 (ссылка)

Недавно Лукас Эдер заинтересовался в своём блоге, способен ли JIT-компилятор Java оптимизировать такой код, чтобы убрать ненужный обход списка из одного элемента:



// ... а тут мы "знаем", что список содержит только одно значение
for (Object object : Collections.singletonList("abc")) {
doSomethingWith(object);
}


Вот мой ответ: JIT может даже больше. Мы будем говорить про HotSpot JVM 64 bit восьмой версии. Давайте рассмотрим вот такой простой метод, который считает суммарную длину строк из переданного списка:



static int testIterator(List list) {
int sum = 0;
for (String s : list) {
sum += s.length();
}
return sum;
}


Многим Java-программистам известно, что этот код полностью эквивалентен следующему:



static int testIterator(List list) {
int sum = 0;
Iterator it = list.iterator();
while(it.hasNext()) {
String s = it.next();
sum += s.length();
}
return sum;
}


Разумеется, в общем случае в list может оказаться всё что угодно и поэтому JIT-компилятору придётся сгенерировать честные виртуальные вызовы на месте iterator(), hasNext() и next(), что, конечно, не очень быстро. Но что случится, если мы всегда будем вызывать этот метод, подавая ему на вход singletonList? Давайте добавим простенький метод main():



public class Test {
static int res = 0;

public static void main(String[] args) {
for (int i = 0; i < 100000; i++) {
res += testIterator(Collections.singletonList("x"));
}
System.out.println(res);
}
}


Здесь мы вызываем наш testIterator в цикле достаточное количество раз, чтобы скомпилировать метод JIT-компилятором C2. Как некоторые из вас уже знают, в виртуальной машине HotSpot есть два JIT-компилятора: C1 (клиентский) и C2 (серверный). В 64-битной версии Java 8 с настройками по умолчанию они работают совместно. Сперва метод компилируется с помощью C1 (который компилирует быстро, но создаёт не очень оптимальный код). При этом в код добавляются дополнительные инструкции, которые собирают некоторую статистику (это называется "профилирование"). Это, конечно, замедляет выполнение, но пригождается в дальнейшем. Среди различных профилей собирается профиль типов. В нашем случае JVM внимательно следит, какой тип имеет параметр list при каждом вызове. И тут виртуальная машина замечает, что в 100% случаев на входе был список типа Collections$SingletonList (который возвращает метод singletonList).



Когда количество вызовов метода достигает некоторого порога, метод перекомпилируется компилятором C2, которому доступен собранный профиль. C2 делает разумное предположение, что раз до сих пор всегда был SingletonList, то и далее он будет часто попадаться. А значит, iterator() точно вызовет метод singletonIterator(). Но там уже нетривиальный объект, который, к примеру, содержит поле hasNext, чтобы отследить, что его не вызвали дважды, и кинуть если надо NoSuchElementException. Способен ли C2 с этим побороться?



Чтобы узнать ответ, мы можем попросить JIT-компилятор вывести ассемблер сгенерированный для методов. Для этого нам потребуется установить hsdis. Потом можно воспользоваться удобными инструментами вроде JITWatch или написать JMH-бенчмарк и воспользоваться опцией -perfasm. Но здесь у нас пример простой, поэтому мы обойдёмся без сторонних инструментов и просто запустим виртуальную машину с такими волшебными параметрами:



$ java -XX:+UnlockDiagnosticVMOptions -XX:+PrintCompilation -XX:+PrintAssembly Test >output.txt


Будьте осторожны: вывод этой команды может напугать маленьких детей. Но порывшись в нём, вы найдёте код, сгенерированный для нашего метода testIterator. Вот что сгенерировал C2 на платформе Intel x64 с кучей до 4 Гб:



Ассемблер, можно не вчитываться
  # {method} {0x0000000055120518} 'testIterator' '(Ljava/util/List;)I' in 'Test'
# parm0: rdx:rdx = 'java/util/List'
# [sp+0x20] (sp of caller)
0x00000000028e7560: mov %eax,-0x6000(%rsp)
0x00000000028e7567: push %rbp
0x00000000028e7568: sub $0x10,%rsp ;*synchronization entry
; - Test::testIterator@-1 (line 15)

0x00000000028e756c: mov 0x8(%rdx),%r10d ; implicit exception: dispatches to 0x00000000028e75bd
0x00000000028e7570: cmp $0x14d66a20,%r10d ; {metadata('java/util/Collections$SingletonList')}
0x00000000028e7577: jne 0x00000000028e75a0 ;*synchronization entry
; - java.util.Collections::singletonIterator@-1
; - java.util.Collections$SingletonList::iterator@4
; - Test::testIterator@3 (line 16)

0x00000000028e7579: mov 0x10(%rdx),%ebp ;*getfield element
; - java.util.Collections$SingletonList::iterator@1
; - Test::testIterator@3 (line 16)

0x00000000028e757c: mov 0x8(%rbp),%r11d ; implicit exception: dispatches to 0x00000000028e75c9
0x00000000028e7580: cmp $0x14d216d0,%r11d ; {metadata('java/lang/String')}
0x00000000028e7587: jne 0x00000000028e75b1
0x00000000028e7589: mov %rbp,%r10 ;*checkcast
; - Test::testIterator@24 (line 16)

0x00000000028e758c: mov 0xc(%r10),%r10d ;*getfield value
; - java.lang.String::length@1
; - Test::testIterator@30 (line 17)

0x00000000028e7590: mov 0xc(%r10),%eax ;*synchronization entry
; - Test::testIterator@-1 (line 15)
; implicit exception: dispatches to 0x00000000028e75d5
0x00000000028e7594: add $0x10,%rsp
0x00000000028e7598: pop %rbp
0x00000000028e7599: test %eax,-0x27b759f(%rip) # 0x0000000000130000
; {poll_return}
0x00000000028e759f: retq
... // дальше холодные пути


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



// Стандартный стековый фрейм - подобным образом начинается всякий JIT-компилированный метод
mov %eax,-0x6000(%rsp)
push %rbp
sub $0x10,%rsp
// Загружаем идентификатор класса объекта из переменной list (указатель на объект пришёл в метод в регистре rdx).
// Идентификатор класса лежит в объекте по смещению 0x8. Это похоже на вызов list.getClass().
// При этом здесь происходит неявная проверка на null. Если окажется, что передали в метод null,
// процессор сгенерирует аппаратное исключение в связи с обращением по запрещённому адресу.
// Исключение перехватит виртуальная машина и заботливо транслирует его в NullPointerException
mov 0x8(%rdx),%r10d
// Сравниваем list.getClass() с идентификатором класса Collections$SingletonList. Этот идентификатор не меняется
// за время работы JVM и, конечно, JIT его знает, поэтому это просто сравнение с константой
cmp $0x14d66a20,%r10d
// Если list - это не SingletonList, выпрыгиваем на холодный путь
jne 0x00000000028e75a0
// Читаем приватное поле Collections$SingletonList.element в регистр rbp. Хотя указатели 64-битные, при размере кучи
// меньше 4 Гб верхние 32 бита всегда нули, поэтому виртуальная машина их не хранит и копирует только 32 нижних бита в ebp
mov 0x10(%rdx),%ebp
// Читаем идентификатор класса элемента и сверяем его с идентификатором класса String (аналогично тому, что выше)
mov 0x8(%rbp),%r11d
cmp $0x14d216d0,%r11d
// Если элемент списка - не строка, выпрыгиваем наружу на холодный путь (там будет создано и выброшено ClassCastException)
jne 0x00000000028e75b1
// Читаем приватное поле String.value в регистр r10. Это массив char[], в котором хранится сама строка
mov %rbp,%r10
mov 0xc(%r10),%r10d
// Читаем длину массива в регистр eax, который стандартно используется для передачи возвращаемого значения метода
mov 0xc(%r10),%eax
// Восстановление стекового фрейма
add $0x10,%rsp
pop %rbp
// Проверка на safe-point. С её помощь JVM может забрать контроль у скомпилированного кода, например, для сборки мусора.
test %eax,-0x27b759f(%rip)
// Выход из метода
retq


Если кому-то всё ещё сложно это понять, давайте перепишем на псевдокоде:



if (list.class != Collections$SingletonList) {
goto SLOW_PATH;
}
str = ((Collections$SingletonList)list).element;
if (str.class != String) {
goto EXCEPTIONAL_PATH;
}
return ((String)str).value.length;


Видите? На горячем пути нет ни цикла, ни выделения памяти под итератор, ни одного вызова метода. Всего лишь несколько разыменований и две быстрые проверки (которые всегда были ложны, поэтому предсказание ветвлений в процессоре отработает на ура). JIT-компилятор заинлайнил всё, что можно, понял, что итератор из метода не убегает, избавился от выделения памяти, развернул цикл и даже смог удалить флаг hasNext и связанные с ним проверки, статически доказав, что они не нужны! Сложение и переменная sum также испарились. И тем не менее, метод полностью корректен. Если окажется, что при следующем вызове ему передадут не singletonList, а что-то другое, он просто уйдёт на холодный путь (который, конечно, значительно медленнее). Остальные исключительные ситуации тоже обрабатываются. Можно передать null вместо list или подсунуть в список не строку (слава type erasure) — всё это будет обработано в соответствии с семантикой языка.



Что же произойдёт, если сценарий работы программы изменится? Предположим, через некоторое время мы вообще перестали передавать в этот метод singletonList и передаём теперь всякие другие списки. На медленном пути виртуальная машина продолжает собирать статистику. Если она обнаружит, что медленный путь происходит сильно часто, JIT-компилятор перекомпилирует метод, убрав специальную обработку singletonList и вставив сразу честные виртуальные вызовы. Может ещё, кстати, сделать две ветки, если вы используете только две разные реализации списков. Этим JIT отличается от приложений скомпилированных заранее: исполняемый машинный код вашей программы может меняться, следуя за изменениями в её поведении.


Original source: habrahabr.ru.

https://habrahabr.ru/post/305894/?utm_source=habrahabr&utm_medium=rss&utm_campaign=best

Метки:   Комментарии (0)КомментироватьВ цитатник или сообщество
ermolenko_ludmila

Тормозит компьютер. Программа для очистки компьютера за 1 клик WISE CARE 365 PRO

Среда, 06 Июля 2016 г. 07:05 (ссылка)






4877129__1_ (223x52, 2Kb)
Метки:   Комментарии (1)КомментироватьВ цитатник или сообщество
NetFact

Создание веб-интерфейсов с помощью HTML и CSS. Продвинутый интенсив (2015) Видеокурс » NetFact.Ru: Скачать бесплатно – Популярная Интернет Библиотека

Вторник, 05 Июля 2016 г. 17:17 (ссылка)
netfact.ru/videotech/2331-s...okurs.html


Создание веб-интерфейсов с помощью HTML и CSS. Продвинутый интенсив (2015) Видеокурс




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



Программа интенсива:



1 раздел: организация рабочего процесса (workflow)

*Как будет организован учебный процесс на интенсиве.

*Введение в Git.

*Обзор препроцессоров.

*Обзор систем сборки.

*Обзор средств автоматизации.

*Обзор других полезных инструментов (emmet, его аналоги, и др.).



2 раздел: методология

*Вёрстка независимыми блоками (БЭМ).

*Работа с инспектором.

*Методы поиска проблем.

*Как верстать, чтобы не ломалось.



3 раздел: препроцессоры, часть 1

*LESS.

*SASS.

*Stylus.

*И компания.



4 раздел: препроцессоры, часть 2

*Продолжаем разговор о препроцессорах.



5 раздел: адаптивность, сетки

*Резиновая вёрстка.

*Медиавыражения, брейкпоинты, принцип «перестройки сетки».

*Адаптивность с фиксированными сетками.

*Адаптивность с резиновыми сетками.

*Подходы: от простой сетки к сложной, от сложной к простой.

*Обзор flexbox.



6 раздел: адаптивность, графика, типографика

*Адаптивные графика и медиаконтент.

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

*Ретина.

*SVG.

*Иконочные шрифты.



7 раздел: фреймворки

*Bootstrap и сотоварищи.

*Если вы решили создать свой фреймворк?



8 раздел: прикладной javascript, часть 1

*Улучшаем UX с помощью скриптов.

*Ajax.

*Весёлые анимации, трансформации.



9 раздел: прикладной javascript, часть 2

*Продолжаем разговор о javascript.



10 раздел: введение в автоматизацию

*Проверка кода.

*Оптимизация кода.

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

*Сборка.







Создание веб-интерфейсов с помощью HTML и CSS. Продвинутый интенсив (2015) Видеокурс Создание веб-интерфейсов с помощью HTML и CSS. Продвинутый интенсив (2015) Видеокурс Создание веб-интерфейсов с помощью HTML и CSS. Продвинутый интенсив (2015) Видеокурс






Информация о видеокурсе

Название: Создание веб-интерфейсов с помощью HTML и CSS. Продвинутый интенсив

Автор: Александр Першин., Николай Громов, Алексей Симоненко

Год выхода: 2015

Жанр: Видеокурс

Выпущено: HTML Academy

Продолжительность: 19:59:33



Файл

Формат: MP4 (+ доп. материалы)

Видео: AVC, 1152х720-1728х1080, ~200-705 Kbps

Аудио: AAC, 160-256 Kbps, 48.0 KHz

Размер файла: 5.82 Gb



Скачать: Создание веб-интерфейсов с помощью HTML и CSS. Продвинутый интенсив (2015) Видеокурс



Скачать | Download | TurboBit.net

http://turbobit.net/pngvfbnthxf1/prodvin.intensiv.rar.html



Скачать | Download | HitFile.net

http://www.hitfile.net/EDgdWyQ/prodvin.intensiv.rar.html



Скачать | Download | Файлообменник.рф

http://файлообменник.рф/6x9wgo5ox5ak/prodvin.intensiv.rar.html

Метки:   Комментарии (0)КомментироватьВ цитатник или сообщество
wrsz

Раскрутка сайта,продвижение,оптимизация,попасть в топ,сео. - Сайт webeffector2!

Воскресенье, 03 Июля 2016 г. 20:00 (ссылка)
liveinternet.ru/users/wrsz/...393863891/




SEO,оптимизация,раскрутка сайта,продвижение,попасть в топ,сео.

Метки:   Комментарии (1)КомментироватьВ цитатник или сообщество
wrsz

Без заголовка

Воскресенье, 03 Июля 2016 г. 19:56 (ссылка)

Это цитата сообщения wrsz Оригинальное сообщение

webeffector

Продвижение,оптимизация,попасть в топ,сео

Метки:   Комментарии (0)КомментироватьВ цитатник или сообщество
rss_rss_hh_new

[Из песочницы] Оптимизация веб-сервиса подсказок для почтовых адресов и ФИО

Пятница, 01 Июля 2016 г. 21:13 (ссылка)

В данной статье я хотел бы поделиться опытом разработки веб-сервиса на C++. На мой взгляд, это достаточно интересная тема, поскольку использование C++ для веб-разработки — вещь редкая и зачастую вызывает в ИТ-кругах недоумение. В Интернете можно найти много аргументов не в пользу данного подхода. Использование указателей, утечки памяти, сегфолты, отсутствие поддержки веб-стандартов «из коробки» — вот неполный перечень того, с чем нам пришлось ознакомиться прежде, чем принять решение о выборе данной технологии.



Разработка, о которой идет речь в данной статье была выполнена в 2015 году, однако предпосылки к ней появились значительно раньше. Все началось с того, что в 2008-ом году у нас возникла идея разработать веб-сервис по стандартизации и исправлению пользовательских контактных данных, таких как почтовые адреса и номера телефонов. Веб-сервис должен был получать посредством REST API контактные данные, которые указал некий пользователь в произвольном текстовом виде, и приводить эти данные в порядок. По сути, сервис должен был решать задачу распознавания пользовательских контактных данных в произвольной текстовой строке. Дополнительно в ходе такой обработки сервис должен был исправлять опечатки в адресах, восстанавливать пропущенные компоненты адреса, а также приводить обработанные данные к структурированному виду. Сервис разрабатывался для нужд бизнес-пользователей, для которых корректность клиентских контактных данных является критичным фактором. В первую очередь это интернет-магазины, службы доставки, а также CRM и MDM системы больших организаций.



В вычислительном плане поставленная задача оказалась достаточно тяжелой, поскольку обработке подлежат неструктурированные текстовые данные. Поэтому вся обработка была реализована на C++, тогда как прикладная бизнес-логика была написана на Perl и оформлена в виде FastCGI-сервера.



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



Обработка в реальном времени



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







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



Для оценки приемлемого времени отклика мы провели ряд экспериментов с регулируемой задержкой. В результате чего пришли к выводу, что подсказки перестают быть полезными, когда время отклика начинает превышать 150 мс. Наша исходная архитектура сервиса позволяла оставаться в этих рамках при одновременной работе 40 пользователей (эти показатели получены для сервера с двумя ядрами и 8Гб ОЗУ). Для увеличения этого числа необходимо наращивать количество процессоров у серверного железа. А поскольку функции подсказок для почтовых адресов и ФИО разрабатывались для их свободного использования всеми желающими, мы понимали, что процессоров и серверов может потребоваться значительно больше. Поэтому возник вопрос о том, нельзя ли оптимизировать обработку запросов за счет изменения архитектуры сервиса.



Исходная архитектура



Архитектура сервиса, которую нужно было улучшить, имела следующий вид.







Согласно данной схеме, пользовательское приложение (например, веб-браузер), генерирует HTTP-запросы, которые получает веб-сервер (в нашем случае используется легковесный веб-сервер lighttpd). Если в запросах имеем дело не со статикой, то они транслируются серверу приложений, который соединен с веб-сервером посредством FastCGI интерфейса (в нашем случае сервер приложений написан на Perl). Если запросы касаются обработки контактных данных, то они передается дальше серверу обработки. Для взаимодействия с сервером обработки используются сокеты.



Можно заметить, что если в данной схеме заменить сервер обработки на сервер БД, то получится достаточно распространенная схема, применяемая в традиционных веб-приложениях, разрабатываемых с использованием популярных фреймворков для Python или Ruby, а также для PHP под управлением php_fpm.



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







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



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



Далее запрос проходит через сервер приложений. На это тратится дополнительные 20% времени. В нашем случае никакой обработки запроса сервером приложений не выполняется. Приложение лишь выполняет парсинг HTTP-запроса, передает его дальше серверу обработки, получает от него ответ и передает его обратно FastCGI интерфейсу. Фактически 20% времени уходит на парсинг запроса и на издержки интерпретатора, поскольку приложение реализовано на скриптовом языке.



Еще 20% времени уходит на прохождение данных через сокетный интерфейс, который используется для связи приложения с сервером обработки. Этот интерфейс работает чуть быстрее, в сравнении с FastCGI (20% против 25%), поскольку соответствующий протокол и его реализация значительно проще. Обработка самого запроса, заключающаяся в формировании подсказок для введенных пользователем данных, отнимает лишь 10% от всего времени (в тестах использовался один из самых тяжелых, с точки зрения обработки, запросов).



Хотелось бы подчеркнуть, что вся специфика нашей задачи в проведенных экспериментах проявляется лишь на последней стадии и именно эта стадия, с точки зрения производительности, вызывает меньше всего вопросов. Остальные этапы весьма стандартны. Так, мы используем событийный веб-сервер, который просто извлекает полученный запрос из одного сокета, ассоциированного с прослушиваемым HTTP-портом, и кладет эти данные в FastCGI-сокет. Аналогично сервер приложений – извлекает данные из FastCGI-сокета и передает их сокету сервера обработки. В самом приложении оптимизировать по большому счету нечего.



Удручающая картина, при которой лишь 10% от времени отклика приходится на полезные действия, заставила нас задуматься о смене архитектуры.



Новая архитектура сервиса



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



Событийный сервер приложений



В рамках данного варианта была рассмотрена возможность реализовать событийный сервер приложений, например, на Node.js или Twisted. В такой реализации количество сокетных интерфейсов, через которые проходят запросы, остается прежним, поскольку каждый запрос поступает на балансирующий веб-сервер, тот передает его одному из экземпляров сервера приложений, который в свою очередь транслирует запрос серверу обработки. Суммарное время обработки запроса остается прежним. Однако число одновременно обрабатываемых запросов увеличивается за счет асинхронного использования сокетов. Грубо говоря, пока один запрос находится в процессе перехода через сокетный интерфейс, другой запрос может проходить через бизнес-логику приложения в рамках того же экземпляра.



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



Интеграция приложения и веб-сервера



Здесь была рассмотрена реализация приложения в виде Java-сервлета или .Net приложения, который напрямую вызывается веб-сервером. В этом случае удается избавиться от FastCGI интерфейса, а заодно от интерпретируемого языка. Сокетный интерфейс с сервером обработки сохраняется.



На принятии решения не в пользу данного подхода сказалась привязка всего решения к конкретному веб-серверу, который должен поддерживать выбранную технологию. Например, Tomcat для Java-сервлетов или Microsoft IIS в случае использования .Net. Нам хотелось сохранить совместимость приложения с легковесными серверами lighttpd и nginx.



Интеграция приложения с сервером обработки



В данном случае привязки к конкретному веб-серверу нет, поскольку интерфейс FastCGI сохраняется. Приложение реализуется на C++ и объединяется с сервером обработки. Таким образом, мы уходим от использования интерпретируемого языка, а также устраняем сокетный интерфейс между приложением и сервером обработки.



К недостатку данного подхода можно отнести отсутствие достаточно популярного и обкатанного на больших проектах фреймворка. Из кандидатов мы рассматривали CppCMS, TreeFrog и Wt. По части первого у нас возникли опасения на счет будущей поддержки проекта ее разработчиками, поскольку свежих обновлений на сайте проекта давно не было. TreeFrog базируется на Qt. Эту библиотеку мы активно используем в офлайновых проектах, однако посчитали ее избыточной и недостаточно надежной для поставленной задачи. По части Wt – фреймворк имеет большой акцент на GUI, тогда как в нашем случае GUI – вещь второстепенная. Дополнительным фактором при отказе от использования этих фреймворков было желание минимизировать риски, связанные с использованием сторонних библиотек, без которых в принципе можно обойтись, поскольку в данном случае имела место переработка существующего работающего сервиса, который не хотелось сломать из-за недостаточно отлаженной сторонней библиотеки.



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



Имеющиеся библиотеки



Для взаимодействия с веб-сервером приложение должно реализовывать один из поддерживаемых веб-сервером протоколов HTTP, FastCGI или SCGI. Мы остановились на FastCGI и его реализации в виде libfcgi.



Для парсинга HTTP-запросов и формирования HTTP-ответов нам подошла библиотека cgicc. Данная библиотека берет на себя все заботы по разбору HTTP-заголовков, извлечению параметров запроса, декодированию тела полученного сообщения, а также по формированию HTTP-ответа.



Для парсинга XML-запросов, которые могут приходить от пользователей сервиса в рамках REST API, был выбран Xerces.



В C++ нет поддержки юникода «из коробки», поэтому для работы с текстом было принято решение использовать стандартные STL-строки при условии обязательного соблюдения внутреннего соглашения, что все строковые данные всегда должны быть представлены в UTF-8.



Для взаимодействия с внешними сервисами и почтовыми серверами было решено использовать libcurl, а для генерации хешей — openssl.



Самописные компоненты



Для генерации html-представлений нам нужен был несложный шаблонизатор. В старой реализации сервиса для этих целей использовался HTML::Template, поэтому при переходе на C++ нужен был шаблонизатор с похожим синтаксисом и похожими возможностями. Мы попробовали поработать с CTPP, Clearsilver и Google-ctemplate.



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



У Clearsilver весь интерфейс реализован на чистом C и для его использования потребовалось писать внушительную объектную обертку. Ну а Google-ctemplate не покрывал все возможности HTML::Template, которые использовались в старой версии сервиса. Для его полноценного использования потребовалось бы менять логику, отвечающую за формирование представлений. Поэтому в случае с шаблонизатором пришлось разработать собственный велосипед, что и было сделано.



Разработка собственного C++ шаблонизатора отняла около трех дней, тогда как на поиск и изучение готовых решений, указанных выше, мы потратили вдвое больше времени. Кроме того, свой шаблонизатор позволил расширить синтаксис HTML::Template, добавив в него конструкцию «else if», а также операторы сравнения переменных с предопределенными в шаблоне значениями.



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



Пожалуй, ключевым стандартом, который нам пришлось реализовать самостоятельно, является JSON. На C++ есть довольно много его открытых реализаций, которые мы анализировали, прежде чем создавать еще одну. Основной причиной создания собственной реализации является использование JSON в связке с нестандартным аллокатором памяти, который использовался на сервере обработки для ускорения операций динамического выделения и высвобождения памяти. Данный аллокатор работает в 2-3 раза быстрее стандартного на массовых операциях выделения/высвобождения блоков небольшого размера. Поскольку работа с JSON укладывается в данный паттерн, мы хотели получить бесплатный прирост производительности на всех операциях, связанных с парсингом и построением JSON-объектов.



Итоговый результат



Архитектура итогового решения, которое у нас получилось, отображена на следующей схеме.







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



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



Согласно диаграмме, приведенной ранее, при переходе на новую архитектуру время отклика сервиса при обработке одиночного запроса должно было сократиться примерно на 40%. Реальные эксперименты показали, что сокращение произошло на 43%. Это можно объяснить тем, что монолитное решение стало более эффективно использовать оперативную память.



Мы также провели нагрузочное тестирование для определения числа пользователей, которых новый сервис может обслуживать при одновременном использовании подсказок, обеспечивая при этом время отклика не выше 150 мс. В таком режиме сервис смог обеспечивать одновременную работу 120 пользователей. Напомню, что для старой реализации это значение составляло 40. В данном случае трехкратный прирост производительности объясняется сокращением общего числа процессов, принимающих участие в обслуживании потока запросов. Раньше запросы обрабатывались несколькими экземплярами приложения (в экспериментах число экземпляров варьировалось от 5 до 20), тогда как в новой версии сервиса все запросы обрабатываются в рамках одного многопоточного процесса. В то время как каждый экземпляр работает с собственной обособленной памятью, все вместе они конкурируют за один процессорный кэш, использование которого становится менее эффективным. В случае одного монолитного процесса такой конкуренции нет.



Заключение



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



Для улучшения производительности нам пришлось объединить сервер приложений и сервер обработки данных в единый монолитный сервер, реализованный на C++. Такое решение уменьшило вдвое время отклика при обработке одиночных запросов, а также увеличило производительность сервиса в три раза при массовом использовании.



Кроме решения основной задачи приятными бонусами к проделанной работе стало упрощение рефакторинга, поскольку строгая типизация позволяет не напрягаться по поводу переименований в коде, т.к. проект просто не соберется в случае ошибок. Также получившийся проект стало легче сопровождать в целом, поскольку мы имеем единственный сервер, у которого бизнес-логика и логика обработки данных написана на одном языке.
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/304590/

Метки:   Комментарии (0)КомментироватьВ цитатник или сообщество
rss_rss_hh_new

[Перевод] Как повысить конверсию в екоммерс, используя опросы на выходе

Понедельник, 27 Июня 2016 г. 13:47 (ссылка)

image



Если вы владелец бизнеса е-коммерс, и хотите увеличить конверсию и прибыль, то вы пришли по адресу. В этой статье вы узнаете, как увеличить конверсию с помощью проверенного процесса оптимизации под названием «Exit intent technology» или если по русски «Опрос на выходе».





Как увеличить конверсию и продажи



Самый простой способ – это разбить оптимизацию екоммерс на два этапа:



Этап 1: Узнайте, из какого конкретно раздела ваши посетители покидают сайт



Этап 2: Узнайте, почему ваши посетители покидают сайт и измените это



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



Этап 1 — это информация о цифрах или, как это формально называется, информация о количественных данных. На что именно люди кликают мышкой, находясь на сайте, на какие страницы они переходят и с каких покидают ваш сайт. В качестве примера источника количественных данных может служить ваш аккаунт в Google Analytics.



Этап 2 — это о поведении посетителей сайта, так называемые, качественные данные. Почему польхователи покидают сайт со страницы корзины, почему они не понимают ваших описаний продукта и так далее. Примеры качественных данных – это тестирование пользователями, интервью с клиентами, опросы клиентов или принудительный опрос на выходе из сайта.



Каким образом опрос на выходе вписывается в полный процесс оптимизации конверсии





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



image



Вот общая картина данного процесса:



Шаг 1: Цели бизнеса



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



Шаг 2: Сбор данных



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



Шаг 3: Анализ данных



Третьим шагом является анализ данных, понимание того, что значат эти данные.



Шаг 4: Проверка гипотезы



Четвертый шаг предлагает идею сопоставить ваш сайт с вашими данными. «Гипотеза» исходит из того, что сопоставление и тестирование это наука, а на самом деле это статистика.



При проверке изменений веб-сайта, которые вы пытаетесь подтвердить свои статистические гипотезы и заработать больше денег.



Шаг 5 и 6: Разработка и построение



Пятый шаг заключается в разработке и кодировании новых изменений веб-сайта, которые вы собираетесь тестировать.



Шаг 7: А/В Тестирование



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



Вы должны убедиться, что ваше A / B тестирование достигает статистической значимости, поэтому не останавливайте данный процесс на ранней стадии.



Шаг 8: Итерация



Последним шагом является рассмотрение того, что вы узнали из вашего тестирования, доказали ли вы свою гипотезу и, самое главное, что вы узнали о своих клиентах.



Теперь давайте погрузимся в то, как использовать информацию по опросам на выход для роста электронной коммерции.



Гид по повышению конверсий в е-коммерс с помощью опросов на выходе



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



Шаг 1: Найти, откуда посетители покидают сайт


Самый простой способ узнать, с каких страниц вашего сайта уходят посетители – это просмотр отчета по страницам выхода в Google Analytics.

Чтобы проиллюстрировать эту технику, мы рассмотрим социологическое исследование для ByCharlotte( ювелирная компания из Австралии).



image



Войдите в ваш аккаунт Google Analytics и перейдите к вкладке: Поведение > Контент сайта> Страница выхода



image



Затем отфильтруйте отчеты и удалите страницы с низким трафиком, без трафика и со слишком высоким трафиком. Подтвердите эти изменения и следуйте отфильтрованным установкам.



image



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



image



Шаг 2: Установите принудительный опрос на выход




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



В данном конкретном примере мы используем приложение Hotjar. (Другие приложения — Foresee, Usabilla, Qualaroo). Устанавливаем софт, регистрируемся, входим в личный кабинет устанавливаем код отслеживания на ваш сайт интернет-магазина.



image



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



Если вы используете Shopify, он находится в theme.liquid файле.



Затем вам нужно создать сам опрос, в нашем примере мы опрашиваем посетителей чтобы узнать, почему они покидают страницу корзины.



Перейдите в меню «Опросы» и нажмите на кнопку «Новый опрос».



image



Дайте название вашему опросу и напишите благодарственное сообщение. В нашем примере мы собираемся спросить наших посетителей сайта: Что вас останавливает от сегодняшней покупки?



image



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



image



Кроме того, необходимо установить целевую страницу для опроса, в нашем случае — это страница корзины.



image



Наконец, мы подошли к сути технологии опроса на выходе. При выборе поведенческого триггера, когда опрос должен быть показан, выберите «Когда пользователь собирается покинуть страницу », так же известный, как принудительный выход.



Что такое опрос на выходе?



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



image



Наконец, переведите опрос в стадию «Активный» и приступайте к сбору данных.



Шаг 3: Проанализируйте данные вашего опроса


Есть три способа для анализа качественных данных. Имейте в виду, что когда речь идет о качественных данных, анализ занимает много времени. Первый способ – изобразить с помощью картинки, т.е., принимая все ответы по опросам на выходе с сайта, ваш клиент превращает все слова, относящиеся к проблемной зоне, например, в облако из слов.



image



В нашем примере выше опросы на выходе были сосредоточены на доставке.



Второй способ заключается в рассмотрении всех данных и принятии во внимание основных мыслей посетителей. На чем были сосредоточены люди во время опроса – на одном явном большом недостатке или на одной правильной цели? Цель здесь заключается в обобщении каждой точки данных в одно или два предложения.



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



Третий способ — это о количественной оценке ваших качественных данных путем размещения данных по категориям. Это позволяет определить, сколько людей были сосредоточены на крупном недостатке на странице или на верной цели. В нашем случае 21 из 32 посетителей были сосредоточены на доставке.



Шаг 4: A / B тестирование изменений в вашем интернет-магазине




К данному моменту вы уже узнали в каком месте и по какой причине посетители покидают ваш сайт. В нашем примере мы выяснили, что проблемная страница – это корзина. Через опрос на выходе через Hotjar мы узнали, что люди путают расчетное время доставки и стоимость доставки.



Вот первоначальный дизайн странички корзины:



image



И обновленный дизайн страницы:



image



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



Важно проводить A / B тестирования, чтобы убедиться, что ваш новый дизайн действительно улучшает качество обслуживания клиентов и увеличивает прибыль.



Мы добавили новую информацию о доставке, но в то же время мы обновили дизайн темы магазина, поэтому мы не можем приписать подъем конверсии на 20% только к первым изменениям.



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



Запустите рост вашего интернет-магазина



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



Подписывайтесь на наш блог, дальше будет много всего интересного.

Кстати, мы занимаемся комплексным обслуживанием интернет магазинов, может нужно кому? :-)



С уважением команда фулфилмент-оператора «Ямбокс»

(Ямбокс — превращаем ваш интернет магазин в компьютерную игру)




image

Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/304200/

Комментарии (0)КомментироватьВ цитатник или сообщество
ringing

Seo Solution

Среда, 22 Июня 2016 г. 11:20 (ссылка)

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

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

1.
8 (400x400, 61Kb)

Метки:   Комментарии (0)КомментироватьВ цитатник или сообщество
rss_rss_hh_new

[Перевод] Чем полезен мономорфизм?

Пятница, 17 Июня 2016 г. 14:47 (ссылка)





Выступления и посты в блогах о производительности JavaScript часто обращают внимание на важность мономорфного кода, однако обычно не дается внятного никакого объяснения, что такое мономорфизм/полиморфизм и почему это имеет значение. Даже мои собственные выступления зачастую сводятся к дихотомии в стиле Невероятного Халка: «ОДИН ТИП ХОРОШО! ДВА ТИП ПЛОХО!». Неудивительно, что когда люди обращаются ко мне за советом по производительности, чаще всего они просят объяснить, что на самом деле такое мономорфизм, откуда берется полиморфизм и что в нем плохого.



Ситуацию осложняет еще и то, что само слово «полиморфизм» имеет множество значений. В классическом объектно-ориентированном программировании полиморфизм связан с созданием дочерних классов, в которых можно переопределить поведение базового класса. Программисты, работающие с Haskell, вместо этого подумают о параметрическом полиморфизме. Однако полиморфизм, о котором предупреждают в докладах о производительности JavaScript – это полиморфизм вызовов функции.



Я объяснял этот механизм столькими различными путями, что наконец-то собрался и написал данную статью: теперь можно будет не импровизировать, а просто дать на нее ссылку.



Я также попробовал новый способ объяснять вещи – изображая взаимодействие составных частей виртуальной машины в виде коротких комиксов. Кроме того, данная статья не покрывает некоторые детали, которые я посчитал незначительными, излишними или не связанными напрямую.



Динамический поиск для чайников





Ради простоты изложения мы будем преимущественно рассматривать самые элементарные обращения к свойствам в JavaScript, как например o.x в коде ниже. В то же время важно понимать, что всё, о чем мы говорим, относится к любой операции с динамическим связыванием, будь то обращение к свойству или арифметический оператор, и применимо не только в JavaScript.



function f(o) {
return o.x
}

f({ x: 1 })
f({ x: 2 })


Представьте на минуту, что вы собеседуетесь на отличную должность в ООО «Интерпретаторы», и вам предлагают придумать и реализовать механизм обращения к свойству в виртуальной машине JavaScript. Какой ответ был бы самым банальным и простым?



Сложно придумать что-то проще, чем взять готовую семантику JS из спецификации ECMAScript (ECMA 262) и переписать алгоритм [[Get]] слово в слово с английского языка на C++, Java, Rust, Malbolge или любой другой язык, который вы предпочли использовать для собеседования.



Вообще говоря, если открыть случайный интерпретатор JS, скорее всего можно увидеть нечто наподобие:





jsvalue Get(jsvalue self, jsvalue property_name) {
// 8.12.3 здесь реализация метода [[Get]]
}

void Interpret(jsbytecodes bc) {
// ...
while (/* остались необработанные байткоды */) {
switch (op) {
// ...
case OP_GETPROP: {
jsvalue property_name = pop();
jsvalue receiver = pop();
push(Get(receiver, property_name));
// TODO(mraleph): выкинуть исключение в strict mode, как сказано в 8.7.1 шаг 3.
break;
}
// ...
}
}
}


Это абсолютно правильный способ реализовать обращение к свойству, однако у него есть один значительный недостаток: в сравнении с современными виртуальными машинами JS наша реализация очень медленная.



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





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



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



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



Вернемся к примеру:



function f(o) {
return o.x
}

f({ x: 1 })
f({ x: 2 })


Сколько записей будет в инлайн-кеше для значения o.x?



Поскольку {x: 1} и {x: 2} имеют одинаковую форму (она иногда называется скрытый класс), ответ — 1. Именно это состояние кеша мы называем мономорфным, потому что нам попадались объекты только одной формы.



Моно («один») + морф («форма»)













А что случится, если мы вызовем функцию f с объектом другой формы?



f({ x: 3 })
// кеш o.x все еще мономорфен
f({ x: 3, y: 1 })
// а теперь?


Объекты {x: 3} и {x: 3, y: 1} имеют разную форму, поэтому наш кеш теперь содержит две записи: одну для {x: *} и одну для {x: *, y: *}. Операция стала полиморфной с уровнем полиморфизма, равным двум.



Если мы продолжим передавать в f объекты различных форм, уровень полиморфизма будет расти, пока не достигнет предела емкости для инлайн-кеша (например, в V8 для обращения к свойствам предел емкости будет равен четырем). После этого кеш перейдет в мегаморфное состояние.



f({ x: 4, y: 1 }) // полиморфизм, уровень 2
f({ x: 5, z: 1 }) // полиморфизм, уровень 3
f({ x: 6, a: 1 }) // полиморфизм, уровень 4
f({ x: 7, b: 1 }) // мегаморфизм




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









Небольшое упражнение для проверки понимания:



function ff(b, o) {
if (b) {
return o.x
} else {
return o.x
}
}

ff(true, { x: 1 })
ff(false, { x: 2, y: 0 })
ff(true, { x: 1 })
ff(false, { x: 2, y: 0 })



  1. Сколько инлайн-кешей обращения к свойству объявлены в функции ff?

  2. В каком они состоянии?



Ответы
Кешей два и оба мономорфны, поскольку в каждый из них попадали только объекты одной формы.





Влияние на производительность



На данном этапе производительность различных состояний инлайн-кеша становятся очевидными:




  • Мономорфное — самое быстрое, если вы каждый раз обращаетесь к значению из кеша (ОДИН ТИП ХОРОШО!)

  • Полиморфное — линейный поиск среди значений кеша

  • Мегаморфное — обращение к глобальной хеш-таблице. Самый медленный вариант из кешированных, но все равно быстрее, чем промах кеша

  • Промах кеша — обращение к полю, которого нет в кеше. Приходится платить за переход в рантайм и использование самого общего алгоритма



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



Спекуляции и оптимизации



Инлайн-кеши не могут обеспечить максимальную производительность в одиночку по двум причинам:




  • Каждый инлайн-кеш работает независимо и ничего не знает о соседях

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



function g(o) {
return o.x * o.x + o.y * o.y
}

g({x: 0, y: 0})


В примере выше инлайн-кешей аж семь штук: два на .x, два на .y, еще два на * и один на +. Каждый действует самостоятельно, проверяя объект o на соответствие закешированной форме. Арифметический кеш оператора + проверит, являются ли оба операнда числами, хотя это можно было бы вывести из состояний кешей операторов *. Кроме того, в V8 есть несколько внутренних представлений для чисел, так что это тоже будет учтено.



Некоторые арифметические операции в JS имеют присущий им конкретный тип, например a | 0 всегда возвращает 32-битное целое, а +a — просто число, но для большинства остальных операций таких гарантий нет. По этой причине написание AOT-компилятора для JS является безумно сложной задачей. Вместо того, чтобы компилировать JS-код заранее один раз, большинство виртуальных машин JS имеет несколько режимов исполнения.



Например, V8 сначала компилирует всё обычным компилятором и сразу же начинает исполнять. Позже часто используемые функции перекомпилируются с применением оптимизаций. С другой стороны, Asm.js использует неявные типы операций и с их помощью описывает очень строго ограниченное подмножество Javascript со статической типизацией. Такой код можно оптимизировать еще до его запуска, без спекуляций адаптивной компиляции.



«Прогрев» кода нужен для двух целей:




  • Сокращает задержку при старте: оптимизирующий компилятор включается только в том случае, если код был использован достаточное количество раз

  • Инлайн-кеши успевают собрать информацию о формах объектов



Как уже было сказано выше, написанный людьми код на JavaScript обычно содержит недостаточно информации для того, чтобы его можно было полностью статически типизировать и провести AOT-компиляцию. JIT-компилятору приходится строить догадки о том, как ведет себя ваша программа, и выдавать оптимизированный код, который подходит только при определенных допущениях. Иными словами, ему нужно знать, какие объекты используются в оптимизируемой функции. По счастливому стечению обстоятельств, это именно та информация, которую собирают инлайн-кеши!




  • Мономорфный кеш говорит: «Я видел только тип A»

  • Полиморфный кеш говорит: «Я видел только типы А1, ..., AN»

  • Мегаморфный кеш говорит: «Да я много чего видел...»





Оптимизирующий компилятор анализирует информацию, собранную инлайн-кешами, и по ней строит промежуточное представление (IR). Инструкции IR обычно более специфичные и низкоуровневые, нежели операции в JS. Например, если кеш для обращения к свойству .x видел только объекты формы {x, y}, тогда компилятор может сгенерировать IR-инструкцию, которая получает значение по фиксированному смещению от указателя на объект. Разумеется, использовать такую оптимизацию для любого приходящего объекта небезопасно, поэтому компилятор также добавит проверку типа перед ней. Проверка типа сравнивает форму объекта с ожидаемой, и если они отличаются — оптимизированный код выполнять нельзя. Вместо этого вызывается неоптимизированный код и исполнение продолжается оттуда. Этот процесс называется деоптимизацией. Несовпадение типа — не единственная причина, по которой деоптимизация может произойти. Она также может случиться, если арифметическая операция была «заточена» под 32-битные числа, а результат предыдущей операции вызвал переполнение, или при обращении к несуществуюшему индексу массива arr[idx] (выход за пределы диапазона, разреженный массив с пропусками и т.д.).





Становится понятно, что оптимизация нужна для устранения описанных выше недостатков:
















Неоптимизированный код Оптимизированный код
Каждая операция может иметь неизвестные побочные эффекты и реализует полную семантику Специализация кода устраняет или ограничивает неопределенность, побочные эффекты строго известны (например, у обращения к свойству по смещению их нет)
Каждая операция сама по себе, обучается самостоятельно и не делится опытом с соседями Операции раскладываются на низкоуровневые IR-инструкции, которые оптимизируются вместе, что позволяет устранить избыточность




Разумеется, построение IR под конкретные формы объектов — это только первый шаг в цепочке оптимизации. Когда промежуточное представление сформировано, компилятор будет пробегаться по нему несколько раз, обнаруживая инварианты и вырезая лишнее. Этот тип анализа обычно ограничен областью процедуры и компиляторы вынужден учитывать самые худшие из возможных побочных эффектов на каждом вызове. Следует помнить, что вызов может скрываться в любой неконкретизированной операции: например, оператор + может вызвать valueOf, а обращение к свойству вызовет его getter-метод. Таким образом, если операцию не получилось конкретизировать на первом этапе, об нее будут спотыкаться все последующие проходы оптимизатора.



Один из наиболее распространенных видов избыточных инструкций — это постоянные проверки на то, что один и тот же объект имеет одну и ту же форму. Вот так изначальное промежуточное представление может выглядеть для функции g из примера выше:



    CheckMap v0, {x,y}  ;; shape check 
v1 Load v0, @12 ;; load o.x
CheckMap v0, {x,y}
v2 Load v0, @12 ;; load o.x
i3 Mul v1, v2 ;; o.x * o.x
CheckMap v0, {x,y}
v4 Load v0, @16 ;; load o.y
CheckMap v0, {x,y}
v5 Load v0, @16 ;; load o.y
i6 Mul v4, v5 ;; o.y * o.y
i7 Add i3, i6 ;; o.x * o.x + o.y * o.y


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



;; После ГНЗ 
CheckMap v0, {x,y}
v1 Load v0, @12
i3 Mul v1, v1
v4 Load v0, @16
i6 Mul v4, v4
i7 Add i3, i6


Как уже было сказано выше, убрать эти инструкции стало возможно только потому, что между ними не было операций с побочными эффектами. Если бы между загрузками v1 и v2 был вызов, нам пришлось бы допускать, что он может изменить форму объекта в v0, а следовательно проверка формы v2 была бы обязательна.



Если же операция не мономорфна, компилятор не может просто взять ту же самую связку «проверка формы и конкретизированная операция», как в примерах выше. Инлайн-кеш содержит информацию о нескольких формах. Если взять любую из них и завязаться только на нее, то все остальные будут приводить к деоптимизации, что нежелательно. Вместо этого компилятор будет строить дерево решений. Например, если инлайн-кеш обращения к свойству o.x встречал только формы A, B и C, то развернутая структура будет аналогична следующему (это псевдокод, на самом деле будет построен граф потока управления):



var o_x
if ($GetShape(o) === A) {
o_x = $LoadByOffset(o, offset_A_x)
} else if ($GetShape(o) === B) {
o_x = $LoadByOffset(o, offset_B_x)
} else if ($GetShape(o) === C) {
o_x = $LoadByOffset(o, offset_C_x)
} else {
// в o.x попадали только A, B, C
// поэтому мы предполагаем, что *ничего* иного быть не может
$Deoptimize()
}
// Здесь мы знаем только то, что o имеет одну
// из форм - A, B или C. Но мы уже не знаем,
// какую именно


У мономорфных обращений есть одно очень полезное свойство, которого нет у полиморфных: до следующей операции с побочным эффектом существует гарантия того, что форма объекта осталась неизменной. Полиморфное обращение дает только слабую гарантию «объект имеет одну из нескольких форм», с помощью которой мало что можно оптимизировать (в лучшем случае, можно было бы убрать последнее сравнение и блок с деоптимизацией, но V8 так не поступает).



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



// Проверяем, что форма о является одной из A, B или C - иначе деоптимизируем
$TypeGuard(o, [A, B, C])
// Загружаем свойство, поскольку во всех трех формах смещение одинаковое
var o_x = $LoadByOffset(o, offset_x)


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



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



 var o_x
if ($GetShape(o) === A) {
o_x = $LoadByOffset(o, offset_A_x)
} else if ($GetShape(o) === B) {
o_x = $LoadByOffset(o, offset_B_x)
} else if ($GetShape(o) === C) {
o_x = $LoadByOffset(o, offset_C_x)
} else {
// Мы знаем, что обращение к o.x мегаморфно.
// Во избежание деоптимизации оставим лазейку
// для случайных объектов:
o_x = $LoadPropertyGeneric(o, 'x')
// ^^^^^^^^^^^^^^^^^^^^ неизвестные побочные эффекты
}
// В этот момент о форме объекта "о"
// больше ничего не известно, и могли
// произойти побочные эффекты


В некоторых случаях компилятор может вообще отказаться от затеи конкретизировать операции:




  • Нет способа эффективно ее конкретизировать

  • Операция полиморфна и компилятор не знает, как построить дерево решений (такое раньше происходило с полиморфным обращением к arr[i], но уже исправлено)

  • Нет информации о типе, которым операция может быть конкретизирована (код ни разу не выполнялся, сборщик мусора удалил собранную информацию о формах и т.д.)



В этих (довольно редких) случаях компилятор выдает обобщенный вариант промежуточного представления.



Влияние на производительность



В итоге имеем следующее:




  • Мономорфные операции оптимизируются легче всего и дают оптимизатору простор для действий. Как сказал бы Халк — "ОДИН ТИП БЛИЗКО К ЖЕЛЕЗУ!"

  • Операции с небольшим уровнем полиморфизма, требующие проверку формы, или дерево решений, медленнее мономорфных:


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

    • Полиморфные проверки форм не так сильно препятствуют оптимизации и все еще позволяют удалять некоторые излишние инструкции, но все равно медленнее мономорфных. Влияние на производительность зависит от того, как хорошо ваш процессор работает с условными переходами.


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



Примечание переводчика: автор давал ссылку на микробенчмарк, однако сам сайт больше не работает.



Необсужденное



Я умышленно не стал вдаваться в некоторые детали реализации, чтобы статья не стала слишком обширной.



Формы



Мы не обсуждали, как формы (или скрытые классы) устроены, как они вычисляются и присваиваются объектам. Общее представление можно получить из моей предыдущей статьи или выступления на AWP2014.



Нужно только помнить, что само понятие «формы» в виртуальных машинах JS — это эвристическое приближение. Даже если с точки зрения человека у двух объектов форма одинакова, с точки зрения машины она может быть разной:



function A() { this.x = 1 }
function B() { this.x = 1 }

var a = new A,
b = new B,
c = { x: 1 },
d = { x: 1, y: 1 }

delete d.y
// a, b, c, d - четыре разных формы для V8


Поскольку объекты в JS беззаботно реализованы в виде словарей, можно применить полиморфизм по случайности:



function A() {
this.x = 1;
}

var a = new A(), b = new A(); // форма одинакова

if (something) {
a.y = 2; // форма a больше не совпадает с формой b
}


Умышленный полиморфизм



Во многих языках, где форма созданного объекта не может быть изменена (Java, C#, Dart, C++ и т.д.), полиморфизм также поддерживается. Возможность написать код, основанный на интерфейсах и выполняющихся в зависимости от конкретной реализации — очень важный механизм абстракции. В статически типизированных языках полиморфизм влияет на производительность аналогичным образом.



Неудивительно, что JVM использует инлайн-кеши для оптимизации инструкций invokeinterface и invokevirtual.



Не все кеши одинаково полезны



Следует иметь в виду, что некоторые кеши основаны не на формах объектов, и\или имеют меньшую емкость по сравнению с другими. Например, инлайн-кеш вызова функции может иметь только три состояния: неинициализированное, мономорфное или мегаморфное. Промежуточного полиморфного состояния у него нет, потому что для вызова функции форма объекта не имеет значения, а кешируется сам объект функции.



 function inv(cb) {
return cb(0)
}

function F(v) { return v }
function G(v) { return v + 1 }

inv(F)
// инлайн-кеш мономорфный, указывает на F
inv(G)
// инлайн-кеш становится мегаморфным


Если функция inv оптимизирована и инлайн-кеш находится в мономорфном состоянии, оптимизатор может встроить тело функции в место вызова (что особенно важно для коротких функций в часто вызываемых местах). Если же кеш в мегаморфном состоянии, оптимизатор не знает, тело какой именно функции можно встраивать, и поэтому оставит обобщенный оператор вызова в промежуточном представлении.



С другой стороны, вызовы методов вида o.m(...) работает аналогично обращению к свойству. Инлайн-кеш вызова метода также может иметь промежуточное полиморфное состояние. Виртуальная машина V8 может встроить вызов такой функции в мономорфном, полиморфном, или даже мегаморфном виде тем же способом, как со свойством: сначала проверка типа или дерево решений, после которого располагается само тело функции. Есть только одно ограничение: метод должен содержаться в форме объекта.



На самом деле, o.m(...) использует сразу два инлайн-кеша — один для загрузки свойства, другой для непосредственно вызова функции. Второй имеет только два состояния, как в примере выше у функции cb. Поэтому его состояние игнорируется при оптимизации вызова и используется только состояние инлайн-кеша обращения к свойству.



 function inv(o) {
return o.cb(0)
}

var f = {
cb: function F(v) { return v },
};

var g = {
cb: function G(v) { return v + 1 },
};

inv(f)
inv(f)
// инлайн-кеш в мономорфном состоянии,
// видел только объекты типа f
inv(g)
// а теперь в полиморфном, потому что
// кроме f видел еще и g


Может показаться неожиданным, что в примере выше f и g имеют разную форму. Так происходит потому, что когда мы присваиваем свойству объекта функцию, V8 старается (если возможно) привязать ее к форме объекта, а не к самому объекту. В примере выше f имеет форму {c: F}, то сама форма ссылается на замыкание. Во всех примерах до этого формы содержали только признак наличия определенного свойства — в данном же случае сохраняется и его значение, а форма становится похожа на класс из языков наподобие Java или C++.



Конечно, если потом перезаписать свойство другой функцией, V8 посчитает, что это больше не похоже на взаимоотношение класса и метода, поэтому форма изменится:



var f = {
cb: function F(v) { return v },
};

// Форма f равна {cb: F}

f.cb = function H(v) { return v - 1 }

// Форма f равна {cb: *}


Про то, как V8 строит и поддерживает формы объектов, стоило бы написать отдельную большую статью.



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



К этому моменту создается впечатление, что инлайн-кеш обращения к свойству o.x — это такой словарь, сопоставляющий формы и смещение внутри класса, наподобие Dictionary. На самом деле такое представление не передает глубину: свойство может принадлежать одному из объектов в цепочке прототипов, или же иметь методы доступа. Очевидно, что свойства с геттерами-сеттерами считаются менее конкретизированными, чем обычное свойства с данными.



Например, объект o = {x: 1} можно представить как объект, свойство которого загружает и сохраняет значение в специальный скрытый слот с помощью методов виртуальной машины:



// псевдокод, описывающий o = { x: 1 }
var o = {
get x () {
return $LoadByOffset(this, offset_of_x)
},
set x (value) {
$StoreByOffset(this, offset_of_x, value)
}
// геттер/сеттер сгенерированы виртуальной машиной
// и невидимы для обычного JS-кода
};
$StoreByOffset(o, offset_of_x, 1)


Кстати, примерно так реализованы свойства в Dart VM



Если смотреть так, то инлайн-кеш должен быть скорее представлен в виде Dictionary — сопоставляя формы с функциями-аксессорами. В отличие от наивного представления со смещением, так можно описать и свойства из цепочки прототипов, и геттеры-сеттеры, и даже прокси-объекты из ES6.



Премономорфное состояние



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



Финальный совет по производительности



Самый лучший совет касательно производительности можно взять из названия книги Дэйла Карнеги «Как перестать беспокоиться и начать жить».



И правда, переживать из-за полиморфизма обычно бессмысленно. Вместо этого прогоните ваш код с нормальными данными, поищите узкие места профайлером — и если они связаны с JS, то стоит взглянуть на промежуточное представление, которое производит компилятор.



И только тогда, если в середине вашего нагруженного цикла вы увидите инструкцию под названием XYZGeneric или что-то будет помечено атрибутом changes [] (то есть «меняет всё»), тогда (и только тогда) можно начинать волноваться.




Original source: habrahabr.ru.

https://habrahabr.ru/post/303542/?utm_source=habrahabr&utm_medium=rss&utm_campaign=best

Метки:   Комментарии (0)КомментироватьВ цитатник или сообщество
rss_rss_hh_new

[recovery mode] Кейс: новый сайт в ТОП «Яндекса» за полтора года

Пятница, 10 Июня 2016 г. 16:18 (ссылка)

В этом выпуске мы расскажем о том, как вывели новый сайт в ТОП «Яндекса» за полтора года. Опытом делится Алексей Леонтьев — руководитель группы оптимизаторов Kokoc.com (Kokoc Group).



image



Кого продвигали: клиент, обратившийся за услугой SEO, решил захватить рынок отопительного оборудования. Сайта у клиента не было, поэтому начинали с «нуля».



Шаг 1. Разведка



Для начала необходимо было провести анализ топ-10 сайтов поисковой выдачи по тематическим запросам:

Общим («инженерная сантехника», «отопительное оборудование»);

Детальным («металлические радиаторы», «радиаторы отопления», «купить котлы отопления»).

Сайты конкурентов рассматривали по следующим критериям:



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

2. Меню: структура, глубина, уровень вложенности страниц, количество кликов для перехода с главной страницы на карточку товара и категории. К примеру, для конкретного радиатора путь может составлять 3-4 клика, а для категории не должен превышать 1 клик.

3. Адреса страниц: структура, использование ЧПУ, длина, вхождение ключевых слов. В одних тематиках адреса короткие — к примеру, site.ru/otoplenie; в других — длинные, с использование слова «каталог»: site.ru/katalog/dom/otoplenie.



Универсального рецепта создания адресов нет, но важно, чтобы в них присутствовали коммерческие паттерны: каталог, корзина и страницы услуг. Так поисковые системы смогут распознать страницу как коммерческую и ранжировать ее по запросам с ключевым словом «купить».



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



Шаг 2. Вооружение



На основе анализа топов составили семантическое ядро — список запросов, по которым будет продвигаться сайт. Здесь лучше использовать давно придуманные инструменты, а не тратить время на сбор ключевиков вручную. (Мы пользуемся Just Magic.)



Как мы работали с Just Magic: прежде всего, собрали маркеры запросов (например: «водонагреватели», «котлы», «полотенцесушители», «радиаторы») и указали их в файле Excel вместе с планируемыми посадочными страницами для удобства сортировки результатов.



image



После этого начали сбор семантического ядра (раздел «Сервисы», вкладка «Маркеры»). Указали название задания, выбрали базу данных и режим работы, отметили порог привязки и приложили файл с маркерами.



image



Сервис отправил нам e-mail со ссылкой на результаты — собранное семантическое ядро с указанными ранее группами и страницами.



image



Здесь важно отфильтровать ненужные запросы и проверить актуальный поисковый спрос в wordstat.yandex.ru по точному вхождению («! слова! запроса»).



Сделали это с помощью собственного сервиса сбора частотностей и получили готовое ядро на основе поданных маркеров.



image



Подсказки собираем также в Just Magic с помощью сервиса «Парсер подсказок».



Программа автоматически расширила маску основных запросов («радиаторы», «отопители», «нагреватели») словами «цены», «купить», «стоимость», «в Москве». Получилось ядро, которое «почистили» вручную и, естественно, согласовали с клиентом.



Дальше распределили его по страницам, предварительно разбив по группам:


  • Биметаллические радиаторы;

  • Биметаллические радиаторы купить;

  • Биметаллические радиаторы цены;

  • Биметаллические радиаторы в Москве;

  • Биметаллические радиаторы стоимость;

  • Биметаллические радиаторы недорого.



Шаг 3. Расстановка войск. Выбор огневых позиций



На основании собранного семантического ядра и кластеризации по группам сформировали структуру будущего сайта. Получилось несколько вариантов пути пользователя к товару:


  • Котлы -> Газовые котлы -> Настенные газовые котлы -> Настенные газовые двухконтурные котлы -> Настенные газовые двухконтурные котлы Baxi.

  • Котлы -> Настенные котлы -> Настенные двухконтурные котлы -> Настенные двухконтурные котлы Baxi.

  • Котлы -> Двухконтурные котлы -> Двухконтурные котлы Baxi.

  • Котлы -> Газовые котлы -> Газовые котлы Baxi.

  • Котлы -> Котлы Baxi.



Важно было учесть, что все категории и подкатегории должны быть доступны в один клик из меню, кроме деления на бренды и фильтрации по параметрам. А адреса страниц, независимо от вложенности, сформированы по принципу: site.ru/catalog/category.



Например:


  • site.ru/catalog/kotly/

  • site.ru/catalog/kotly-gazovye/

  • site.ru/catalog/kotly-gazovye-dvuhkonturnye/



Так можно исключить традиционные для этой тематики длинные адреса.



Шаг 4. Контроль действий.



На основании предыдущих шагов мы дали рекомендации по разработке сайта и заказчик все учёл.



Теперь пришло время проверить его на ошибки. Традиционно начали с поиска дублей страниц и контента. Кто-то использует для этого различные онлайн-сервисы или поиск «Яндекса», у нас собственный инструмент Diplicate Finder.



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



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



Правило следующее: Купить [Название товара] по цене [стоимость] в интернет-магазине.



Один из сгенерированных мета-тегов выглядел так: «Купить газовый настенный комбинированный котел Wolf CGG-1К-24 с закрытой камерой по цене 65 000 рублей в интернет-магазине».



Проверку на 404-ю ошибку делали через Sape: в личном кабинете, во вкладке «Технический аудит» указали сайт и поисковую систему, для которой он оптимизирован. Процесс происходит автоматически и занимает несколько часов в зависимости от объема сайта и загруженности сервиса. Sape находит страницы, с которых стоят ссылки на несуществующие адреса или файлы, имеющие ответ сервера 404. Часть ссылок скорректировали, а некоторые удалили.



image



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



Помните, как несколько лет назад в интернете всплыли смс-переписки клиентов «Мегафона»? Компания обвиняла в этом «Яндекс», но на самом деле это была ошибка оптимизаторов, которые не закрыли страницы от роботов поисковика в файле robots.txt. Мы перепроверили открытые и закрытые для поисковика страницы, все оказалось в порядке. Кстати, подобные проверки нужно проводить после каждого обновления ресурса.



Шаг 5. Первые победы



Через месяц после индексации сайт стал виден в поиске по низкочастотным запросам, например: «купить радиатор отопления», «купить биметаллические батареи отопления», «дымоходы для котлов».



image

Динамика трафика после оптимизации сайта в течение двух месяцев.



Шаг 6. Помощь союзников



Пришла пора подключать внешние ссылки. Сегодня «Яндекс» наказывает за покупные ссылки, но еще несколько лет назад не обращал на них пристального внимания. Мы воспользовались биржей Sape, выбрав тематичные, околотематичные и СМИ-доноров, имеющих уникальный контент и небольшое количество исходящих ссылок.



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



В результате через 1,5 месяца сайт вышел в топ-10 поисковой выдачи «Яндекса» по запросам «теплый пол», «батареи отопления», «радиаторы отопления», «полотенцесушители».



Шаг 7. Расширение



Когда в ассортименте товаров клиента добавлялись новые категории, необходимо было расширять структуру сайта. К примеру, анализ топ-10 поисковой выдачи по запросам «тепловентиляторы», «купить тепловентилятор», «тепловентиляторы цены» позволил расширить семантическое ядро для новых страниц — процесс вам уже знаком по шагам 1 и 2.



Параллельно с этим сайт регулярно проверялся на возможные ошибки по схеме, описанной в Шаге 4.



Шаг 8. Победа



За полтора года сайт вышел в топ выдачи «Яндекса» по высокочастотным, среднечастотным и низкочастотным запросам, таким как: «батареи отопления», «шаровые краны», «радиаторы отопления», «водонагреватели электрические накопительные».



Основной результат получен за счет грамотного построения структуры и контента сайта.



image



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



Источник: Cossa.
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/303088/

Метки:   Комментарии (0)КомментироватьВ цитатник или сообщество
rss_rss_hh_new

Re: Что такое SEO и как оно работает

Суббота, 04 Июня 2016 г. 08:49 (ссылка)

Зачастил я что-то с публикациями, но опять не смог удержаться. 1 июня пользователь JustRamil оставил публикацию о том, что такое SEO и как оно работает.



image



Я её внимательно прочитал, потом перечитал, потом перечитал опять. Несмотря на то, что статья достаточно точно отвечает на вопрос о том, что же это такое, мне показалось недостаточным объяснение того как оно работает и я решил поделиться с вами короткой заметкой о лайфхаках, полученных за время многолетней работы с AdSense под буржунет и продвижением коммерческих русскоязычных сайтов. Так как через 2 часа я должен отчаливать к лучшему другу на свадьбу, я постараюсь быть кратким. Очень кратким.





Я не буду заострять свое внимание на приведении многочисленных определений SEO, как и не буду приводить 100500 тысяч отличий SEO от контекстной рекламы. Пирог съеден задолго до меня.



Вместо этого, я расскажу о том, как можно существенно увеличить траффик с помощью поисковой оптимизации (обратите внимание, не вывести сайт в ТОП, а увеличить траффик). Если всё, что делает ваш сео-специалист заключается в покупке ссылок и размещении статей, полученных от копирайтеров, передайте ему, что он — мудак.



1. Seo-оптимизация — это внутренняя оптимизация сайта. Прежде чем начинать какие-либо работы, необходимо адаптировать сайт для пользователя. Простой и понятный дизайн, продуманный интерфейс, быстрая загрузка — всё это чрезвычайно важно в 2016, так как именно поведенческие факторы выходят на передовые позиции. Очень часто можно вывести сайт в топ исключительно благодаря тому, что пользователи, которые пришли на ваш веб-сайт, весело и задорно почитывают ваши публикации по несколько минут, переходят по ссылкам и редко закрывают его с первых секунд. Итак, сперва делаем сайт удобным, быстрым и красивым.



2. Семантическое ядро — это всё. Есть много глупых компаний, которые очень хотят быть Number 1 по супер-популярному коммерческому запросу. Они нанимают горе сеошников, которые выкидывают сотни тысяч рублей на закупку дорогих ссылок в трубу, и порой даже выводят сайт в ТОП, но вот только все дело в том, что ориентация на низкочастотные и среднечастотные запросы даёт ГОРАЗДО более высокий результат (с точки зрения скорости продвижения, низкой конкуренции, малых затрат). Очень часто имеет смысл продвигаться по десяти, а то и 100 СЧ/НЧ запросам вместо одного ЖИРНОГО, который забит конкурентами.



Итак, ядро — наше всё. Чем лучше вы продумаете структуру вашего сайта, и чем более интересные запросы научитесь подбирать, тем легче окажется приводить посетителей на ваш сайт. www.bukvarix.com — шикарный и БЕСПЛАТНЫЙ инструмент, который представляет собой 40 гиговую базу, содержащую миллионы запросов выкачанных из Яндекс вордстата. Но работает гораздо быстрее и удобнее. MUST HAVE, если вы продвигаете более одного сайта. Ну или используйте Яндекс Вордстат.



Я рекомендую подбирать 1 СЧ запрос под Title и h1-заголовок вашей статьи, и разбавлять его 2-5 низкочастотниками в теле статьи, а также h2-h3 заголовках. Более того, имеет смысл воспользоваться потрясным сервисом МУТАГЕН (РЕФЕРАЛЬНАЯ ССЫЛКА!!!) для того, чтобы оценить уровень конкуренции по тому или иному запросу (русскоязычный аналог moz-панели). Несмотря на то, что ссылка реферальная — я абсолютно искренне рекомендую вам обратить внимание на этот сервис, ибо он потрясен. Все, что имеет оценку конкуренции 18 и ниже — замечательные запросы, которые вы можете с уверенностью поработить.



3. Внутренняя перелинковка — это хоть и не всё, но многое. Очень многое. Почитайте топовые англоязычные блоги. Помимо блока «Вам также будет интересно» с подборкой РЕЛЕВАНТНЫХ (очень здорово, если ваша статья имеет что-то общее с ссылкой, которую вы продвигаете. Чем релевантнее будет запрос, тем больший вес этой внутренней ссылке отдадут поисковики) ссылок, было бы здорово вставлять 2-3 ссылки на схожие или более подробные материалы по теме внутри самой статьи. Чем больше человек будет ползать по вашему сайту, переходя со ссылки на ссылку — тем лучше ваш сайт смотрится в глазах поисковика.



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



5. Делайте Title и description — продажными текстами. Практически НИКТО в рунете не использует этот лайфхак, который с легкостью повышает CTR переходов по вашим запросам, даже если они не входят в ТОП-3. Добавляйте в тайтл ДЛИННОЕ тире и завершайте его знаком восклицания или вопроса. В дескрипшине первым делом вставляйте сам запрос (чтобы его подчеркнуло жирненьким), и также завершайте его призывом к действию, вопросом или восклицанием:

«Разделительные вопросы в английском языке — это просто!

Разделительные вопросы в английском языке являются одним из самых интересных видов среди существующих вопросов. Убедитесь сами!»



6. Один разок (не больше!) можно выделить запрос жирненьким (а можно и не выделять). Беспокойтесь о структуре страницы. H1 — основной заголовок тесно коррелирующий с Title. Парочку H2 — со смежными запросами, прописать alt картинкам с использованием вспомогательных запросов и слов, и в принципе, проведя все вышеуказанные действия, вы можете не торопясь подкрепляться парой хороших ссылок (а можете и нет)



Кстати, в статье, на которую я ссылаюсь автор говорит о бесполезности форумных ссылок. Автор заблуждается.



И напоследок — освойте Google Analytics/Яндекс.Метрику и панели вебмастеров от вышеуказанных компаний. Там все просто и лёгко, но без них ваше SEO далеко не убежит.



На этом у меня всё. Вам хорошего дня, а мне — хорошей вечеринки на свадьбе.



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



P.P.S. Почему так мало? Где сок, где подробности? — Не знаю где сок и подробности, но исключительно эти моменты помогли мне заработать более 10 штук от Адсенс и продать несколько сеток сайтов за достаточно большие деньги. Не так всё сложно, как говорят сео-оптимЕзаторы.

Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/302586/

Метки:   Комментарии (0)КомментироватьВ цитатник или сообщество
kiev2376393

В Google AdSense появился новый инструмент для оптимизации рекламы

Среда, 01 Июня 2016 г. 11:04 (ссылка)

Google AdSense запустил новый инструмент для оптимизации рекламы «Автоматические эксперименты».
Новый функционал позволяет сравнить эффективность нескольких вариантов настроек объявлений. В ходе эксперимента для части трафика используются

Читать далее...
Метки:   Комментарии (0)КомментироватьВ цитатник или сообщество

Следующие 30  »

<оптимизация - Самое интересное в блогах

Страницы: [1] 2 3 ..
.. 10

LiveInternet.Ru Ссылки: на главную|почта|знакомства|одноклассники|фото|открытки|тесты|чат
О проекте: помощь|контакты|разместить рекламу|версия для pda