...

суббота, 17 января 2015 г.

[Из песочницы] Краткий курс копьютерной графики, пишем упрощённый OpenGL своими руками, статья 1 из 6

[Из песочницы] 2D система освещения, работающая на GPU, для Unity3D


Всем привет. Как известно, Unity3D отсутствует поддержка освещения для 2D игр. В Asset Store можно найти такую систему, но у неё есть один недостаток — она работает на CPU и потребляет весьма много ресурсов (64-4096 рейкастов за кадр на каждый источник света). Поэтому я решил сделать своё освещение, производительности которого хватило бы для мобильных устройств. Для этого вычисления были перенесены на GPU. Получилось что-то похожее свет Terraria или Starbound.


Ссылка на демку. Скриншоты взяты из неё.



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


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


Теперь подробнее, в порядке отрисовки.


Препятствия света






Текстура препятствий света. RGB каналы. Эта и последующие похожие текстуры имеют масштаб 400%

Вот такую текстуру выдает камера. Черные области полностью прозрачны, белые — полностью непрозрачные. Также поддерживаются цветные области, например, полностью красный пиксель будет блокировать красную часть света и пропускать зеленую и синюю.


Свет окружения






Источники света окружения



Источники света окружения. Альфа канал




Итеративно генерируемая текстура света окружения




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


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


Алгоритм расчета для одного пикселя:



  1. Берем начальное значение пикселя из предыдущей итеративной текстуры.

  2. Вычитаем из пикселя силу препятствия из текстуры препятствий.

  3. Прибавляем к пикселю силу свечения из текстуры источников света окружения.

  4. Прибавляем к пикселю усредненное значение соседних пикселей


Источники света






Источники света

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

Для источников света доступно несколько шейдеров с трассировкой пути и один без неё. Шейдеры с трассировкой различаются по количеству трассируемых точек. Я использую два таких — один на 9 точек, работающий с Shader Model 2.0, другой на 20 точек для Shader Model 3.0. Шейдер без трассировки пути используется для систем частиц, так как ему не нужна какая-либо дополнительная информация для работы.


Алгоритм трассировки путя:



  1. Берем яркость пикселя из текстуры.

  2. Находим позицию источника света и текущего пикселя в текстуре препятствий.

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


Смешивание и наложение света






Свет источников + свет окружения

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




Скриншоты результата, большее разрешение по клику.


И напоследок плюсы и минусы




Плюсы:


  • Вычисления происходят на GPU.

  • Источниками света являются обычные спрайты, соответственно можно делать источник света любой формы.

  • Каждый источник света потребляет очень мало ресурсов.

  • Работает на мобильный устройствах, потребляя ~8 мс за кадр на Nexus 4.

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

  • Поддержка света окружения.

  • Сама по себе система генерирует 6 DrawCalls, все источники света можно уместить один плюс еще один для света окружения.

  • Разноцветные источники света и препятствия.

  • Возможность эмитить источники света в системе частиц. Производительность почти не отличается от обычных партиклов.

  • Гибкие настройки качества.




Минусы:


  • Система освещает по сетке, в следствии чего мелкие препятствия могут игнорироваться. На мощных платформах можно сделать сетку очень мелкой.

  • Необходимо генерировать меши для света окружения и препятствий.

  • Размер камер, в которых создается освещение должен быть больше размера игровой камеры, чтобы корректно отображались источники света за экраном.

  • Вычислительная сложность системы почти не зависит от количества источников. Это означает, что если она потребляет 8 мс за кадр с 10 источниками света, то без источников она так и будет потреблять около 8 мс.


P.S. При наличии интереса сообщества доработаю и выложу в Asset Store.


Recommended article: Chomsky: We Are All – Fill in the Blank.

This entry passed through the Full-Text RSS service - if this is your content and you're reading it on someone else's site, please read the FAQ at http://ift.tt/jcXqJW.


Как подружить QML с чужим OpenGL контекстом. Часть II: Загружаем QML

Международная изоляция сделала Кубу островом хакеров


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


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



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


Куба находится в международной изоляции уже больше полувека, с тех пор как американский президент Эйзенхауэр подписал указ о разрыве дипломатических отношений и первом торговом эмбарго в 1961 году. Ситуация значительно ухудшилась после распада СССР в 1991 году: тогда серьёзно уменьшился импорт нефти, и дефицит стал тотальным.


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


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


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




Инструмент для изготовления дубликатов ключей на двигателе от советской стиральной машины «Эврика»




Самодельный мопед (rikimbili) из велосипеда с мотором




Автобус с сиденьями и окнами в салоне, сделанный из советского грузовика




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




Зарядное устройство для одноразовых батареек




Стартер для двигателей, который запускается при сбрасывании тяжелого предмета с высоты (слева); система ирригации из пластиковых бутылок (справа)




Маленький ноутбук на приборной панели Ford Mercury 1955 года выпуска




Общественный автобус «Верблюд» (El Camello), сваренный из деталей полугрузовиков




Телевизионные антенны из старых подносов для еды




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





Самодельные детские игрушки




Вентиляторы из деталей телефона и виниловых пластинок


Recommended article: Chomsky: We Are All – Fill in the Blank.

This entry passed through the Full-Text RSS service - if this is your content and you're reading it on someone else's site, please read the FAQ at http://ift.tt/jcXqJW.


Искусство Feauture Engineering в машинном обучении

Привет, хабр!


В предыдущей статье (Введение в машинное обучение с помощью Python и Scikit-Learn) мы с вами познакомились с основными этапами решения задач машинного обучения. Сегодня подробнее поговорим о техниках, которые позволяют заметно увеличить качество разрабатываемых алгоритмов. Одна из таких техник — Feature Engineering. Сразу отметим, что это своего рода искусство, обучиться которому можно только прорешав огромное количество задач. Тем не менее, с опытом вырабатываются некие общие подходы, которыми хотелось бы поделиться в данной статье.



Итак, как мы уже знаем, почти любая задача начинается с создания (Engineering) и отбора (Selection) признаков. Методы отбора признаков изучены достаточно хорошо и уже существует большое количество алгоритмов для этого (подробнее о них поговорим в следующий раз). А вот задача создания признаков является своего рода искусством и полностью ложится на плечи Data Scientist'а. Стоит отметить, что именно эта задача зачастую является самой сложной на практике и именно благодаря удачному отбору и созданию признаков получаются очень качественные алгоритмы. Зачастую на kaggle.com побеждают сами по себе простые алгоритмы с хорошо отобранными признаками (отличные примеры — Heritage Provider Network Health Prize или Feature Engineering and Classifier Ensemble for KDD Cup 2010)


Наверное, самый известный и понятный пример Feature Engineering многие из вас уже видели в курсе Andrew Ng. Пример заключался в следующем: с помощью линейных моделей прогнозируется цена дома в зависимости от множества признаков, среди которых есть такие, как длина дома и ширина. Линейная регрессия в данном случае прогнозирует цену дома, как линейную комбинацию ширины и длины. Но ведь любому здравому человеку понятно, что цена дома в первую очередь зависит от площади дома, которая никак не выражается через линейную комбинацию длины и ширины. Поэтому, качество алгоритма существенным образом увеличивается, если длину и ширину заметить на их произведение. Тем самым, мы получим новый признак, который наиболее сильно влияет на цену дома, а также сократим размерность признакового пространства. На мой взгляд, это самый простой и наглядный пример создания признаков. Заметим, что очень сложно придумать метод, который для любой наперед заданной задачи давал бы технику построения признаков. Именно поэтому пост и называется «Искусство Feature Engineering». Тем не менее, существует ряд простых методов и техник, которыми бы хотелось поделиться из собственного опыта:


Категориальные признаки




Допустим, у наших обьектов есть признаки, которые принимают значения на конечном наборе. Например, цвет (color), который может быть синим (blue), красным (red), зеленым (green), или его значение может быть неизвестно (unknown). В таком случае бывает полезным добавлять признаки вида is_red, is_blue, is_green, is_red_or_blue и другие возможные комбинации.

Даты и время




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

Числовые переменные




Если переменная вещественная, часто помогает ее округление или разделение на целую и вещественную часть (с последующей нормализацией). Или же, часто помогает приведение числового признака в категориальный. Например, если есть такой признак как масса, то можно ввести признаки вида «масса больше X», «масса от X до Y».

Обработка строковых признаков




Если есть признак, значением которого есть конечное количество строк — то стоит на забывать, что в самих строках зачастую содержится информация. Наглядным примером является задача Titanic: Machine Learning from Disaster, в которой имена участников плавания имели приставки «Mr.», «Mrs.» и «Miss.», по которым легко извлечь половой признак.

Результаты других алгоритмов




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

Агрегированные признаки




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

Добавление новых признаков




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

Итак, мы рассмотрели несколько техник создания (Engineering) признаков в задачах машинного обучения, которые могут помочь заметно увеличить качество существующих алгоритмов. В следующий раз мы подробнее поговорим о методах отбора (Selection) признаков. К счастью, там будет все проще, потому как для отбора признаков есть уже разработанные техники, в то время как создание признаков, как уже, наверное, заметил читатель, является искусством!


Recommended article: Chomsky: We Are All – Fill in the Blank.

This entry passed through the Full-Text RSS service - if this is your content and you're reading it on someone else's site, please read the FAQ at http://ift.tt/jcXqJW.


Apple заблокировала аккаунты разработчиков в Крыму

В связи с санкциями направленными штатами против жителей Крыма, все забугорные компании должны прекратить свою деятельность на полуострове. Буквально вчера Steam перестал продавать игры жителям Республики, а сегодня все крымские разработчики, которые пользовались developer.apple.com заблокированы на этом сайте.

Письмо, которое я получил сегодня ночью:



Dear Vladimir,


This letter serves as notice of termination of the Registered Apple Developer Agreement (the “RAD Agreement”) between you and Apple, effective immediately.


Apple may terminate your status as a Registered Apple Developer at any time at its sole discretion under the RAD Agreement. The new sanctions on the Crimea Region announced by the US Government on December 19, 2014 and announced by the European Commission on December 18, 2014 prohibit the continuation of the RAD Agreement between you and Apple. For more information, please review Executive Order 13685 and the European Commission notice.


We would like to remind you of your obligations with regard to all software and other confidential information that you obtained from Apple as a Registered Apple Developer. You must promptly cease all use of and destroy such materials and comply with all the other termination obligations set forth in the RAD Agreement.


Sincerely,

Apple Inc.



Ну и конечно, на самом сайте, при входе в свой аккаунт я теперь вижу:



Account problem


There may be a problem with your account. Please contact us.


Sign out



Не уверен в выборе хаба, туда ли?


Recommended article: Chomsky: We Are All – Fill in the Blank.

This entry passed through the Full-Text RSS service - if this is your content and you're reading it on someone else's site, please read the FAQ at http://ift.tt/jcXqJW.


Интернет тест-драйв «Мехатроника и робототехника». Нужна помощь Сообщества

Привет, Хабр!

Пишу — потому что нужна помощь Сообщества.


Меня зовут Александр Лемех. Я являюсь руководителем Лаборатории развития робототехники Механико-машиностроительного института УрФУ. 6 лет назад для меня стало очевидным — что будущее за робототехникой и автоматизацией. С тех пор начал заниматься этим направлением.


Вначале мы с друзьями-единомышленниками организовали Клуб по развитию робототехники в Уральском федеральном университете, потом я стал в университете преподавать дисциплину «Промышленные роботы» и другие, 2 года назад удалось основать Лабораторию развития робототехники Механико-машиностроительного института УрФУ.


Видео о нашей лаборатории:






Несколько лет мы с коллегами вели клуб по робототехнике в Гимназии «Менталитет», г. Екатеринбург. Из этого опыта возникло понимание, что нужно качественное массовое образование по этой теме.

Сейчас в планах открыть специальность магистратуры, а затем и бакалавриата по мехатронике и робототехнике на мехфаке УрФУ.


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


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


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


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

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


В данный момент мы заканчиваем работу над крупным проектом Интернет тест-драйв направление Hi-Tech «Мехатроника и робототехника» , который объединяет несколько частей разных электронных курсов, которые мы делали в этом году: 3д-моделирование и черчение в SolidWorks, Электроника на Ардуино, Программирование в ROS, Летающие роботы и другие.


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


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

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


Видео о проекте:



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


Вот ссылка на установочное видео:



Цель проекта «Hi-Tech-ДРАЙВ» — создание познавательной и профессиональной мотивации к техническим специальностям через изучение электроники, программирования и робототехники, направленный на освоение базовых принципов работы с конструированием, электроникой и программированием.


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


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


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


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


Обращаюсь к Сообществу — помогите сделать действительно крутой курс! Прошу неравнодушных записаться на сайте http://ift.tt/1AZpMGo

и вместе с нашими школьниками пройти его — выявить ошибки, понять как их можно исправить.

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


Сделаем качественное образование по робототехнике вместе.


Роботы — это ваше будущее!


Recommended article: Chomsky: We Are All – Fill in the Blank.

This entry passed through the Full-Text RSS service - if this is your content and you're reading it on someone else's site, please read the FAQ at http://ift.tt/jcXqJW.


[Перевод] Пишем быстрый и экономный код на JavaScript

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

Что бы там ни было — V8, SpiderMonkey (Firefox), Carakan (Opera), Chakra (IE) или что-то ещё, знание внутренних процессов поможет вам оптимизировать работу ваших приложений. Но не призываю вас оптимизировать движок для одного браузера или движка – не делайте так.


Задайте себе вопрос:

— можно ли что-то в моём коде сделать более эффективным?

— какую оптимизацию проводят популярные движки JS?

— что движок не может компенсировать, и может ли сборка мусора подчистить всё так, как я от неё ожидаю?



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



И как же JS работает в V8?




Хотя возможно разрабатывать большие приложения без должного понимания работы движка JS, любой автовладелец скажет вам, что он хоть раз заглядывал под капот автомобиля. Поскольку мне нравится браузер Chrome, я расскажу про его JavaScript-движок. V8 состоит из нескольких основных частей.

Основной компилятор, который обрабатывает JS и выдаёт машинный код перед его исполнением, вместо того, чтобы исполнять байткод или просто интерпретировать его. Этот код обычно не сильно оптимизирован.

— V8 преобразовывает объекты в объектную модель. В JS объекты реализованы как ассоциативные массивы, но в V8 они представлены скрытыми классами, которые являются внутренней системой типов для оптимизированного поиска.

профайлер времени выполнения, который отслеживает работу системы и определяет «горячие» функции (код, который долго выполняется)

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

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

сборка мусора. Представление о её работе так же важно, как представление об оптимизации.


Сборка мусора




Это одна из форм управления памятью. Сборщик пытается вернуть память, занятую объектами, которые уже не используются. В языке с поддержкой сборки мусора, объекты, на которые ещё есть ссылки, не подчищаются.

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



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


Ошибки с удалением ссылок на объекты



В некоторых спорах в онлайне по поводу возврата памяти в JS возникает ключевое слово delete. Хотя изначально оно предназначается для удаления ключей, некоторые разработчики считают, что с его помощью можно провести принудительное удаление ссылок. Избегайте использования delete. В примере ниже delete o.x приносит больше вреда, чем пользы, поскольку меняет скрытый класс у o и делает его медленным объектом.

var o = { x: 1 };
delete o.x; // true
o.x; // undefined


Вы обязательно найдёте отсылки к delete во многих популярных JS-библиотеках, поскольку в нём есть смысл. Главное, что нужно усвоить – не нужно изменять структуру «горячих» объектов во время выполнения программы. Движки JS могут распознавать такие «горячие» объекты и пробовать оптимизировать их. Это будет проще сделать, если структура объекта не сильно меняется, а delete как раз приводит к таким изменениям.


Есть и непонимание по поводу того, как работает null. Установка ссылки на объект в null не обнуляет объект. Писать o.x = null лучше, чем использовать delete, но смысла это не имеет.



var o = { x: 1 };
o = null;
o; // null
o.x // TypeError


Если эта ссылка была последней ссылкой на объект, его затем приберёт сборщик мусора. Если это была не последняя ссылка, до него можно добраться, и сборщик его не подберёт.


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



var myGlobalNamespace = {};


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


Простые правила



Чтобы сборка мусора сработала так рано, как это возможно, и собрала как можно больше объектов, не держитесь за те объекты, которые вам не нужны. Обычно это происходит автоматически, но вот о чём необходимо помнить:

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

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

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


Функции



Теперь обратимся к функциям. Как мы уже сказали, сборка мусора освобождает использовавшиеся блоки памяти (объекты), до которых уже нельзя добраться. Для иллюстрации – несколько примеров.

function foo() {
var bar = new LargeObject();
bar.someCall();
}


По возвращению из foo, объект, на который указывает bar, будет подчищен сборщиком мусора, поскольку на него уже ничего не ссылается.


Сравните с:



function foo() {
var bar = new LargeObject();
bar.someCall();
return bar;
}

// где-то ещё в коде
var b = foo();


Теперь у нас есть ссылка на объект, которая сохраняется, пока вызывавший функцию код не назначит в b что-либо ещё (или пока b не выйдет из области видимости).


Замыкания



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

function sum (x) {
function sumIt(y) {
return x + y;
};
return sumIt;
}

// Использование
var sumA = sum(4);
var sumB = sumA(3);
console.log(sumB); // Возвращает 7


Сборщик мусора не может прибрать созданный функциональный объект, поскольку к нему ещё есть доступ, например через sumA(n). Вот ещё пример. Можем ли мы получить доступ к largeStr?


var a = function () {

var largeStr = new Array(1000000).join('x');

return function () {

return largeStr;

};

}();


Да — через а(), поэтому он тоже не устраняется сборщиком. Как насчёт такого:



var a = function () {
var smallStr = 'x';
var largeStr = new Array(1000000).join('x');
return function (n) {
return smallStr;
};
}();


У нас уже нет к нему доступа, поэтому его можно подчищать.


Таймеры



Одно из наихудших мест для утечек – цикл, или в паре setTimeout()/setInterval(), хотя такая проблема встречается довольно часто. Рассмотрим пример:

var myObj = {
callMeMaybe: function () {
var myRef = this;
var val = setTimeout(function () {
console.log('Время выходит!');
myRef.callMeMaybe();
}, 1000);
}
};


Если мы выполним



myObj.callMeMaybe();


чтобы запустить таймер, каждую секунду будет выводиться “'Время выходит!”. Если мы выполним:



myObj = null;


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


Нужно помнить, что ссылки внутри вызовов setTimeout/setInterval такие, как функции, должны выполниться и завершиться перед тем, как их можно будет подчищать.


Бойтесь ловушек быстродействия




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


Скажем, нам нужен модуль, который:

— читает из локального источника данные, имеющие численные id;

— рисует табличку с этими данными;

— добавляет обработчики событий для кликов по ячейкам.


Сразу появляются вопросы. Как хранить данные? Как эффективно рисовать табличку и вставлять её в DOM? Как обрабатывать события оптимальным образом?


Первый и наивный подход – хранить каждый кусочек данных в объекте, который можно сгруппировать в массив. Можно использовать jQuery для обхода данных и рисования таблицы, а затем добавить её в DOM. И наконец, можно использовать привязку событий, чтобы добавить поведение по клику.


Вот как вы НЕ должны делать:



var moduleA = function () {

return {

data: dataArrayObject,

init: function () {
this.addTable();
this.addEvents();
},

addTable: function () {

for (var i = 0; i < rows; i++) {
$tr = $('


'); for (var j = 0; j < this.data.length; j++) { $tr.append(''); } $tr.appendTo($tbody); } }, addEvents: function () { $('table td').on('click', function () { $(this).toggleClass('active'); }); } }; }();

Дёшево и сердито.


Однако, в данном примере мы проходим только по id, по числовым свойствам, которые можно было бы представить проще в виде массива. Кроме того, прямое использование DocumentFragment и родных методов DOM более оптимально, чем использование jQuery для создания таблицы, и конечно, обрабатывать события через родительский элемент получится гораздо быстрее.


jQuery «за кулисами» непосредственно использует DocumentFragment, но в нашем примере код вызывает append() в цикле, а каждый из вызовов не знает про остальные, поэтому код может не быть оптимизирован. Может, это и не страшно, но лучше проверить это через тесты.


Добавив следующие изменения мы ускорим работу скрипта.



var moduleD = function () {

return {

data: dataArray,

init: function () {
this.addTable();
this.addEvents();
},
addTable: function () {
var td, tr;
var frag = document.createDocumentFragment();
var frag2 = document.createDocumentFragment();

for (var i = 0; i < rows; i++) {
tr = document.createElement('tr');
for (var j = 0; j < this.data.length; j++) {
td = document.createElement('td');
td.appendChild(document.createTextNode(this.data[j]));

frag2.appendChild(td);
}
tr.appendChild(frag2);
frag.appendChild(tr);
}
tbody.appendChild(frag);
},
addEvents: function () {
$('table').on('click', 'td', function () {
$(this).toggleClass('active');
});
}

};

}();


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



moduleG = function () {};

moduleG.prototype.data = dataArray;
moduleG.prototype.init = function () {
this.addTable();
this.addEvents();
};
moduleG.prototype.addTable = function () {
var template = _.template($('#template').text());
var html = template({'data' : this.data});
$tbody.append(html);
};
moduleG.prototype.addEvents = function () {
$('table').on('click', 'td', function () {
$(this).toggleClass('active');
});
};

var modG = new moduleG();


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


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


Советы по оптимизации для V8




Не будем приводить абсолютно все советы, а остановимся на наиболее нужных.

— некоторые модели мешают оптимизации, например связка try-catch. Подробности о том, какие функции могут, или не могут быть оптимизированы, можно подчерпнуть из утилиты d8 при помощи команды --trace-opt file.js

— старайтесь, чтобы ваши функции оставались мономорфными, т.е. чтобы переменные (включае свойства, массивы и параметры функций) всегда содержали только объекты из того же скрытого класса. Например, не делайте так:



function add(x, y) {
return x+y;
}

add(1, 2);
add('a','b');
add(my_custom_object, undefined);


— не загружайтесь из непроинициализированных или удалённых элементов

— не пишите огромные функции, т.к. их сложнее оптимизировать


Объекты или массивы?



— для хранения кучи чисел или списка однотипных объектов используйте массив

— если семантика требует объекта со свойствами (разных типов), используйте объект. Это довольно эффективно с точки зрения памяти, и довольно быстро.

— по элементам с целочисленными индексами итерация будет быстрее, чем по свойствам объекта

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

Между массивами и объектами в JS есть одно серьёзное различие – свойство length. Если вы сами отслеживаете этот параметр, то объекты будут примерно такими же быстрыми, как и массивы.


Советы по использованию объектов



Создавайте объекты через конструктор. Тогда у всех объектов будет один скрытый класс. Кроме того, это чуть быстрее, чем Object.create().

На число разных типов объектов и их сложности ограничений нет (в разумных пределах – длинные цепочки прототипов вредны, а объекты с небольшим количеством свойств представляются движком несколько по-другому и чуть быстрее, чем большие). Для «горячих» объектов старайтесь делать короткие цепочки наследований и небольшое число свойств.


Клонирование объектов



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

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



function clone(original) {
this.foo = original.foo;
this.bar = original.bar;
}
var copy = new clone(original);


Кеширование функций в Модульной модели



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


Вот тест на быстродействие прототипов супротив модулей:



// Модель прототипов
Klass1 = function () {}
Klass1.prototype.foo = function () {
log('foo');
}
Klass1.prototype.bar = function () {
log('bar');
}

// Модель модулей
Klass2 = function () {
var foo = function () {
log('foo');
},
bar = function () {
log('bar');
};

return {
foo: foo,
bar: bar
}
}


// Модули с кешированием функций
var FooFunction = function () {
log('foo');
};
var BarFunction = function () {
log('bar');
};

Klass3 = function () {
return {
foo: FooFunction,
bar: BarFunction
}
}

// Итерационные тесты

// Прототипы
var i = 1000,
objs = [];
while (i--) {
var o = new Klass1()
objs.push(new Klass1());
o.bar;
o.foo;
}

// Модули
var i = 1000,
objs = [];
while (i--) {
var o = Klass2()
objs.push(Klass2());
o.bar;
o.foo;
}

// Модули с кешированием функций
var i = 1000,
objs = [];
while (i--) {
var o = Klass3()
objs.push(Klass3());
o.bar;
o.foo;
}
// Обращайтесь к тесту за подробностями


Если вам не нужен класс, не создавайте его. Вот пример того, как можно улучшить быстродействие, избавившись от накладок, связанных с классами http://ift.tt/1uopIic.


Советы по использованию массивов



Не удаляйте элементы. Если в массиве образуются пустые места, V8 переключается на словарный метод работы с массивами, что делает скрипт ещё медленнее.
Литералы массивов



Полезны, т.к. намекают V8 насчёт типов и количества элементов в массиве. Подходят для небольших и средних массивов.

// V8 знает, что вам нужен массив чисел из 4 элементов:
var a = [1, 2, 3, 4];

// Не надо так:
a = []; // V8 ничего не знает про массив - совсем как Джон Сноу
for(var i = 1; i <= 4; i++) {
a.push(i);
}


Одинарные или смешанные типы



Не смешивайте разные типы в одном массиве (var arr = [1, “1”, undefined, true, “true”])

Тестирование быстродействия смешанных типов


Из теста видно, что быстрее всего работает массив целых чисел.


Разреженные массивы



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

Тестирование разреженных массивов


«Дырявые» массивы



Избегайте дырявых массивов, получающихся при удалении элементов, или присвоении a[x] = foo, где x > a.length). Если удалить всего лишь один элемент, работа с массивом замедляется.

Тест дырявых массивов


Предварительное заполнение массивов или заполнение на лету



Не стоит предварительно заполнять большие массивы (более 64К элементов). Nitro (Safari) работает с предварительно заполненными массивами лучше. Но другие движки (V8, SpiderMonkey) работают иначе.


Тест предзаполненных массивов



// Пустой массив
var arr = [];
for (var i = 0; i < 1000000; i++) {
arr[i] = i;
}

// Предзаполненный массив
var arr = new Array(1000000);
for (var i = 0; i < 1000000; i++) {
arr[i] = i;
}


Оптимизация приложения




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


— измерить (найти узкие места)

— понять (найти, в чём проблема)

— простить исправить


Тесты скорости (бенчмарки)



Обычный принцип измерения скорости – замерять время выполнения и сравнить. Одна модель сравнения была предложена командой jsPerfи используется в SunSpiderи Kraken:

var totalTime,
start = new Date,
iterations = 1000;
while (iterations--) {
// Здесь идёт тестируемый код
}
// totalTime → количество миллисекунд,
// требуемое для выполнения кода 1000 раз
totalTime = new Date - start;


Код помещается в цикл и выполняется несколько раз, затем из времени окончания вычитается время начала.


Но это слишком простой подход – особенно для проверки работы в разных браузерах или окружениях. На быстродействие может влиять даже сборка мусора. Об этом нужно помнить даже при использовании window.performance


Для серьёзного погружения в тестирование кода рекомендую прочесть JavaScript Benchmarking.


Профилирование



Chrome Developer Tools поддерживают профилирование. Его можно использовать, чтобы узнать, какие функции отжирают больше всего времени, и оптимизировать их.


Профилирование начинается с определения точки отсчёта для быстродействия вашего кода – для этого используется Timeline. Там отмечено, как долго выполнялся наш код. В закладке «профили» более подробно указано, что происходит в приложении. Профиль JavaScript CPU показывает, сколько процессорного времени отнял код, CSS selector – сколько времени ушло на обработку селекторов, а Heap snapshots показывает использование памяти.


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



Хорошие инструкции по профилированию находятся здесь: JavaScript Profiling With The Chrome Developer Tools.


В идеале, на профилирование не должны влиять установленные расширения и программы, поэтому запускайте Chrome с параметром the --user-data-dir <пустая_директория>.


Избегаем утечек памяти – техника трёх снимков памяти



В Google Chrome Developer Tools активно используются в проектах вроде Gmail для обнаружения и устранения утечек.


Некоторые параметры, на которые наши команды обращают внимание – приватное использование памяти, размер кучи JS, количество узлов DOM, чистка хранилища, счётчик обработчиков событий, сборка мусора. Знакомым с событийными архитектурами будет интересно, что самые частые проблемы у нас возникали, когда у listen() отсутствует unlisten() (замыкание) и когда нет dispose() для объектов, создающих обработчики событий.


Есть замечательная презентация техники «3 снимков», которая помогает находить утечки через DevTools.


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


Управление памятью в одностраничных приложениях



В современных одностраничных приложениях важно управлять памятью (фреймворки AngularJS, Backbone, Ember), потому что они не перезагружаются. Поэтому утечки памяти могут быстро проявить себя. Это большая ловушка для таких приложений, потому что память ограничена, а приложения работают длительное время (емейл-клиенты, соц.сети). Большая власть – большая ответственность.

В Backbone убедитесь, что вы избавляетесь от старых видов и ссылок через dispose(). Эта функция была добавлена недавно, она удаляет все хендлеры, добавленные в объект events, и все коллекции обработчиков, когда вид передаётся как третий аргумент (в обратных вызовах). dispose() также вызывается в функции view remove(), что решает большинство простых проблем с очисткой памяти. В Ember подчищайте обозревателей, когда они обнаруживают, что элемент был удалён из вида.


Совет от Дерика Бэйли::



Разберитесь, как, с точки зрения ссылок работают события, а в остальном следуйте стандартным правилам по работе с памятью, и всё будет ОК. Если вы загружаете данные в коллекцию Backbone, в которой много объектов User, эта коллекция должна быть подчищена, чтобы она не использовала больше памяти, вам нужно удалить все ссылки на неё и все объекты по отдельности. Когда вы удалите все ссылки, всё будет очищено.



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


Ещё один хороший тьюториал по отладке утечек в Node.


Минимизируем пересчёт позиций и размеров элементов при обновлении внешнего вида страницы



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


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



function addDivs(element) {
var div;
for (var i = 0; i < 20; i ++) {
div = document.createElement('div');
div.innerHTML = 'Heya!';
element.appendChild(div);
}
}


Вместо этого можно использовать DocumentFragment, добавить div к нему, а затем добавить его в DOM через appendChild. Тогда все наследники фрагмента будут добавлены к странице за один пересчёт.



function addDivs(element) {
var div;
// Creates a new empty DocumentFragment.
var fragment = document.createDocumentFragment();
for (var i = 0; i < 20; i ++) {
div = document.createElement('a');
div.innerHTML = 'Heya!';
fragment.appendChild(div);
}
element.appendChild(fragment);
}


Подробнее – в статьях Make the Web Faster, JavaScript Memory Optimization и Finding Memory Leaks.


Детектор утечек памяти JavaScript



Чтобы помочь с обнаружением утечек, была разработана утилита для Chrome Developer Tools, работающая через протокол удалённой работы, которая делает снимки кучи и выясняет, какие объекты служат причиной утечки.


Рекомендую ознакомиться с постом на эту тему или почитать страницу проекта.


Флаги V8 для оптимизации отладки и сборки мусора



Отслеживание оптимизации:

chrome.exe --js-flags="--trace-opt --trace-deopt"


Подробнее:


trace-opt – записывать имена оптимизированных функций и показывать пропущенный код, с которым оптимизатор не справился

trace-deopt – записывать код, который пришлось деоптимизировать при выполнении

trace-gc – записывать каждый этап сборки мусора


Оптимизированные функции помечаются звёздочкой (*), а не оптимизированные – тильдой (~).


Пикантные подробности о флагах и внутренней работе V8 читайте в посте Вячеслава Егорова.


Время высокого разрешения и Navigation Timing API



Время высокого разрешения (High Resolution Time, HRT) – это интерфейс JS для доступа к таймеру с разрешением меньше миллисекунды, который не зависит от смены времени пользователем. Полезен для написания тестов быстродействия.

Доступен в Chrome (stable) как window.performance.webkitNow(), а в Chrome Canary без префикса -window.performance.now(). Пол Айриш написал об этом подробно в своём посте на HTML5Rocks.


Если нам необходимо измерить работу приложения в вебе, нам поможет Navigation Timing API. С его помощью можно получить точные и подробные измерения, выполняемые при загрузке страницы. Доступно через window.performance.timing, которую можно использовать прямо в консоли:



Из этих данных можно узнать много полезного. К примеру, задержка сети responseEnd-fetchStart; время, которое потребовалось потратить на загрузку страницы после получения с сервера loadEventEnd-responseEnd; время между загрузкой страницы и стартом навигации loadEventEnd-navigationStart.


Подробности можно узнать в статье Measuring Page Load Speed With Navigation Timing.


about:memory и about:tracing



about:tracing в Chrome показывает интимные подробности о быстродействии браузера, записывая всю его деятельность в каждом из тредов, закладок и процессов.


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


Хорошая статья про использование about:tracing для профилирования WebGL-игр.


about:memory в Chrome – также полезная штука, которое показывает, сколько памяти использует каждая закладка – это можно использовать для поиска утечек.


Заключение




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


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


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


Recommended article: Chomsky: We Are All – Fill in the Blank.

This entry passed through the Full-Text RSS service - if this is your content and you're reading it on someone else's site, please read the FAQ at http://ift.tt/jcXqJW.









' + this.data[j]['id'] + '

02 Ссылки для UX-специалистов

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







  • http://inspireux.com — статьи и советы по UX-дизайну;

  • johnnyholland.org — популярный источник информации на различных UX темы;

  • http://uxbooth.com/ ... — лучшие «работающие» и «неработающие» UX-практики;

  • http://ift.tt/dZTkxh ... — 5 способов использовать Google Analytics для ваших UX-исследований;

  • smallnudg.es — лучшие примеры маленьких деталей в интерфейсах, приводящих к большим конверсиям;

  • usabilitypost.com — блог с мыслями о дизайне и пользовательском опыте;

  • http://ift.tt/1eV2WZf ... — интереснейший рассказ о том как в Flipboard придумали движок создания кастомных макетов;

  • reallygoodemails.com/ — большая подборка красивой вёрстки электронных писем;

  • http://medium.com/ ... — подборка лучших ссылок за минувший год по теме UX;

  • http://ift.tt/14KrEah ... — тёмные паттерны UX у LinkedIn;

  • http://ift.tt/1wNBQe3 — подробное руководство по практическому применению методов человеко-ориентированного дизайна;

  • http://ift.tt/1y5ONjh — экспериментальные Lean UX-исследования в Яндексе;

  • http://usemenot.com.ua/ ... — eye-tracking: кейсы и инсайты

  • http://medium.com/ ... — почему я занимаюсь UX-дизайном для Enterprise, и вам тоже советую;

  • http://ift.tt/LwhLo5 ... — роль UX-дизайнера от начала до запуска продукта в стартапе;

  • http://uxmag.com/… — увеличение вовлеченности пользователей с помощью вознаграждений в процессе взаимодействия;





  • http://ift.tt/1DLhJAB — презентация о создание UX-стратегии для носимых устройств и мобильных мэйнфреймов;

  • http://ift.tt/14KrEar ... — ещё одна презентация, но об идеях по интеграции пользовательского опыта в растущем сегменте носимых устройств и датчиков;

  • http://ift.tt/pXsGrS ... — проектирование приложений для носимых устройств на базе Android Wear;

  • http://youtube.com/ ... — коллекция видео на тему носимых гаджетов и опыте взаимодействия с ними;

  • http://medium.com/ ... — рассказа о дизайне для носимых технологиях;

  • http://ift.tt/tcLcdR ... — заметка о том, как носимые технологии повлияют на способы оплаты;

  • http://ift.tt/1DLhLbO ... — презентация с конференции Tizen: «Дизайн для носимых устройств»;

  • http://ift.tt/rQW57O ... — статья о 5 неявных принципов дизайна для «умных» часов;

  • gumroad.com/l/qNbNb — в продолжение темы часов — набор .psd с графическими элементами интерфейса Apple Watch;

  • http://ift.tt/1ki9KPm ... — статья о UX носимых устройств;





  • www.justinmind.com — мощный сервис прототипирования;

  • www.flinto.com — iOS и Android прототипы, сделанные из отрисованных заранее макетов экранов;

  • appgif.io — превратит показ дизайна вашего приложения в анимированную гифку;

  • http://ift.tt/1fcWUg3 — приложение для iOS, с коллекцией анимаций в интерфейсах от capptivate.co;

  • mockingbot.com — простой и интересный инструмент прототипирования от китайских разработчиков;

  • www.mockflow.com — ещё одна платформа для создания прототипов, с возможностью планирования проекта;

  • www.hotgloo.com — инструмент для совместного прототипирования;

  • www.protoshare.com — здесь можно помимо прототипов даже сайты создавать;

  • interactions.webflow.com — утверждают, что теперь мы сможем проектировать взаимодействия и анимации без единой строчки кода;

  • usabilityhub.com — сервис для проведения тестирования ваших дизайн-макетов;

  • www.userreport.com — инструмент для сбора фидбэка от пользователей и различных данных в вашем продукте;

  • doorbell.io — ещё один схожий инструмент, что и выше;

  • www.seevolution.com — сервис отслеживания тепловых карт ваших сайтов и аналитикой;

  • www.inspectlet.com — эдакий иностранный аналог нашего «Вебвизора»;

  • marklyapp.com — инструмент для создания спецификаций в Photoshop и Sketch;


Спасибо за внимание!

Ссылка на предыдущий выпуск — 02 Ссылки для UX-специалистов


Recommended article: Chomsky: We Are All – Fill in the Blank.

This entry passed through the Full-Text RSS service - if this is your content and you're reading it on someone else's site, please read the FAQ at http://ift.tt/jcXqJW.


пятница, 16 января 2015 г.

[Из песочницы] Как мы строим систему обработки сообщений

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


По каналам связи устройства присылают сообщения на наш шлюз (gateway) – входную точку приложения. Задача приложения – разобраться, что именно пришло, произвести необходимые действия и сохранить информацию в базе данных для дальнейшего анализа. Базу мы будем рассматривать как конечную точку обработки. Звучит просто, но с ростом количества и разнообразия сообщений появляется несколько нюансов, которые я и хочу обсудить.



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


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


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


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



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


Таков базовый расклад, о чем же еще надо подумать?


Определяемся с ценностями




1. Простота создания, изменения и поддержки



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

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


2. Отказоустойчивость



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

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


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


3. Производительность



Число сообщений, обрабатываемых в единицу времени, задержки, нагрузки на сервера — всё это важные параметры производительности системы. Именно поэтому мы закладываем в проект гибкую архитектуру.

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


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


Настраиваем мозг




1. Оперируйте очередями и асинхронными обработчиками



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



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



Обычно, сообщение приходит в формате какого-то протокола взаимодействия устройств в сети: бинарном, xml, json и т.д. Декодируйте и переводите их в свой внутренний формат как можно раньше. Это позволит решить минимум две задачи. Во-первых, протоколов может быть несколько; после декодирования вы сможете унифицировать формат дальнейших сообщений. Во-вторых, упрощается логирование и отладка.
4. Сделайте изменение конфигурации очередей легким



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



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

Создаем инструменты




1. Реализуйте тотальное наблюдение



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


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


2. Тестируйте всё



Системы обработки сообщений — идеальный полигон для автоматизированного тестирования. Протокол входных данных определен и ограничен, никаких взаимодействий с живыми людьми. Покрывайте код модульными тестами. Предусмотрите возможность заменить боевые очереди на тестовые очереди в локальной памяти и делайте быстрые тесты взаимодействия обработчиков. Наконец, делайте полноценные интеграционные тесты, которые можно гонять в бета (staging) окружении (а лучше и в продукции).
3. Заведите отстойник для ошибочных сообщений



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

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


4. Автоматизируйте развертывание



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

Вместо заключения




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

Наша метафора похожа на вот эту картинку из статьи дядюшки Боба The Clean Architecture:



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


Recommended article: Chomsky: We Are All – Fill in the Blank.

This entry passed through the Full-Text RSS service - if this is your content and you're reading it on someone else's site, please read the FAQ at http://ift.tt/jcXqJW.


[Из песочницы] Уязвимость MS14-063 в драйвере FastFat в ОС Windows. Разбор полетов

В данном исследовании проведем анализ уязвимости MS14-063, связанной с некорректной работой драйвера fastfat.sys и приводящей (по крайней мере, по словам Microsoft) к несанкционированному повышению привилегий. Данной уязвимости до недавнего времени были подвержены Win Server 2003/2008 и Win Vista (в Win7 данная дыра была исправлена давным давно, кстати говоря, но это уже совсем другая история — об этом подробнее рассказывается в статье на ресурсе xakep.ru). Тут же мы поговорим о том, какие возможности могла на самом деле предоставить данная уязвимость злоумышленнику, решившему реализоващью атаку типа BadUSB.



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

image

Иллюстрация построения файловой системы FAT, взятая с ресурса c-jump.com (ссылка)


В начале тома (по нулевому смещению) находится так называемый Boot Sector (на самом деле BIOS Parameter Block, или, сокращенно, BPB). Это структура, описывающая общее устройство тома и необходимая для того, чтобы том был правильно обработан драйвером. В этой структуре множество полезных полей, и к некоторым из них мы еще вернемся. Главное, что сейчас стоит отметить — это то, что мы говорим именно о файловой системе Fat32, т.к. в других реализациях FAT смещения в BPB будут другими, причем не только они.


Далее, за BPB, следует сама FAT — структура (FAT Data Structure), по сути своей, являющаяся односвязный списком кластеров файла (весь диск разбит на кластеры (причем файл может занимать только кластер целиком, даже если не полностью), размер которых задается при форматировании диска — в свою очередь, кластеры разбиты на сектора)


image

Иллюстрация устройства области хранения информации (Data Area) на диске, взятая с ресурса technet.microsoft.com (ссылка)


На данном этапе необходимо вспомнить про BPB, а точнее обратить внимание на поле NumFats. Согласно спецификации, это поле задает количество копий FAT — структуры для каждого файла. В той же спецификации утверждается, что стандартное значение (которое, соответственно, гарантирует совместимость ФС с различными драйверами) для данного поля это 2, т.е. для каждого файла существует 2 копии FAT-структуры — это необходимо для предотвращения потери файла в случае появления bad sector’ов. Именно в обработке этого поля и кроется уязвимость. В случае, когда numfat > 2 происходит условный переход на участок кода, неверно выделяющий память в kernel pool (т.к. код исполняется внутри драйвера).


image

Иллюстрация участка уязвимого кода функции FatCommonWrite драйвера fastfat.sys, взятая с ресурса blog.beyondtrust.com — блога специалистов из компании BeyondTrust, также исследовавших данную уязвимость (ссылка)


Уязвимость находится в функции FatCommonWrite, а точнее в вызове ExAllocatePoolWithTag, где в качестве аргумента — необходимого места передается значение numfat — количество копий структур IoRuns, не умноженное на их размер. В коде, приведенном на иллюстрации, в регистре ecx находится указатель на структуру BPB, а по смещению 96h от него находится поле NumFats, которое, как видно из иллюстрации, попадает на стек в качестве аргумента NumberOfBytes у функции ExAllocatePoolWithTag (кстати говоря, в качестве патча, выпущенного от Microsoft, является как раз добавления недостающего умножения в блоке, приведенном на иллюстрации).


На данном этапе стоит подробнее поговорить об устройстве памяти драйверов (очень хороший разбор данного класса уязвимостей есть на хабре — ссылка). Резюмируя ее, имеем: память драйверов разбита на Pool’ы, каждый из которых является списком Chunk’ов, в которых, соответственно лежит необходимая драйверу информация.


Таким образом, здесь имеет место уязвимость типа kernel pool overflow в участке памяти, и, как следствие, существует возможность ее эксплуатации посредеством перебития chunk'а памяти другого драйвера, лежащего в памяти сразу за переполняемым chunk'ом.


image

Иллюстрация процесса переполнения chunk’а, взятая статьи на хабре о Kernel Pool Overflow (ссылка)


На данном этапе мы уже добились некоторого результата — мы корраптим память другого драйвера, что приводит к BSOD’у с ошибкой Bad Pool Header. Но этого мало, т.к. Memory Corruption это еще далеко не повышение привилегий.


Нашей целью является изменение header'а атакуемого (лежащего сразу за переполняемым) чанка на корректный, но не валидный, а произвольный (с целью захвата потока памяти драйвера). Это потенциально приведет к тому, что у атакующего будет возможность исполнить код от имени драйвера, т.е. с повышением привилегий. Однако, как показал анализ данных, которыми мы переполняем chunk, такой возможности в данной ситуации нет. Для обоснования этого рассмотрим структуру IoRuns, для которой как раз и выделяется память в уязвимой функции. Предположительно (опять же исходя из исходников Windows 2000), структура IoRuns служит инструментом, используемым при записи FAT-структур в файловую систему (т.к. функция FatCommonWrite вызывает как раз при записи новых файлов на диск, т.е. создание/изменение FAT-структур). Смысл в том, что все копии (их кол-во регламентируется полем NumFats в BPB) записываются асинхронно, для этого каждой копии ставится в соответствие свой экземпляр IoRuns, в котором указано, по какому оффсету писать(поля Vbo и Lbo) и сколько писать (ByteCount).


image

Иллюстрация цикла инициализации структур IoRuns (согласно иcходникам из WDK 8.0 Samples )


Таким образом, значения в полях структур IoRuns не так просто подменить, причем с все остальные структуры будут зависеть, а вернее, однозначно вычисляться, из значений полей первой структуры. Что мы имеем: первое поле — смещение, по которому начинается запись копий FAT-структур, второе поле — смещение, по которому записывается данная копия (согласно значению литератора), третье поле — вероятно, нужно для обработки дополнительного пролога к FAT-структуре. И последнее поле задается напрямую из BPB — оно равно полю BPB_BytsPerSec, т.к. каждая копия FAT-структура занимает один сектор.


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


Подведем итог: данное исследование приводит к вопросу, правы ли специалисты из Microsoft, говоря о том, что MS14-063 действительно существенна и может привести к эскалации привилегий? Согласно проведенному анализу, возможность “навредить” у данной уязвимости имеется только в вызове BSOD, но говорить о BadUSB и каких либо эскалаций говорить не приходится.


Recommended article: Chomsky: We Are All – Fill in the Blank.

This entry passed through the Full-Text RSS service - if this is your content and you're reading it on someone else's site, please read the FAQ at http://ift.tt/jcXqJW.