-Поиск по дневнику

Поиск сообщений в rss_rss_hh_new

 -Подписка по e-mail

 

 -Статистика

Статистика LiveInternet.ru: показано количество хитов и посетителей
Создан: 17.03.2011
Записей:
Комментариев:
Написано: 51

Habrahabr/New








Добавить любой RSS - источник (включая журнал LiveJournal) в свою ленту друзей вы можете на странице синдикации.

Исходная информация - http://habrahabr.ru/rss/new/.
Данный дневник сформирован из открытого RSS-источника по адресу http://feeds.feedburner.com/xtmb/hh-new-full, и дополняется в соответствии с дополнением данного источника. Он может не соответствовать содержимому оригинальной страницы. Трансляция создана автоматически по запросу читателей этой RSS ленты.
По всем вопросам о работе данного сервиса обращаться со страницы контактной информации.

[Обновить трансляцию]

Опыт внедрения PSR стандартов в одном легаси проекте

Пятница, 29 Сентября 2017 г. 00:08 + в цитатник
Fantyk сегодня в 00:08 Разработка

Опыт внедрения PSR стандартов в одном легаси проекте

    Всем привет!
    В этой статье я хочу рассказать о своем опыте переезда на “отвечающую современным трендам” платформу в одном legacy проекте.


    Все началось примерно год назад, когда меня перекинули в “старый” (для меня новый) отдел.
    До этого я работал с Symfony/Laravel. Перейдя на проект с самописным фреймворком количество WTF просто зашкаливало, но со временем все оказалось не так и плохо.
    Во-первых, проект работал. Во-вторых, применение шаблонов проектирования прослеживалось: был свой контейнер зависимостей, ActiveRecord и QueryBuilder.
    Плюс, был дополнительный уровень абстракции над контейнером, логгером, работе с очередями и зачатки сервисного слоя(бизнес логика не зависела от HTTP слоя, кое-где логика была вынесена из контроллеров).


    Далее я опишу те вещи, с которыми трудно было мириться:


    1. Логгер log4php


    Сам по себе логгер работал и хорошо. Но были жирные минусы:


    • Отсутствие интерфейса
    • Сложность конфигурации для задач чуть менее стандартных (например, отправлять логи уровня error в ElastickSearch).
    • Подавляющее большинство компонентов мира opensource зависят от интерфейса Psr\Log\LoggerInterface. В проекте все равно пришлось держать оба логгера.

    2-6. Контроллеры были вида:


    https://habrahabr.ru/post/337692/


    Метки:  

    Криптоалгоритмы. Классификация с точки зрения количества ключей

    Четверг, 28 Сентября 2017 г. 23:26 + в цитатник

    Метки:  

    На шаг ближе к С++20. Итоги встречи в Торонто

    Четверг, 28 Сентября 2017 г. 22:47 + в цитатник
    antoshkka сегодня в 22:47 Разработка

    На шаг ближе к С++20. Итоги встречи в Торонто

      Несколько недель назад состоялась встреча международного комитета по стандартизации C++. На ней люди (в основном) не разменивались на мелочи и совершили несколько больших шагов на пути к С++20.

      image

      Главные новости:

      • Расширению Concepts быть в C++20!
      • Ranges, Networking и Coroutines/сопрограммы: выпущены в эксперимент в виде TS.
      • Модули: черновик TS готов.

      Что всё это значит, как это упростит написание кода и что было ещё — читайте под катом.

      Concepts


      Замечательная вещь под названием Concepts внесена в черновик будущего стандарта С++20. Это большая радость для разработчиков обобщенных библиотек, использующих идиому SFINAE.

      Мотивирующий пример для любителей SFINAE
      В вашей библиотеке есть функции `*_fast` и `*_slow`, принимающие на вход два шаблонных параметра `v` и `data`:

      1. `*_fast` — сильно соптимизированы, но требуют, чтобы следующие операции возвращали T& и были валидны:

            v += data;
            v -= data;
            v *= data;
            v /= data;
        
      2. `*_slow` — медленные, но работают со всеми типами данных.

      Задача — написать функции `*_optimal`, которые используют версию `*_fast`, если это возможно:

      #include 
      
      template 
      void compute_vector_fast(Container& v, const Data& data) {
          std::cout << "fast\n";
          // ...
      }
      
      template 
      void compute_vector_slow(Container& v, const Data& data) {
          std::cout << "slow\n";
          // ...
      }
      
      template 
      void compute_vector_optimal(Container& v, const Data& data) {
          // ??? call `compute_vector_slow(v, data)` or `compute_vector_fast(v, data)` ???
      }
      

      Без концептов эта задача, например, решается через `std::enable_if_t` и множество нечитаемого шаблонного кода.

      С концептами всё намного проще:

      #include 
      
      template 
      concept bool VectorOperations = requires(T& v, const Data& data) {
          { v += data } -> T&;
          { v -= data } -> T&;
          { v *= data } -> T&;
          { v /= data } -> T&;
      };
      
      template 
          requires VectorOperations
      void compute_vector_optimal(Container& v, const Data& data) {
          std::cout << "fast\n";
      }
      
      template 
      void compute_vector_optimal(Container& v, const Data& data) {
          std::cout << "slow\n";
      }
      


      Концепты позволяют:

      • писать более простой шаблонный код,
      • выдавать более короткие сообщения об ошибках при использовании неверных шаблонных параметров (в теории, пока что это не так!).

      С концептами уже можно поэкспериментировать в GCC, если использовать флаг -fconcepts, например, тут. Вот последний доступный proposal на Concepts.

      Ranges TS


      Ranges увидят свет в виде технической спецификации. Это значит, что поэкспериментировать с ними можно будет еще до C++20.

      С Ranges можно писать `sort(container)` вместо `sort(container.begin(), container.end())`, нужно только заиспользовать нужный namespace.

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

      #include 
      #include ranges/algorithm>
      namespace ranges = std::experimental::ranges;
      
      int main () {
          // Функция get_some_values_and_delimiter() фозвращает вектор,
          // в котором гарантированно есть число 42
          std::vector v2 = get_some_values_and_delimiter();
      
          // Необходимо найти число 42 и отсортировать все элементы, идущие после него:
          auto it = ranges::find(v.begin(), ranges::unreachable{}, 42);
          ranges::sort(++it, v.end());
      }
      

      Нечто подобное Александреску делал для получения супербыстрого поиска.

      Любителям SFINAE и обобщённых библиотек Ranges тоже принесут счастье, так как они определяют огромное количество концептов: Sortable, Movable, Copyable, DefaultConstructible, Same…

      Можно поэкспериментировать, скачав библиотеку отсюда. Вот последний доступный черновик Ranges.

      Networking TS


      Все, что необходимо для работы с сокетами (в том числе для асинхронной работы), будет выпущено в эксперимент еще до C++20. В основе Networking TS лежит доработанный и улучшенный ASIO.

      Вот пара приятных различий:
      • В Networking TS можно передавать move-only callback. В ASIO для этого надо было на свой страх и риск поплясать с бубном макросом BOOST_ASIO_DISABLE_HANDLER_TYPE_REQUIREMENTS. Так что если у вас есть функциональный объект с unique_ptr, то его можно спокойно использовать для callback.
      • Больше constexpr для базовых типов (например, для ip::address).
      • Вменяемые способы передачи аллокаторов: можно в класс-callback добавить allocator_type и метод allocator_type get_allocator(); можно специализировать шаблон associated_allocator и описать, какие методы класса надо дергать вместо get_allocator().

      Можно поэкспериментировать, скачав библиотеку отсюда. Вот последний доступный черновик Networking.

      Coroutines TS


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

      Самый смак получается, если смешать Coroutines TS и Networking TS. Тогда вместо асинхронного нечитабельного кода на +100 строк можно получить то же самое, но на 40 строк:

      #include 
      #include 
      #include 
      #include net>
      
      using net = std::experimental::net;
      using net::ip::tcp;
      
      std::string make_daytime_string() {
          using namespace std; // For time_t, time and ctime;
          time_t now = time(0);
          return ctime(&now);
      }
      
      void start_accept(net::io_context& io_service) {
          tcp::acceptor acceptor{io_service, tcp::endpoint(tcp::v4(), 13)};
      
          while (1) {
              tcp::socket socket(acceptor.get_io_service());
              auto error = co_await acceptor.async_accept(socket, net::co_future);
              if (error) break;
      
              std::string message = make_daytime_string();
              auto& [error, bytes] = co_await async_write(
                  socket, net::buffer(message), net::co_future
              );
              if (error) break;
          }
      }
      
      int main() {
          net::io_context io_service;
          io_service.post([&io_service](){
              try {
                  start_accept(io_service);
              } catch (const std::exception& e) {
                  std::cerr << e.what() << std::endl;
              }
          });
      
          io_service.run();
      }
      

      Но вот плохие новости: такую интеграцию Coroutines TS и Networking TS в стандарт еще не привнесли. Пока что придется реализовывать ее самим.

      С сопрограммами уже можно поэкспериментировать в CLANG-6.0, если использовать флаги -stdlib=libc++ -fcoroutines-ts, например, тут. Вот последний доступный черновик Coroutines.

      Модули


      Подготовлен черновик TS. Дело сдвинулось и есть шансы увидеть модули уже в течение года!

      Что такое модули и почему они лучше заголовочных файлов?
      Когда вы собираете проект, каждый файл cpp может компилироваться параллельно (это хорошо, отличная масштабируемость).

      Однако, как правило, вы используете одни и те же заголовочные фалы в каждом файле cpp. За счет того, что компиляция различных файлов cpp никак не связана друг с другом, при каждой компиляции компилятор разбирает одни и те же заголовочные файлы снова и снова. Именно этот разбор и тормозит (заголовочный файл iostream весит более мегабайта, подключите 20 подобных заголовочных файлов — и компилятору придётся просмотреть и разобрать около 30 мегабайт кода при компиляции одного файла cpp).

      И тут на сцену выходят модули! Модуль — это набор файлов, собранных воедино и сохранённых на диск в понятном для компилятора бинарном виде. Таким образом, при подключении модуля компилятор просто считает его с диска в свои внутренние структуры данных (минуя этапы открытия нескольких фалов, парсинга, препроцессинга и некоторых другие вспомогательные этапы).

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

      И наконец, финальная стадия сборки проекта — линковка. В данный момент линковщик может тратить много времени на выкидывание одинаковых блоков скомпилированного кода (вы написали функцию inline/force_inline, 100 раз её использовали, а компилятор решил её не встраивать — линкер выкинет 99 скомпилированных тел вашей функции и оставит одно). С модулями такого происходить не должно, поскольку файл модуля не будет «вкомпиливаться» внутрь собранного файла cpp.

      Модули в черновике не экспортируют макросы, поэтому будет сложновато использовать их для системных файлов с множеством макросов (``, я на тебя намекаю!) и поддерживать код, использующий модуль и его старый заголовочный файл (если у вас std::string описан в модуле и в заголовочном файле , то при подключении модуля и заголовочного файла будет multiple definitions, поскольку макрос для include guards не экспортируется из модуля). Это как раз такие модули, за которые вы проголосовали в прошлом посте (ваши голоса мы донесли до комитета).

      Вот последний доступный черновик Modules.

      Мелочи, принятые в C++20


      В C++20 можно будет инициализировать bitfields в описании класса:

      struct S {
          unsigned x1:8 = 42;
          unsigned x2:8 { 42 };
      };
      

      Можно будет понимать платформы endianness стандартными методами:

      if constexpr (std::endian::native == std::endian::big) {
          // big endian
      } else if constexpr (std::endian::native == std::endian::little) {
          // little endian
      } else {
          // mixed endian
      }
      

      Можно будет инициализировать поля структур, прям как в чистом C:

      struct foo { int a; int b; int c; };
      foo b{.a = 1, .b = 2};
      

      У лямбд можно будет явно указывать шаблонные параметры:

      auto bar = [](Args&&... args) {
          return foo(std::forward(args)...);
      };
      

      Заслуги РГ21


      На встречу в Торонто мы ездили с несколькими предложениями:

      • P0652R0 — конкурентные ассоциативные контейнеры. Комитет хорошо встретил предложение, посоветовал провести эксперименты для улучшения ряда мест, посоветовал улучшения в интерфейсе. Начали работу над следующей версией предложения.
      • P0539R1 — integers, размер (количество байт) которых задаётся на этапе компиляции. Возникли споры по поводу интерфейса (указывать шаблонным параметром биты, байты или машинные слова), так что в следующей итерации необходимо будет привести плюсы и минусы различных подходов.
      • P0639R0 — наше предложение направить усилия в сторону разработки `constexpr_allocator` вместо `constexpr_string + constexpr_vector`. Встретили крайне благосклонно, проголосовали за. Следующие наши шаги — прорабатывать тему вместе с Давидом.
      • P0415R0 — constexpr для std::complex. Предложение было одобрено и теперь находится в подгруппе LWG (вместе с предложением на constexpr для стандартных алгоритмов). Должно быть в скором времени смержено в черновик C++20.

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

        Кроме того, ряду библиотек уже нужны constexpr-функции: [1], [2].

      Вдобавок нас попросили представить комитету два предложения, непосредственно над написанием которых мы не работали:

      • P0457R0 — starts_with и ends_with для строк. Комитет предложил сделать это в виде свободных функций. Один из присутствующих сказал, что у них в компании есть эти методы и их используют чаще, чем остальные алгоритмы вместе взятые. Все с нетерпение ждут новой версии proposal, уже со свободными функциями.
      • P0458R0 — функция contains(key) member для классов [unordered_]map/set/multimap/multiset. Комитету идия пришлась по душе, почти отправили в LWG для внедрения в C++20.

      На подходе


      Обсуждали предложение по форматированию текста, и многим понравились предлагаемые возможности (вероятно, потому, что людям нравится Python):

      fmt::format("The answer is {}", 42);

      Обсуждали ring_span, который по функциональности напоминает boost::circular_buffer, но не владеет элементами (является view над контейнером).

      На подходе битовые операции. Когда их примут, правильным ответом на вопрос «Как подсчитать количество выставленых битов в переменной X?» на собеседованиях станет «std::popcount(X)».

      Планы и прочее


      РГ21 планирует в ближайшее время написать предложения на std::stacktrace (в качестве прототипа послужит Boost.Stacktrace), доработать предложение на std::shared_library и на экспорт символов из динамических библиотек.

      Если у вас есть идеи для C++20, если вы нашли проблемы в C++17/14/11 либо просто хотите подстегнуть разработку той или иной фичи C++ — заходите на сайт рабочей группы stdcpp.ru. Добро пожаловать!

      Есть желание помочь с написанием предложений и внести своё имя в историю? Мы подготовили мини-инструкцию по написанию предложений.
      Original source: habrahabr.ru (comments, light).

      https://habrahabr.ru/post/336264/


      Запуск регулярных задач на кластере или как подружить Apache Spark и Oozie

      Четверг, 28 Сентября 2017 г. 22:45 + в цитатник
      entony сегодня в 22:45 Разработка

      Запуск регулярных задач на кластере или как подружить Apache Spark и Oozie


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


        Содержание


        • Задача
        • Оборудование и установленное ПО
        • Написание Spark задачи
        • Написание workflow.xml
        • Написание coordinator.xml
        • Размещение проекта на hdfs
        • Запуск регулярного выполнения
        • Заключение

        Задача


        Мы имеем следующую структуру на hdfs:


        hdfs://hadoop/project-MD2/data
        hdfs://hadoop/project-MD2/jobs
        hdfs://hadoop/project-MD2/status

        В директория data ежедневно поступают данные и раскладываются по директориям в соответствие с датой. Например, данные за 31.12.2017 запишутся по следующему пути: hdfs://hadoop/project/data/2017/12/31/20171231.csv.gz.


        Формат входных данных

        • Разделитель строк: “\n”
        • Разделитель столбцов: “;”
        • Способ сжатия: gzip
        • Количество столбцов: 5 (device_id; lag_A0; lag_A1; flow_1; flow_2)
        • Заголовок в первой строке отсутствует
        • Данные за предыдущие сутки гарантированно записывается в соответствующую директория в интервал времени с 00:00 до 03:00 следующих суток.

        В директории jobs располагаются задачи, которые имеют непосредственное отношение к проекту. Нашу задачу мы также будем размещать в этом каталоге.
        В директорию status должна сохраняться статистика по количеству пустых полей за каждый день в формате json. Например, для данных за 31.12.2017 должен будет появиться файл hdfs://hadoop/project-MD2/status/2017/12/31/part-*.json


        Примет json файла:

        {
           "device_id_count_empty" : 0, 
           "lag_A0_count_empty" : 10, 
           "lag_A1_count_empty" : 0, 
           "flow_1_count_empty" : 37, 
           "flow_2_count_empty" : 100
        }

        Оборудование и установленное ПО


        В нашем распоряжение есть кластер из 10 машин, каждая из которых имеет 8-и ядерный процессор и оперативную память в размере 64 Гб. Общий объём жёстких дисков на всех машинах 100 Тб. Для запуска задач на кластере отведена очередь PROJECTS.


        Установленное ПО:

        • Apache Hadoop 2.7.3 (Hortonworks)
        • Apache Spark 2.0.0
        • Apache Oozie 4.2.0
        • Scala 2.11.11
        • Sbt 1.0.2

        Написание Spark задачи


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


        mkdir -p daily-statistic/project
        echo "sbt.version = 1.0.2" > daily-statistic/project/build.properties
        echo "" > daily-statistic/project/plugins.sbt
        echo "" > daily-statistic/build.sbt
        mkdir -p daily-statistic/src/main/scala

        Замечательно, теперь добавил плагин для сборки, для этого в файле daily-statistic/project/plugins.sbt добавляем следующую строку:


        addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "0.14.5")

        Добавил описание проекта, зависимости и особенности сборки в файл daily-statistic/build.sbt:


        name := "daily-statistic"
        
        version := "0.1"
        
        scalaVersion := "2.11.11"
        
        libraryDependencies ++= Seq(
          "org.apache.spark" %% "spark-core" % "2.0.0" % "provided",
          "org.apache.spark" %% "spark-sql" % "2.0.0" % "provided"
        )
        
        assemblyJarName in assembly := s"${name.value}-${version.value}.jar"

        Перейдём в директорию daily-statistic и выполнил команду sbt update, для обновления проекта и подтягивания зависимостей из репозитория.
        Создаём Statistic.scala в директории src/main/scala/ru/daily


        Код задачи:

        package ru.daily
        
        import org.apache.spark.sql.SparkSession
        import org.apache.spark.sql.DataFrame
        import org.apache.spark.sql.functions._
        
        object Statistic extends App {
        
           // инициализация   
           implicit lazy val spark: SparkSession = SparkSession.builder()
             .appName("daily-statistic")
             .getOrCreate()
        
           import spark.implicits._
        
           val workDir = args(0)
           val datePart = args(1)
           val saveDir = args(2)
        
           try {
        
              val date = read(s"$workDir/$datePart/*.csv.gz")
                 .select(
                    '_c0 as "device_id",
                    '_c1 as "lag_A0",
                    '_c2 as "lag_A1",
                    '_c3 as "flow_1",
                    '_c4 as "flow_2"
                 )
        
                 save(s"$saveDir/$datePart", agg(date))
        
           } finally spark.stop()
        
           // чтение исходных данных   
           def read(path: String)(implicit spark: SparkSession): DataFrame = {
        
              val inputFormat = Map("header" -> "false", "sep" -> ";", "compression" -> "gzip")
        
              spark.read
                 .options(inputFormat)
                 .csv(path)
           }
        
           // построение агрегата
           def agg(data: DataFrame):DataFrame = data
              .withColumn("device_id_empty", when('device_id.isNull, lit(1)).otherwise(0))
              .withColumn("lag_A0_empty", when('lag_A0.isNull, lit(1)).otherwise(0))
              .withColumn("lag_A1_empty", when('lag_A1.isNull, lit(1)).otherwise(0))
              .withColumn("flow_1_empty", when('flow_1.isNull, lit(1)).otherwise(0))
              .withColumn("flow_2_empty", when('flow_2.isNull, lit(1)).otherwise(0))
              .agg(
                 sum('device_id_empty) as "device_id_count_empty",
                 sum('lag_A0_empty) as "lag_A0_count_empty",
                 sum('lag_A1_empty) as "lag_A1_count_empty",
                 sum('flow_1_empty) as "flow_1_count_empty",
                 sum('flow_2_empty) as "flow_2_count_empty"
              )
        
           // сохранение результата
           def save(path: String, data: DataFrame): Unit = data.write.json(path)
        
        } 

        Собираем проект командой sbt assembly из директории daily-statistic. После успешного завершения сборки в директории daily-statistic/target/scala-2.11 появится пакет с задачей daily-statistic-0.1.jar.


        Написание workflow.xml


        Для запуска задачи через Oozie нужно описать конфигурацию запуска в файле workflow.xml. Ниже привожу пример для нашей задачи:


        
        
           
              
                 
                    oozie.launcher.mapred.job.queue.name
                    ${queue}
                 
              
           
        
           
        
           
              
                 ${jobTracker}
                 ${nameNode}
                 yarn-client
                 project-md2-daily-statistic
                 ru.daily.Statistic
                 ${nameNode}${jobDir}/lib/daily-statistic-0.1.jar
                 
                    --queue ${queue}
                    --master yarn-client
                    --num-executors 5
                    --conf spark.executor.cores=8
                    --conf spark.executor.memory=10g
                    --conf spark.executor.extraJavaOptions=-XX:+UseG1GC
                    --conf spark.yarn.jars=*.jar
                    --conf spark.yarn.queue=${queue}
                 
                 ${nameNode}${dataDir}
                 ${datePartition}
                 ${nameNode}${saveDir}
               
        
               
               
        
           
        
           
              Statistics job failed [${wf:errorMessage(wf:lastErrorNode())}]
           
        
           
        
        

        В блоке global устанавливается очередь, для MapReduce задачи которая будет находить нашу задачи и запускать её.
        В блоке action описывается действие, в нашем случае запуск spark задачи, и что нужно делать при завершении со статусом ОК или ERROR.
        В блоке spark определяется окружение, конфигурируется задача и передаются аргументы. Конфигурация запуска задачи описывается в блоке spark-opts. Параметры можно посмотреть в официальной документации
        Если задача завершается со статусом ERROR, то выполнение переходит в блок kill и выводится кратное сообщение об ошибки.
        Параметры в фигурных скобках, например ${queue}, мы будем определять при запуске.


        Написание coordinator.xml


        Для организации регулярного запуска нам потребуется ещё coordinator.xml. Ниже приведу пример для нашей задачи:


        
            
                
                    ${workflowPath}
                    
                        
                            datePartition
                            ${coord:formatTime(coord:dateOffset(coord:nominalTime(), -1, 'DAY'), "yyyy/MM/dd")}
                        
                    
                
            
        

        Здесь из интересного, параметры frequency, start, end, которые определяют частоту выполнения, дату и время начала выполнения задачи, дату и время окончания выполнения задачи соответственно.
        В блоке workflow указывается путь к директории с файлом workflow.xml, который мы определим позднее при запуске.
        В блоке configuration определяется значение свойства datePartition, которое в данном случае равно текущей дате в формате yyyy/MM/dd минус 1 день.


        Размещение проекта на hdfs

        Как уже было сказано ранее нашу задачу мы будем размещать в директории hdfs://hadoop/project-MD2/jobs:


        hdfs://hadoop/project-MD2/jobs/daily-statistic/lib/daily-statistic-0.1.jar
        hdfs://hadoop/project-MD2/jobs/daily-statistic/workflow.xml
        hdfs://hadoop/project-MD2/jobs/daily-statistic/coordinator.xml
        hdfs://hadoop/project-MD2/jobs/daily-statistic/sharelib

        Здесь в принципе всё понятно без комментариев за исключением директории sharelib. В эту директорию мы положим все библиотеки, которые использовались в процессе создания зашей задачи. В нашем случае это все библиотеки Spark 2.0.0, который мы указывали в зависимостях проекта. Зачем это нужно? Дело в том, что в зависимостях проекта мы указали "provided". Это говорит системе сборки не нужно включать зависимости в проект, они будут предоставлены окружением запуска, но мир не стоит на месте, администраторы кластера могут обновить версию Spark. Наша задача может оказаться чувствительной к этому обновлению, поэтому для запуска будет использоваться набор библиотек из директории sharelib. Как это конфигурируется покажу ниже.


        Запуск регулярного выполнения


        И так сё готово к волнительному моменту запуска. Мы будем запускать задачу через консоль. При запуске нужно задать значения свойствам, которые мы использовали в xml файлах. Вынесем эти свойства в отдельный файл coord.properties:


        # описание окружения
        nameNode=hdfs://hadoop
        jobTracker=hadoop.host.ru:8032
        
        # путь к директории с файлом coordinator.xml
        oozie.coord.application.path=/project-MD2/jobs/daily-statistic
        
        # частота в минутах (раз в 24 часа)
        frequency=1440
        startTime=2017-09-01T07:00Z
        endTime=2099-09-01T07:00Z
        
        # путь к директории с файлом workflow.xml
        workflowPath=/project-MD2/jobs/daily-statistic
        
        # имя пользователя, от которого будет запускаться задача
        mapreduce.job.user.name=username
        user.name=username
        
        # директория с данными и для сохранения результата
        dataDir=/project-MD2/data 
        saveDir=/project-MD2/status
        jobDir=/project-MD2/jobs/daily-statistic 
        
        # очередь для запуска задачи
        queue=PROJECTS
        
        # использовать библиотеке из указанной директории на hdfs вместо системных
        oozie.libpath=/project-MD2/jobs/daily-statistic/sharelib
        oozie.use.system.libpath=false

        Замечательно, тереть всё готово. Запускаем регулярное выполнение командой:


        oozie job -oozie http://hadoop.host.ru:11000/oozie -config coord.properties -run

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


        oozie job -info {job_id}

        Остановить задачу:


        oozie job -kill {job_id}

        Если Вы не знаете id задачи, то можно найти его, показав все регулярные задачи для вашего пользователя:


        oozie jobs -jobtype coordinator -filter user={user_name}

        Заключение


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

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

        https://habrahabr.ru/post/338952/


        Метки:  

        Опционалы в Swift

        Четверг, 28 Сентября 2017 г. 19:53 + в цитатник
        IFITOWS сегодня в 19:53 Разработка

        Опционалы в Swift

        • Tutorial

        Несмотря на некоторый опыт в мобильной разработке (в том числе с применением Swift), регулярно на почве свифтовых опционалов возникали ситуации, когда я знал что нужно делать, но не совсем внятно представлял, почему именно так. Приходилось отвлекаться и углубляться в документацию — количество "заметок на полях" пополнялось с удручающей периодичностью. В определенный момент они достигли критической массы, и я решил упорядочить их в едином исчерпывающем руководстве. Материал получился довольно объемным, поскольку предпринята попытка раскрыть тему максимально подробно. Статья будет полезна как начинающим Swift-разработчикам, так и матерым профессионалам из мира Objective-C — есть ненулевая вероятность, что и последние найдут для себя что-то новое. А если не найдут, то добавят свое новое в комментарии, и всем будет польза.


        Что такое Optionals?


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


        Зачем нужны Optionals, когда есть проверка на nil?


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


        Примечание

        NSNotFound не только нужно рассматривать как спецзначение, но и следить, чтобы оно не входило в множество допустимых значений переменной. Ситуация усложняется еще и тем, что NSNotFound считается равным NSIntegerMax, т.е. может иметь разные значения для разных (32-bit/64-bit) платформ. Это значит, что NSNotFound нельзя напрямую записывать в файлы и архивы или использовать в Distributed Objects.


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


        Во-вторых: явная опциональность проверяется на этапе компиляции, что снижает количество ошибок в runtime. Опциональную переменную в Swift нельзя использовать точно так же, как неопциональную (за исключением неявно извлекамых опционалов, подробности в разделе Implicit Unwrapping). Опционал нужно либо принудительно преобразовывать к обычному значению, либо использовать специальные преобразующие идиомы, такие как if let, guard let и ??. Опционалы в Swift реализуют не просто проверку, но целую парадигму опционального типа в теории типов.


        В-третьих, опционалы синтаксически более лаконичны, чем проверки на nil, что особенно хорошо видно на цепочках опциональных вызовов — так называемый Optional Chaining.


        Как это работает?


        Опционал в Swift представляет из себя особый объект-контейнер, который может содержать в себе либо nil, либо объект конкретного типа, который указывается при объявлении этого контейнера. Эти два состояния обозначаются терминами None и Some соответственно. Если при создании опциональной переменной присваемое значение не указывать, то nil присваивается по умолчанию.


        Примечание

        В документации значение по умолчанию в случае отсутсвия явного присвоения не упоминается, но сказано, что опционал represents either a wrapped value or nil, the absence of a value. Если опциональная переменная объявлена без явного присвоения (какое-либо Some не присваивалось), то логично следует, что неявно присваивается None — третьего "неинициализрованного" состояния у опционалов нет.


        Опционал объявляется посредством комбинации имени типа и лексемы ?. Таким образом, запись Int? — это объявление контейнера, экземпляр которого может содержать внутри nil (состояние None Int) либо значение типа Int (состояние Some Int). Именно поэтому при преобразовании Int? в Int используетя термин unwrapping вместо cast, т.е. подчеркивается "контейнерная" суть опционала. Лексема nil в Swift обозначает состояние None, которое можно присвоить любому опционалу. Это логично приводит к невозможности присвоить nil (состояние None) переменной, которая не является опционалом.


        По факту опционал представляет собой системное перечисление:


        public enum Optional : ExpressibleByNilLiteral {
        
            /// The absence of a value.
            ///
            /// In code, the absence of a value is typically written using the `nil`
            /// literal rather than the explicit `.none` enumeration case.
            case none
        
            /// The presence of a value, stored as `Wrapped`.
            case some(Wrapped)
        
            /// Creates an instance that stores the given value.
            public init(_ some: Wrapped)
        
                ...
        
            /// Creates an instance initialized with `nil`.
            ///
            /// Do not call this initializer directly. It is used by the compiler
            // when you initialize an `Optional` instance with a `nil` literal.
            public init(nilLiteral: ())
        
                ...
        
        }

        Перечисление Optional имеет два возможных состояния: .none и some(Wrapped). Запись Wrapped? обрабатывается препроцессором (Swift’s type system) и трансформируется в Optional, т.е. следующие записи эквивалентны:


        var my_variable: Int?

        var my_variable: Optional

        Лексема nil по факту обозначает Optional.none, т.е. следующие записи эквивалентны:


        var my_variable: Int? = nil

        var my_variable: Optional = Optional.none

        var my_variable = Optional.none

        Перечисление Optional имеет два конструктора. Первый конструктор init(_ some: Wrapped) принимает на вход значение соответсвующего типа, т.е. следующие записи эквивалентны:


        var my_variable = Optional(42) // тип .some-значения Int опеределен неявно

        var my_variable = Optional(42) // явное указание типа Int для наглядности

        var my_variable = Int?(42) // тип Int необходимо указать явно

        var my_variable: Int? = 42 // тип Int необходимо указать явно

        var my_variable = Optional.some(42) // тип Int опеределен неявно

        var my_variable = Optional.some(42) // явное указание типа для наглядности

        Второй конструктор init(nilLiteral: ()) является реализацией протокола ExpressibleByNilLiteral


        public protocol ExpressibleByNilLiteral {
        
            /// Creates an instance initialized with `nil`.
            public init(nilLiteral: ())
        }

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


        var my_variable1 = Optional(nil) // ошибка компиляции
        var my_variable2 = Optional.none(nil)// ошибка компиляции

        Вместо них следует использовать


        var my_variable: Int? = nil // или var my_variable: Int? = Optional.none

        или вообще не использовать явное присвоение


        var my_variable: Int?

        поскольку nil будет присвоен по умолчанию.


        Перечисление Optional также содержит свойство unsafelyUnwrapped, которое предоставляет доступ на чтение к .some-значению опционала:


        public enum Optional : ExpressibleByNilLiteral {
        
                ...
        
            /// The wrapped value of this instance, unwrapped without checking whether
            /// the instance is `nil`.
            public var unsafelyUnwrapped: Wrapped { get }
        }

        Если опционал находится в состоянии .none, обращение к unsafelyUnwrapped приведет к серьезному сбою программы.


        Подробнее

        В режиме отладки debug build -Onone будет ошибка рантайма:


        _fatal error: unsafelyUnwrapped of nil optional_

        В релизной сборке optimized build -O будет ошибка рантайма либо неопределенное поведение. Более безопасной операцией является Force Unwrapping (или Explicit Unwrapping) — принудительное извлечение .some-значения, обозначаемое лексемой !. Применение Force Unwrapping к опционалу в состоянии .none приведет к ошибке рантайма:


        _fatal error: unexpectedly found nil while unwrapping an Optional value_

        let my_variable1 = Int?(42) // содержит 42, тип Optional Int
        let my_value1A = my_variable1! // содержит 42, тип Int
        let my_value1B = my_variable1.unsafelyUnwrapped // содержит 42, тип Int
        
        let my_variable2 = Int?.none // содержит nil, тип Optional Int
        let my_value2A = my_variable2! // ошибка рантайма
        // ошибка рантайма в режиме -Onone, НЕОПРЕДЕЛЕННОЕ ПОВЕДЕНИЕ в режиме -O
        let my_value2B = my_variable2.unsafelyUnwrapped

        Идиомы использования


        Нет особого смысла использовать обычное перечисление с двумя состояниями. Вполне можно реализовать подобный механизм самостоятельно: создать enum c двумя состояниями и конструкторами для соответствующих значений, добавить какой-нибудь постфиксный оператор для Force Unwrapping (например, как это сделано здесь), добавить возможность сравнения с nil или вообще придумать "свой" nil и т.д. Опционалы должны быть интегрированы непосредственно в сам язык, чтобы их использование было естественным, не чужеродным. Разумеется, можно рассматривать такую интеграцию как "синтаксический сахар", однако языки высокого уровня для того и существуют, чтобы писать (и читать) код на них было легко и приятно. Использование опционалов в Swift подразумевает ряд идиом или особых языковых конструкций, которые помогают уменьшить количество ошибок и сделать код более лаконичным. К таким идиомам относятся Implicit Unwrapping, Optional Chaining, Nil-Coalescing и Optional Binding.


        Implicit unwrapping


        Безопасное использование Force Unwrapping подразумевает предварительную проверку на nil, например, в условии if:


        // getOptionalResult() может вернуть nil
        let my_variable: Int? = getOptionalResult() // тип Optional Int
        if my_variable != nil {
          // my_value получит .some-значение опционала из getOptionalResult()
          let my_value = my_variable!
        } else {
          // ошибка рантайма
          let my_value = my_variable!
        }

        Иногда из структуры программы очевидным образом следует, что переменная технически является опционалом, но к моменту первого использования всегда находится в состоянии .some, т.е. не является nil. Для использования опционала в неопциональном контексте (например, передать его в функцию с параметром неопционального типа) приходится постоянно применять Force Unwrapping с предварительной проверкой, что скучно и утомительно. В этих случаях можно применить неявно извлекаемый опционал — Implicitly Unwrapped Optional. Неявно извлекамый опционал объявляется посредством комбинации имени типа и лексемы !:


        let my_variable1: Int? = 42 // тип Optional Int
        let my_variable2: Int! = 42 // тип Implicitly Unwrapped Optional Int
        var my_variable3: Int! = 42 // тип Implicitly Unwrapped Optional Int
        
        ...
        
        my_variable3 = nil // где-то непредвиденно присвоен nil
        
        ...
        
        func sayHello(times:Int) {
            for _ in 0...times {
                print("Hello!")
            }
        }
        
        sayHello(times: my_variable1!) // обязаны извлекать значение явно
        sayHello(times: my_variable1) // ошибка компиляции
        sayHello(times: my_variable2!) // можем, но не обязаны извлекать значение явно
        sayHello(times: my_variable2) // неявное извлечение
        sayHello(times: my_variable3) // ошибка рантайма

        В вызове sayHello(times: my_variable2) извлечение значения 42 из my_variable2 все равно осуществляется, только неявно. Использование неявно извлекаемых опционалов делает код более удобным для чтения — нет восклицательных знаков, которые отвлекают внимание (вероятно, читающего код будет беспокоить использование Force Unwrapping без предварительной проверки). На практике это скорее анти-паттерн, увеличивающий вероятность ошибки. Неявно извлекаемый опционал заставляет компилятор "закрыть глаза" на то, что опционал используется в неопциональном контексте. Ошибка, которая может быть выявлена во время компиляции (вызов sayHello(times: my_variable1)), проявится только в рантайме (вызов sayHello(times: my_variable3)). Явный код всегда лучше неявного. Логично предположить, что такое снижение безопасности кода требуется не только ради устранения восклицательных знаков, и это действительно так.


        Неявно извлекаемые опционалы позволяют использовать self в конструкторе для иницализации свойств и при этом:


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

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


        class Country {
            let name: String
            var capitalCity: City!
            init(name: String, capitalName: String) {
                self.name = name
                self.capitalCity = City(name: capitalName, country: self)
            }
        }
        
        class City {
            let name: String
            unowned let country: Country
            init(name: String, country: Country) {
                self.name = name
                self.country = country
            }
        }
        
        var country = Country(name: "Canada", capitalName: "Ottawa")
        print("\(country.name)'s capital city is called \(country.capitalCity.name)")
        // Prints "Canada's capital city is called Ottawa"

        В этом примере экземпляры классов Country и City должны иметь ссылки друга на друга к моменту завершения инициализации. У каждой страны обязательно должна быть столица и у каждой столицы обязательно должна быть страна. Эти связи не являются опциональными — они безусловны. В процессе инициализации объекта country необходимо инициализировать свойство capitalCity. Для инициализации capitalCity нужно создать экземпляр класса City. Конструктор City в качестве параметра требует соответствующий экземпляр Country, т.е. требует доступ к self. Сложность в том, что экземпляр Country на этот момент еще не до конца инициализирован, т.е. self использовать нельзя.


        Эта задача имеет изящное решение: capitalCity объявляется мутабельным неявно извлекаемым опционалом. Как и любой мутабельный опционал, capitalCity по умолчанию инициализируется состоянием nil, т. е. к моменту вызова конструктора City все свойства объекта country уже инициализированы. Требования двухэтапной инициализации соблюдены, конструктор Country находится во второй фазе — можно передавать self в конструктор City. capitalCity является неявным опционалом, т.е. к нему можно обращаться в неопциональном контексте без добавления !.


        Побочным эффектом использования неявно извлекаемого опционала является "встроенный" assert: если capitalCity по каким-либо причинам останется в состоянии nil, это приведет к ошибке рантайма и аварийному завершению работы программы.


        Другим примером оправданного использования неявно извлекаемых опционалов могут послужить инструкции @IBOutlet: контекст их использования подразумевает, что переменной к моменту первого обращения автоматически будет присвоено .some-значение. Если это не так, то произойдет ошибка рантайма. Автоматическая генерация кода в Interface Builder создает свойства с @IBOutlet именно в виде неявных опционалов. Если такое поведение неприемлемо, свойство с @IBOutlet можно объявить в виде явного опционала и всегда обрабатывать .none-значения явным образом. Как правило, все же лучше сразу получить "падение", чем заниматься долгой отладкой в случае случайно отвязанного @IBOutlet-свойства.


        Optional Chaining


        Optional Chaining — это процесс последовательных вызовов по цепочке, где каждое из звеньев возвращает опционал. Процесс прерывается на первом опционале, находящемся в состоянии nil — в этом случае результатом всей цепочки вызовов также будет nil. Если все звенья цепочки находятся в состоянии .some, то результирующим значением будет опционал с результатом последнего вызова. Для формирования звеньев цепочки используется лексема ?, которая помещается сразу за вызовом, возвращающим опционал. Звеньями цепочки могут любые операции, которые возвращают опционал: обращение к локальной переменной (в качестве первого звена), вызовы свойств и методов, доступ по индексу.


        Optional сhaining всегда работает последовательно слева направо. Каждому следующему звену передается .some-значение предыдущего звена, при этом результирующее значение цепочки всегда является опционалом, т.е. цепочка работает по следующим правилам:


        • первое звено должно быть опционалом;
        • после лексемы ? должно быть следующее звено;
        • если звено в состояни .none, то цепочка прерывает процесс вызовов и возвращает nil;
        • если звено в состояни .some, то цепочка отдает .some-значение звена на вход следующему звену (если оно есть);
        • если результат последнего звена является опционалом, то цепочка возвращает этот опционал;
        • если результат последнего звена не является опционалом, то цепочка возвращает этот результат, "завернутый" в опционал (результат вычислений присваивается .some-значению возвращаемого опционала).

        // цепочка из трех звеньев: первое звено — опционал `country.mainSeaport?`,
        country.mainSeaport?.nearestVacantPier?.capacity
        
        // ошибка компиляции, после `?` должно быть следующее звено
        let mainSeaport = country.mainSeaport?
        
        // цепочка вернет `nil` на первом звене
        country = Country(name: "Mongolia")
        let capacity = country.mainSeaport?.mainPier?.capacity
        
        // цепочка вернет опционал — ближайший незанятый пирс в Хельсинки
        country = Country(name: "Finland")
        let nearestVacantPier = country.mainSeaport?.nearestVacantPier
        
        // цепочка вернет опционал — количество свободных мест, даже если capacity
        // является неопциональным значением
        country = Country(name: "Finland")
        let capacity = country.mainSeaport?.nearestVacantPier?.capacity

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


        let valueA = 42
        let optionalValueA = Optional(valueA)
        let doubleOptionalValueA = Optional(optionalValueA)
        let tripleOptionalValueA = Optional(doubleOptionalValueA)
        
        let tripleOptionalValueB: Int??? = 42 // три `?` означают тройную вложенность
        let doubleOptionalValueB = tripleOptionalValueB!
        let optionalValueB = doubleOptionalValueB!
        let valueB = optionalValueB!
        
        print("\(valueA)") // 42
        print("\(optionalValueA)") // Optional(42)
        print("\(doubleOptionalValueA)") // Optional(Optional(42))
        print("\(tripleOptionalValueA)") // Optional(Optional(Optional(42)))
        
        print("\(tripleOptionalValueB)") // Optional(Optional(Optional(42)))
        print("\(doubleOptionalValueB)") // Optional(Optional(42))
        print("\(optionalValueB)") // Optional(42)
        print("\(valueB)") // 42

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


        let optionalAppDelegate = UIApplication.shared.delegate
        let doubleOptionalWindow = UIApplication.shared.delegate?.window
        let optionalFrame = UIApplication.shared.delegate?.window??.frame // два '?'
        print("\(optionalAppDelegate)") // Optional( ... )
        print("\(doubleOptionalWindow)") // Optional(Optional( ... ))
        print("\(optionalFrame)") // Optional( ... )

        Примечание

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


        Цепочка UIApplication.shared.delegate?.window??.frame фактически состоит из четырех звеньев: UIApplication.shared.delegate?, .frame и два звена, объединенных в одном вызове .window??. Второе "двойное" звено представлено опционалом второго уровня вложенности.


        Важной особенностью этого примера также является особый способ формирования двойного опционала, отличающийся от способа формирования doubleOptionalValue в предыдущем примере. UIApplication.shared.delegate!.window является опциональным свойством, в котором возвращается опционал. Опциональность свойства означает, что может отсутствовать само свойство, а не только .some-значение у опционала, возвращаемого из свойства. Опциональное свойство, также как и все прочие свойства, может возвращать любой тип, не только опциональный. Опциональность такого рода формируется в @objc-протоколах с помощью модификатора optional:


        public protocol UIApplicationDelegate : NSObjectProtocol {
        
        ...
        
        @available(iOS 5.0, * )
        optional public var window: UIWindow? { get set } // модификатор optional
        
        ...
        
        }

        В протоколах с опциональными свойствами и методами (иначе, опциональными требованиями) модификатор @objc указывается для каждого опционального требования и для самого протокола. На протокол UIApplicationDelegate из примера выше это требование не распространяется, т.к. он транслируется в Swift из системной библиотеки на Objective-C. Вызов нереализованного опционального требования у объекта, принимающего такой протокол, возвращает опционал соответствующего типа в состоянии .none. Вызов реализованного опционального требования возвращает опционал соответсвующего типа в состоянии .some. Таким образом, опциональные свойства и методы, в отличие от optional сhaining, увеличивают уровень вложенности возвращаемого опционала. Опциональный метод, как и свойство, "заворачивается" в опционал полностью — в .some-значение помещается метод целиком, а не только возращаемое значение:


        @objc
        public protocol myOptionalProtocol {
        
            @objc
            optional var my_variable: Int { get }
        
            @objc
            optional var my_optionalVariableA: Int? { get } // ошибка компиляции:
            // модификатор @objc не применим к опционалу типа Int?, т.к. Int
            // не является классом
        
            @objc
            optional var my_optionalVariableB: UIView? { get }
        
            @objc
            optional func my_func() -> Int
        
            @objc
            optional func my_optionalResultfuncA() -> Int? // ошибка компиляции:
            // модификатор @objc не применим к опционалу типа Int?, т.к. Int
            // не является классом
        
            @objc
            optional func my_optionalResultfuncB() -> UIView?
        
            @objc
            optional init(value: Int) // ошибка компиляции:
            // модификатор optional не применим инициализаторам
        
        }
        
        @UIApplicationMain
        class AppDelegate: UIResponder, UIApplicationDelegate, myOptionalProtocol {
        
            var window: UIWindow?
        
            func application(_ application: UIApplication,
              didFinishLaunchingWithOptions launchOptions:
              [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
        
                let protocolAdoption = self as myOptionalProtocol
        
                // Optional
                print("\(type(of: protocolAdoption.my_variable))")
        
                // Optional>
                print("\(type(of: protocolAdoption.my_optionalVariableB))")
        
                // Optional<() -> Int>
                print("\(type(of: protocolAdoption.my_func))")
        
                // Optional
                print("\(type(of: protocolAdoption.my_func?()))")
        
                // Optional<() -> Optional>
                print("\(type(of: protocolAdoption.my_optionalResultfuncB))")
        
                // Optional
                print("\(type(of: protocolAdoption.my_optionalResultfuncB?()))")
        
                return true
            }
        }

        Для опциональных @objc-протоколов имеется ряд ограничений, в виду того, что они были введены в Swift специально для взаимодействия с кодом на Objective-C:


        • могут быть реализованы только в классах, унаследованных от классов Objective-C, либо других классов с аттрибутом @objc (т.е. не могут быть реализованы в структурах и перечислениях);
        • модификатор optional неприменим к требованиям-конструкторам init;
        • cвойства и методы с аттрибутом @objc имеют ограничение на тип возвращаемого опционала — допускаются только классы.

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


        Nil-Coalescing


        Оператор Nil-Coalescing возвращает .some-значение опционала, если опционал в состоянии .some, и значение по умолчанию, если опционал в состоянии .none. Обычно Nil-Coalescing более лаконичен, чем условие if else, и легче воспринимается, чем тернарный условный оператор ?:


        let optionalText: String? = tryExtractText()
        
        // многословно
        let textA: String
        if optionalText != nil {
            textA = optionalText!
        } else {
            textA = "Extraction Error!"
        }
        
        // в одну строку, но заставляет думать
        let textB = (optionalText != nil) ? optionalText! : "Extraction Error!"
        
        // кратко и легко воспринимать
        let textC = optionalText ?? "Extraction Error!"

        Тип значения по умолчания справа должен соответствовать типу .some-значения опционала слева. Значение по умолчанию тоже может быть опционалом:


        let optionalText: String?? = tryExtractOptionalText()
        let a = optionalText ?? Optional("Extraction Error!")

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


        let wayA: Int? = doSomething()
        let wayB: Int? = doNothing()
        let defaultWay: Int = ignoreEverything()
        
        let whatDo = wayA ?? wayB ?? defaultWay

        Optional Binding и приведение типов


        Optional Binding позволяет проверить, содержит ли опционал .some-значение, и если содержит, извлечь его и предоставить к нему доступ через с помощью локальной переменной (обычно константной). Optional Binding работает в контексте конструкций if, while и guard.


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


        В Swift каждый метод или функция без явно заданного return неявно возвращает пустой кортеж (). Оператор присваивания = является исключением и не возвращает значение, тем самым позволяя избежать случайного присвоения вместо сравнения ==.


        Допустим, что оператор присваивания также обычно возвращает пустой кортеж, но если правый операнд является опционалом в состоянии nil, то оператор присваивания вернет nil. Тогда эту особенность можно будет использовать в условии if, так как пустой кортеж расценивается как true, а nil расценивается как false:


        var my_optionalVariable: Int? = 42
        
        // условие истинно, my_variable "привязана" к .some-значению my_optionalVariable
        if let my_variable = my_optionalVariable {
            print("\(my_variable)") // 42
        }
        
        my_optionalVariable = nil
        
        // условие ложно, my_variable не создана
        if let my_variable = my_optionalVariable {
            print("\(my_variable)")
        } else {
            print("Optional variable is nil!") // Optional variable is nil!
        }

        Переменную, сформированную в контексте ветки true, можно будет автоматически объявить неявно извлекаемым опционалом или даже обычной переменной. Не-nil как результат операции присвоения будет являться следствием .some-состояния правого операнда. В ветке true .some-значение всегда будет успешно извлечено и "привязано" к новой переменной (поэтому механизм в целом и называется Optional Binding).


        Область видимости извлеченной переменной в условии if ограничивается веткой true, что логично, поскольку в ветке false такая переменная не может быть извлечена. Тем не менее, существуют ситуации, когда нужно расширить область видимости извлеченного .some-значения, а в ветке false (опционал в состоянии .none) завершить работу функции. В таких ситуациях удобно воспользоваться условием guard:


        let my_optionalVariable: Int? = extractOptionalValue()
        
        // чересчур многословно
        let my_variableA: Int
        if let value = my_optionalVariable {
          my_variableA = value
        } else {
          return
        }
        print(my_variableA + 1)
        
        // лаконично
        guard let my_variableB = my_optionalVariable else {
          return
        }
        print(my_variableB + 1)

        В Swift гарантированное компилятором приведение типов (например, повышающее приведение или указание типа литерала) выполняется с помощью оператора as. В случаях, когда компилятор не может гарантировать успешное приведение типа (например, понижающее приведение), используется либо оператор принудительного приведения as!, либо оператор опционального приведения as?. Принудительное приведение работает в стиле Force Unwrapping, т.е. в случае невозможности выполнить приведение приведет к ошибке рантайма, в то время как опциональное приведение в этом случае вернет nil:


        class Shape {}
        class Circle: Shape {}
        class Triangle: Shape {}
        
        let circle = Circle()
        let circleShape: Shape = Circle()
        let triangleShape: Shape = Triangle()
        
        circle as Shape // гарантированное приведение
        42 as Float // гарантированное приведение
        circleShape as Circle // ошибка компиляции
        
        circleShape as! Circle // circle
        triangleShape as! Circle // ошибка рантайма
        
        circleShape as? Circle // Optional
        triangleShape as? Circle // nil

        Таким образом, оператор опционального приведения as? порождает опционал, который часто используется в связке с Optional Binding:


        class Shape {}
        class Circle: Shape {}
        class Triangle: Shape {}
        
        let circleShape: Shape = Circle()
        let triangleShape: Shape = Triangle()
        
        // условие истинно, успешное приведение
        if let circle = circleShape as? Circle {
            print("Cast success: \(type(of: circle))") // Cast success: (Circle #1)
        } else {
            print("Cast failure")
        }
        
        // условие ложно, приведение не удалось
        if let circle = triangleShape as? Circle {
            print("Cast success: \(type(of: circle))")
        } else {
            print("Cast failure") // Cast failure
        }

        map и flatMap


        Методы map и flatMap условно можно отнести к идиомам Swift, потому что они определены в системном перечислении Optional:


        public enum Optional : ExpressibleByNilLiteral {
        
            ...
        
            /// Evaluates the given closure when this `Optional` instance is not `nil`,
            /// passing the unwrapped value as a parameter.
            ///
            /// Use the `map` method with a closure that returns a nonoptional value.
            public func map(_ transform: (Wrapped) throws -> U) rethrows -> U?
        
            /// Evaluates the given closure when this `Optional` instance is not `nil`,
            /// passing the unwrapped value as a parameter.
            ///
            /// Use the `flatMap` method with a closure that returns an optional value.
            public func flatMap(_ transform: (Wrapped) throws -> U?) rethrows -> U?
        
                ...
        
        }

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


        let my_variable: Int? = 4
        
        let my_squareVariable = my_variable.map { v in
            return v * v
        }
        
        print("\(my_squareVariable)") // Optional(16)
        
        let my_reciprocalVariable: Double? = my_variable.flatMap { v in
            if v == 0 {
                return nil
            }
            return 1.0 / Double(v)
        }
        
        print("\(my_reciprocalVariable)") // Optional(0.25)

        Выражения с map и flatmap обычно более лаконичны, чем конструкции с предварительной проверкой в if или guard, и воспринимаются легче, чем конструкции с тернарным условным оператором:


        let dateFormatter = DateFormatter()
        dateFormatter.dateFormat = "dd MMM yyyy"
        let date: Date? = extractOptionalDate()
        
        // многословно
        let dateStringA: String
        if date != nil {
            dateStringA = dateFormatter.string(from: date!)
        } else {
            dateStringA = "Unknown date"
        }
        
        // в одну строку, но сложно воспринимать
        let dateStringB =
        (date == nil ? nil : dateFormatter.string(from: date!)) ?? "Unknown date"
        
        // лаконично, с непривычки сложно воспринимать (если map используется редко)
        let dateStringC = date.map(dateFormatter.string) ?? "Unknown date"

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


        // отдельно Optional Binding и явный вызов метода у извлеченного объекта
        func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
          if let cell = sender as? UITableViewCell,
            let indexPath = tableView.indexPathForCell(cell) {
              let item = items[indexPath.row]
          }
        }
        
        // В одном вызове 3 этапа:
        // 1) опционал как результат приведения типов;
        // 2) передача неявного замыкания в метод flatMap полученного опционала;
        // 3) Optional Binding к результату flatMap.
        func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
          if let indexPath =
            (sender as? UITableViewCell).flatMap(tableView.indexPathForCell) {
              let item = items[indexPath.row]
          }
        }

        Опционалы и обработка исключений


        Swift поддерживает несколько способов обработки исключений, и один из них — это преобразование исключения в nil. Такое преобразование можно осуществить автоматически с помощью оператора try? (примеры из документации):


        func someThrowingFunction() throws -> Int {
            // ...
        }
        
        // многословно
        let y: Int?
        do {
            y = try someThrowingFunction()
        } catch {
            y = nil
        }
        
        // лаконично
        let x = try? someThrowingFunction()

        Обе переменные x и y являются опциональными, независимо от того, какой тип возвращает someThrowingFunction(). Таким образом, семантика поведения оператора try? такая же, как и у оператора as?. Логично также наличие оператора try!, который позволяет проигнорировать возможность выброса исключения из функции. Если исключение все же будет выброшено, то произойдет ошибка рантайма:


        // ошибка рантайма, если loadImage будет выброшено исключение
        // photo не является опционалом (в отличие от x и y)
        let photo = try! loadImage(atPath: "./Resources/John Appleseed.jpg")

        Опционалы и Objective-C


        В Objective-C нет понятия опциональности. Лексема nil в Objective-C обозначает нулевой указатель, т.е. обращение к любой переменной ссылочного типа потенциально может вернуть nil. В Swift nil обозначает опционал в состоянии .none, неопциональная переменная не может быть nil, поэтому до Xcode 6.3 любой указатель из Objective-C транслировался в Swift как неявно извлекаемый опционал. В Xcode 6.3 в Objective-C для совместимости с семантикой опционалов были введены так называемые nullability annotations:


        @interface myObject : NSObject
        
        @property (copy, readonly) NSArray * _Nullable myValuesA;
        @property (copy, readonly) NSString * _Nonnull myStringA;
        
        @property (copy, readonly, nullable) NSArray * myValuesB;
        @property (copy, readonly, nonnull) NSString * myStringB;
        
        @end

        К ним относятся nullable (или _Nullable), nonnull (или _Nonnull), а также null_unspecified и null_resettable. Nullability-aннотациями могут быть обозначены ссылочные типы в свойствах, а также в параметрах и результатах функций. Помимо отдельных аннотаций можно использовать специальные макросы NS_ASSUME_NONNULL_BEGIN и NS_ASSUME_NONNULL_END для пометки участков кода целиком. Аннотации не являются полноценными модификаторами указателей или аттрибутами свойств, поскольку не влияют на компиляцию кода на Objective-C (если не считать предупреждений компиляции, например, при попытке присвоить nil свойству с аннотацией nonnull).


        Примечание

        Аннотация null_resettable подразумевает, что сеттер свойства может принимать nil, но при этом геттер свойства вместо nil возвращает некоторое значение по умолчанию.


        Трансляция из Objective-C в Swift осуществлятся по следующим правилам:


        • значения из области между NS_ASSUME_NONNULL_BEGIN и NS_ASSUME_NONNULL_END импортируются в виде неопциональных (обычных) значений;
        • значения с аннотациями nonnull или _Nonnull импортируются в виде неопциональных (обычных) значений;
        • значения с аннотациями nullable или _Nullable импортируются в виде опционалов;
        • значения с аннотацией null_resettable импортируются в виде неявно извлекаемых опционалов;
        • значения без nullability-aннотаций или аннотацией null_unspecified (аннотация по умолчанию) импортируются в виде неявно извлекаемых опционалов.

        Правила передачи опционалов из Swift в Objective-C несколько проще:


        • если опционал в состоянии .none, то возвращается экземпляр NSNull;
        • если опционал в состоянии .some, то возвращается указатель на .some-значение.

        Резюме, синтаксис


        Лексема ! может быть использована в четырех контекстах, связанных с опциональностью:


        • для принудительного извлечения значения из опционала;
        • для объявления неявного опционала;
        • для принудительной конвертации типов в операторе as!;
        • для принудительного подавления исключения в операторе try!.

        Примечание

        Унарный оператор логического отрицания ! не считается, поскольку относится к другому контексту.


        Лексема ? может быть использована в четырех контекстах, связанных с опциональностью:


        • для объявления явного опционала;
        • для использования опционала в optional chaining;
        • для опциональной конвертации типов в операторе as?;
        • для преобразования исключения в nil в операторе try?.

        Примечание

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


        Лексема ?? может быть использована в двух контекстах:


        • в Optional сhaining для обращения к опционалу второго уровня вложенности;
        • в качестве оператора Nil-Coalescing.

        Заключение


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


        Факт отсутствия данных должен обрабатываться отдельной сущностью, внешней по отношению к самим данным. В С++ или Java в область допустимых значений указателя включено специальный "адрес", обозначающий отсутствие адресата. "Правильный" указатель не может существовать без адресата, следовательно, не может "осознать" отсутствие адресата. Даже человеку, т.е. довольно сложной системе, приходится довольстоваться аксиомой Cogito, ergo sum (лат. — "Мыслю, следовательно существую"). У человека нет достоверных признаков собственного бытия или небытия, но у внешних по отношению к человеку сущностей эти критерии есть. В Swift такой внешней сущностью является опционал.


        Дополнительные материалы


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

        https://habrahabr.ru/post/338766/


        Метки:  

        [Из песочницы] Micromaster's degree как новая форма образования. Краткий обзор первого пилота программы

        Четверг, 28 Сентября 2017 г. 18:52 + в цитатник
        reedrefucher сегодня в 18:52 Разное

        Micromaster's degree как новая форма образования. Краткий обзор первого пилота программы

        Micromaster's degree как новая форма образования. Краткий обзор первого пилота программы.


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


        Массачусетский технологический институт

        Отступительное слово


        Онлайн образование ни для кого уже не новость. Само направление существует на рынке услуг около 20 лет, но на этап экспоненциального роста было выведено относительно недавно, а точнее в 2011 году, после запуска курса в так называемом формате MOOC – massive open online courses (массовые открытые онлайн-курсы) Стэндфордским университетом, на который записалось около 160 тысяч человек. С тех пор возникло несколько больших агрегаторов, на которых сконцентрированы курсы от различных образовательных организаций, позволяющие освоить тот или иной материал. Среди них можно выделить две полярные группы: с более академическим уклоном (coursera, edx), или значительной долей неформальных направлений (udacity, udemy). При этом платформы с академическим уклоном, являющие собой следующий виток эволюции дистанционного обучения, продолжительное время работали по модели бесплатного образования с платными сертификатами, а на edx до 2016 года даже был фейл с тем, что на ряду с платными сертификатами выдавали бесплатные, которые, за редкими исключениями, не уступали в полезности платным, (и те и те, по большому счету, не давали никаких преимуществ своим обладателям). В желании монетизировать такие курсы, платным сертификатам начали присваивать новую ценность, сначала в виде дополнительного контента, далее в виде возможности закрыть несколько кредитов будучи студентом кампуса пройдя онлайн-курс. Идея с зачислением результатов прохождения MOOC по дисциплине вместо традиционного офлайн обучения в итоге выросла в создание первых серий курсов, которые могут официально заменить целый семестр обучения на магистратуре и по завершению дают право продолжить образование в кампусе и получить классический диплом. Пионером выступил MIT с циклом курсов Supply Chain Management, развернутом на платформе edx. Они же и ввели понятие Micromaster’s degree, о прелестях которого пойдёт речь дальше.

        Что такое Micromaster’s degree?


        Это серия из онлайн-курсов связанных единой сферой применения, которые направлены на изучение определённого объема материала, имеют сравнительно высокое признание в отрасли, результаты которого эквивалентны 25-50% кредитам необходимым для получения степени магистра и дают право продолжить обучение в кампусе в рамках образовательной программы, частью которой они являются. Так, программа Supply Chain Management состоит из 5 MOOC, длительностью 10 недель каждый и финального экзамена. На прохождение одной недели тратиться от 4 до 12 часов, и еще 4 часа отводиться на финальный экзамен. Таким образом прохождение данной программы в спокойном темпе длиться около года, из которых на занятия суммарно тратиться около 400 часов времени.

        Что даёт Micromaster’s degree?


        Выгоды данной программы рационально рассматривать на всё том же примере Supply Chain Management Micromaster’s degree, первый в мире выпуск которой состоялся в августе 2017 года. Всего на все курсы цикла зарегистрировалось более 180 000 человек, однако до конца дошли далеко не все – сертификаты micromasters получили только 622 человек (из 1100 сдававших финальный экзамен). Данные сертификаты действуют 3 года с момента получения и с точки зрения академического потенциала позволяют своим обладателям следующее:

        1) засчитать часть кредитов по таким программам, как:

        2) подать свою кандидатуру на продолжение обучения в кампусе, с зачислением результатов micromasters как одного семестра обучения и фактическим стартом во втором семестре по таким программам как:

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

        1. Упрощенная схема подачи документов на поступление, исключающая необходимость предоставления результатов GRE/GMAT;
        2. Значительное сокращение времени обучения в кампусе (на 50%), и самое главное – огромная разница в цене в пользу студента.

        Так, полная стоимость аналогичной программы в кампусе MIT для 2017-2018 года официально оценивается в 102 012 долларов США, тогда как программа на базе онлайн курсов обойдется студенту в 58 059 долларов США. Менее выгодно данное сравнение выглядит для варианта с обучением в испанском филиале MIT Global Scale Network – Сарагоском Логистическом Центре, стоимость полной программы в кампусе для которого неофициально оценивается в 37 000 евро, тогда как программа на базе онлайн курсов обойдется студенту в 25 000 евро. Однако стоит отметить, что в случае с обучением в Сарагосе, первых три недели студент всё же проведет в США, поэтому второй семестр данной программы выходит значительно дороже, чем первый и в данном случае достигается не такой высокий процент экономии, как в случае с обучением в MIT. Стоимость онлайн-курсов оплачивается отдельно по мере их прохождения, и составляет 150 долларов за каждый курс, плюс 600 долларов за экзамен, то есть 1350 долларов в сумме. Предполагается, что если студент работает в отрасли и может доказать применимость результатов в деятельности, то его курсы может оплатить работодатель. Практика показывает, что так и происходит.


        PLaza logistics park – место дислокации Zaragoza Logistics Center

        Почему это так важно?


        В то время как стоимость гибридных программ обучения на базе MOOCs остается неприемлемо большой для многих потенциальных студентов, Micromaster’s degree является значительным шагом вперед в образовании, с точки зрения его доступности и качества. Необходимость зачисления результатов прохождения онлайн-курсов наравне с программой в кампусе требует от их создателей максимальной взаимозаменяемости, а значит уравнивает их образовательную ценность. В то же время такие курсы постепенно получат признание от большего количества работодателей, а значит могут стать весомым конкурентным преимуществом на рынке труда. На сегодняшний день в рамках платформы edx доступно более 30 программ «микромастера», в сферах IT, менеджмента, робототехники, бизнеса, биоинформатики и прочих. Выпускников, имеющих сертификат micromaster, готовы принять к себе на обучение в рамках гибридных программ такие университеты как: Массачусетский Технологический Институт, Беркли, Рочестерский Институт Технологий, Колумбийский Университет, Гонконгский политехнический институт, Лёвенский Католический Университет (Бельгия), Дельфтский Технологический Университет (Нидерланды), и многие другие.


        Платформа edx

        Итог


        Первый набор на гибридные программы по управлению цепями поставок в Массачусетском Технологическом Институте и Сарагосском Логистическом Центре уже завершен, и около 40 человек получивших свой Micromaster’s degree продолжат обучение в кампусах уже с 4 января 2018 года. Окончательное мнение о подобных программах удастся сложить уже в ближайшие 2-5 лет, когда их выпускники закончат обучение и какое-то время поработают в отрасли. В ближайшие годы также стоит ожидать рост доступности гибридных программ, с появлением возможностей частично или полностью компенсировать стоимость такого обучения посредством стипендий и грантов.
        Original source: habrahabr.ru (comments, light).

        https://habrahabr.ru/post/338942/


        Метки:  

        V8 под капотом

        Четверг, 28 Сентября 2017 г. 17:59 + в цитатник
        MaxJoint сегодня в 17:59 Разработка

        V8 под капотом

          Ведущий разработчик из «Яндекс.Денег» Андрей Мелихов (также редактор/переводчик сообщества devSchacht) на примере движка V8 рассказывает о том, как и через какие стадии проходит программа, прежде чем превращается в машинный код, и зачем на самом деле нужен новый компилятор.



          Материал подготовлен на основе доклада автора на конференции HolyJS 2017, которая проходила в Санкт-Петербурге 2-3 июня. Презентацию в pdf можно найти по этой ссылке.



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

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

          Интерпретируемый или компилируемый у нас язык?


          Я надеюсь, все знают отличие, но повторю. Компилируемые языки: в них исходный код преобразуется компилятором в машинный код и записывается в файл. В них используется компиляция до выполнения. В чем преимущество? Его не нужно повторно компилировать, он максимально автоматизирован для той системы, под которую скомпилирован. В чем недостаток? Если у вас меняется операционная система, и если у вас нет исходников, вы теряете программу.
          Интерпретируемые языки – когда исходный код исполняется программой-интерпретатором. Преимущества в том, что легко достичь кроссплатформенности. Мы поставляем наш исходный код как есть, и если в этой системе есть интерпретатор, то код будет работать. Язык JavaScript, конечно, интерпретируемый.

          Погрузимся в историю. В 2008 выходит браузер Chrome. В том году Google презентовал новый движок V8. В 2009 году на том же самом движке была представлена Node.js, которая состояла из V8 и библиотеки libUV, которая обеспечивает io, т.е. обращение к файлам, сетевые какие-то вещи и т.д. В общем, две очень важные вещи для нас построены на движке V8. Посмотрим, из чего он состоит.



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

          Для чего в этой схеме нужен парсер?


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



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



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



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

          Зачем в JavaScript компилятор?


          Как я сказал, язык у нас интерпретируемый, но в его схеме мы видим компилятор. Зачем он? На самом деле есть два типа компиляторов. Есть компиляторы (ahead-of-time), которые компилируют до выполнения, и компиляторы JIT, которые компилируют во время выполнения. И за счет JIT-компиляции получается хорошее ускорение. Для чего это нужно? Давайте сравним.



          Есть один и тот же код. Один на Pascal, другой на JavaScript. Pascal — прекрасный язык. Я считаю, что с него и надо учиться программировать, но не с JavaScript. Если у вас есть человек, который хочет научиться программировать, то покажите ему Pascal или C.

          В чем отличие? Pascal может быть и компилируемым и интерпретируемым, а JavaScript требует уже интерпретации. Самое важное отличие – это статическая типизация.



          Потому что, когда мы пишем на Pascal, мы указываем переменные, которые необходимы, а потом пишем их типы. Потом компилятору легко построить хороший оптимизированный код. Как мы обращаемся к переменным в память? У нас есть адрес, и у нас есть сдвиг. Например, Integer 32, то мы делаем сдвиг на 32 по этому адресу в память и получаем данные.

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

          Ведь у нас JavaScript, у него прототипная модель, и классов для объектов у нас нет. На самом деле есть, но они не видны. Это так называемые Hidden Classes. Они видны только самому компилятору.

          Как создаются Hidden Classes?




          У нас есть point – это конструктор, и создаются объекты. Сначала создается hidden class, который содержит только сам point.



          Дальше у нас устанавливается свойство этого объекта x и из того, что у нас был hidden class, создаётся следующий hidden class, который содержит x.



          Дальше у нас устанавливается y и, соответственно, мы получаем еще один hidden class, который содержит x и y.



          Так мы получили три hidden class. После этого, когда мы создаем второй объект, используя тот же самый конструктор, происходит то же самое. Уже есть hidden classes, их уже не нужно создавать, с ними необходимо только сопоставить. Для того, чтобы позже мы знали, что эти два объекта одинаковы по структуре. И с ними можно похоже работать.



          Но что происходит, когда мы позже еще добавляем свойство в объект p2? Создается новый hidden class, т.е. p1 и p2 уже не похожи. Почему это важно? Потому что, когда компилятор будет перебирать в цикле point, и вот у него будут все такие же, как p1, он их крутит, крутит, крутит, натыкается на p2, а у него другой hidden class, и компилятор уходит в деоптимизацию, потому что он получил не то, что ожидал.



          Это так называемая утиная типизация. Что такое утиная типизация? Выражение появилось из американского сленга, если что-то ходит как утка, крякает как утка, то это утка. Т.е. если у нас p1 и p2 по структуре одинаковы, то они принадлежат к одному классу. Но стоит нам добавить в структуру p2 еще, и эти утки крякают по-разному, соответственно, это разные классы.

          И вот мы получили данные о том, к каким классам относятся объекты, и получили данные о том, какого рода переменные, где эти данные использовать и как их хранить. Для этого используется система Inline Caches.



          Рассмотрим, как происходит создание Inline Caches для этой части. Сначала, когда у нас код анализируется, он дополняется такими вызовами. Это только инициализация. Мы еще не знаем, какого типа будет у нас наш Inline Caches.

          Мы можем сказать, что вот в этом месте проиницилизируй его, вот здесь загрузка this.primes:



          Вот здесь загрузка по ключу:



          А дальше операция BinaryOperation — это не значит, что она двоичная, это значит что она бинарная, а не унарная операция. Операция, у которой есть левая и правая части.



          Что происходит во время выполнения?


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



          Вот здесь подменяется на код, который знает, как получить элемент из массива SMI:



          Здесь на код, который знает как посчитать остаток от деления двух SMI:



          Он уже будет оптимизирован. И вот так примерно работал компилятор, насыщая такими кусочками.


          Это, конечно, дает некоторый overhead, но и дает производительность.

          У нас интернет развивался, увеличивалось количество JavaScript, требовалось большая производительность, и компания Google ответила созданием нового компилятора Crankshaft.



          Старый компилятор стал называться FullCodegen, потому что он работает с полной кодовой базой, он знает весь JavaScript, как его компилировать. И он производит неоптимизированный код. Если он натыкается на какую-то функцию, которая вызывается несколько раз, он считает, что она стала горячей, и он знает, что компилятор Crankshaft может ее оптимизировать. И он отдает знания о типах и о том, что эту функцию можно оптимизировать в новый компилятор Crankshaft. Дальше новый компилятор заново получает абстрактное синтаксическое дерево. Это важно, что он получает не от старого компилятора AST, а снова идет и запрашивает AST. И зная о типах, делает оптимизацию, и на выходе мы получаем оптимизированный код.

          Если он не может сделать оптимизацию, он сваливается в деоптимизацию. Когда это происходит? Вот как я сказал раньше, например, у нас в цикле Hidden Class крутится, потом неожиданное что-то и мы вывалились в деоптимизацию. Или, например, многие любят делать проверку, когда у нас есть что-то в левой части, и мы берем, например, длину, т.е. мы проверяем, есть ли у нас строка, и берем ее длину. Чем это плохо? Потому, что когда у нас строки нет, то в левой части у нас получается Boolean и на выходе получается Boolean, а до этого шел Number. И вот в этом случае мы сваливаемся в деоптимизацию. Или он встретил код, не может его оптимизировать.



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



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



          У нас есть исходный код, который попадает в парсер, далее в компилятор FullCodegen. Так было до этого, никаких отличий. На выходе мы получаем неоптимизированный код. Если мы можем сделать какую-либо оптимизацию, то мы уходим в два компилятора, Crankshaft и Turbofan. FullCodegen сам решает, может ли оптимизировать конкретные вещи компилятор Turbofan, и если может, то отправляет в него, а если не может, то отправляет в старый компилятор. Туда постепенно стали добавлять новые конструкции из ES6. Начали с того, что заоптимизировали asm.js в него.

          Зачем нужен новый компилятор?



          1. Улучшить базовую производительность
          2. Сделать производительность предсказуемой
          3. Уменьшить сложность исходного кода

          Что значит «улучшить базовую производительность»?


          Старый компилятор был написан в те годы, когда у нас стояли мощные десктопы. И его тестировали на таких тестах, как octane, синтетических, которые проверяли пиковую производительность. Недавно была конференция Google I/O, и там менеджер, управляющий разработкой V8, завил, что они отказались в принципе от octane, потому что он не соответствует тому, с чем на самом деле работает компилятор. И это привело к тому, что у нас была очень хорошая пиковая производительность, но очень просела базовая, т.е. были не заоптимизированы вещи в коде, и когда код, хорошо работающий, натыкался на такие вещи, то шло значительное падение производительности. И таких операций скопилось много, вот несколько из них: forEach, map, reduce. Они написаны на обычном JS, нашпигованы проверками, сильно медленней, чем for. Часто советовали использовать for.

          Медленная операция bind – она реализована внутри, оказывается, совершенно ужасно. Многие фреймворки писали свои реализации bind. Часто люди говорили, что я сел, написал на коленке bind и он работает быстрее, удивительно. Функции, содержащие try{}catch(e){}(и finally), – очень медленные.



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



          Поэтому все ждут релиза новой node’ы, которая недавно вышла, там важна как раз производительность с async/await’ами. У нас язык асинхронный изначально, а пользоваться хорошо мы могли только callback’ами. И кто пишет с promise, знают, что их сторонние реализации работают быстрее, чем нативная реализация.

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



          Например, у нас есть такой довольно простой код, который вызывает mymax, и если мы проверим его (при помощи ключей trace-opt и trace-deopt – показывают, какие функции были оптимизированы, а какие нет).



          Мы можем запустить это с node, а можем и с D8 – специальной средой, где V8 работает отдельно от браузера. Она нам показывает, что оптимизации были отключены. Потому что слишком много раз запускался на проверку. В чем проблема? Оказывается, псевдомассив arguments — слишком большой, и внутри, оказывается, стояла проверка на размер этого массива. Причем эта проверка, как сказал Benedikt Meurer (ведущий разработчик Turbofan), не имела никакого смысла, она просто copypaste-ом с годами переходила.

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



          Другой пример, вот у нас есть dispatcher, который вызывает два callback. Так же, если мы его вызовем, то увидим, что он был деоптимизирован. В чем здесь проблема? В том, что одна функция является strict, а вторая не strict. И у них в старом компиляторе получаются разные hidden classes. Т.е. он считает их разными. И в этом случае он так же уходит на деоптимизацию. И этот, и предыдущий код, он написан в принципе правильно, но он деоптимизируется. Это неожиданно.



          Еще был вот такой пример в твиттере, когда оказалось, что в некоторых случаях цикл for в chrome работал даже медленнее, чем reduce. Хотя мы знаем, что reduce медленнее. Оказалось, проблема в том, что внутри for использовался let – неожиданно. Я поставил даже последнюю версию на тот момент и результат уже хороший – исправили.



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



          Сейчас же V8 поддерживает девять архитектур!



          И код копился годами. Он был написан частично на C, Assembler, JS, и вот так примерно ощущал себя разработчик, который приходил в команду.



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



          С 2013 по 2017 года стало на 29% меньше архитектурно-специфичного кода. Это произошло за счет появления новой архитектуры генерации кода в Turbofan.



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

          А в чем причина? Причину держит в руках Стив Джобс.



          Это, конечно, не сам iPhone, а те смартфоны, которые породил iPhone, которые дали удобный доступ в интернет. И это привело к тому, что количество пользователей на мобильных устройствах превысило количество на десктопах.



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



          Вот схема времени первичного анализа 1МБ JavaScript. И недавно был вопрос, почему ВКонтакте делает серверный рендеринг, а не клиентский. Потому что время, потраченное на анализ JS, может быть в 2-5 раз больше на мобильных устройствах. И это мы говорим о топовых устройствах, а люди зачастую ходят с совсем другими.

          И еще одна проблема: у многих китайских устройствах памяти 512 МБ, а если посмотреть, как происходит распределение памяти V8, то появляется еще одна проблема.



          Память делится на объекты (то что использует наш код) и кодовые объекты (это то, что использует сам компилятор — например, хранит там inline caches). Получается, что 30% памяти занято виртуальной машиной для поддержки внутреннего использования. Мы не можем этой памятью управлять, ее сам компилятор потребляет.

          С этим необходимо было что-то делать, и в 2016 году команда разработчиков Android из Лондона ответила созданием нового интерпретатора Ignition.



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



          Теперь абстрактное синтаксическое дерево парсится в байт-код, и этот парсинг JavaScript происходит один раз, дальше используется байткод.

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

          Посмотрим, как для такого кусочка программы генерируется байткод.



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

          Так мы устанавливаем значения для этих регистров входные, которые попадут в нашу программу. На первом этапе мы загружаем в специальный регистр accumulator (он нужен для того, чтобы не расходовать лишний раз регистры, а участвует только в вычислениях) smi integer равный 100.



          Следующая команда нам говорит, что нужно вычесть из регистра a2 (в табличке видим там 150) предыдущее значение аккумулятора (100). В accumulator мы получили 50.



          Дальше нам команда говорит, что нужно сохранить в r0. Он связан с переменной d.



          Дальше становится более понятно. Снова загружаем значение из b, умножаем на значение accumulator, добавляем a0 и получаем на выходе, соответственно, 105.



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

          Была вторая проблема, это память, которую потребляли inline caches. Для этого перешли на новые кеши – Data-driven IC, которые уменьшают стоимость медленного пути. Медленный путь – это как работает не оптимизированный код, быстрый код – когда он оптимизирован.



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



          И наконец в этом году схема сильно упростилась.



          Здесь мы всегда работаем в компиляторе Turbofan. Можно заметить, что раньше компилятор FullCodegen знал весь JS, а компилятор Crankshaft — только часть JS, а теперь компилятор Turbofan знает весь JS и работает со всем JS. Если он не может оптимизировать, он выдает неоптимизированный код, если может, то, соответственно, оптимизированный. И за кодом он обращается в интерпретатор.



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

          В V8 код проходит через интерпретатор и JIT-компилятор. Задача JIT-компилятора — сделать код быстрее. JIT-компилятор прогоняет наш код в цикле и каждый раз, основываясь на данных, которые получил в прошлый раз, пытается сделать код лучше. Он наполняет его знанием о типах, которые получает при первом прогоне, и за счет этого делает какие-то оптимизации. В нем внутри лежат кусочки, которые ориентированы для работы с максимальной производительностью. Если у нас в коде есть a+b – это медленно. Мы знаем, что это number+number или string+string, мы можем сделать это быстро. Вот этим занимается JIT-компилятор.

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

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

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

          Вот и все основные секреты.

          Ссылки для чтения
          github.com/v8/v8/wiki/TurboFan

          http://benediktmeurer.de/
          http://mrale.ph/
          http://darksi.de/
          https://medium.com/@amel_true



          Если вы любите JS так же, как мы, и с удовольствием копаетесь во всей его нутрянке, вам могут быть интересные вот эти доклады на грядущей московской конференции HolyJS:

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

          https://habrahabr.ru/post/338930/


          Метки:  

          В поисках перформанса, часть 2: Профилирование Java под Linux

          Четверг, 28 Сентября 2017 г. 17:24 + в цитатник
          ValeriaKhokha сегодня в 17:24 Разработка

          В поисках перформанса, часть 2: Профилирование Java под Linux

            Бытует мнение, что бесконечно можно смотреть на огонь, воду и то, как другие работают, но есть и ещё кое-что! Мы уверены, что можно бесконечно говорить с Сашей goldshtn Гольдштейном о перформансе. Мы уже брали у Саши интервью перед JPoint 2017, но тогда разговор касался конкретно BPF, которому был посвящен доклад Саши.

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



            С чего стоит начинать



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

            Саша Гольдштейн: Если начинать думать о производительности только в момент, когда ваши пользователи жалуются, с вами они будут недолго. Для многих инженерия производительности — это траблшутинг и crisis mode. Телефоны звонят, свет мигает, система упала, клавиатура горит — обычные трудовый будни перформанс-инженера. В реальности же они проводят большую часть своего времени, планируя, проектируя, мониторя и предотвращая кризисы.

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

            Стоит отметить, что тулы постоянно меняются, но сам процесс остаётся неизменным. Приведу пару конкретных примеров: capacity planning вы можете сделать и на бумажке на коленке; можно использовать APM-решения (как New Relic или Plumbr) для end-to-end инструментирования и мониторинга, AB и JMeter для быстрого нагрузочного тестирования и так далее. Чтобы узнать больше, можно почитать книгу Брендана Грегга «Systems Performance» — это отличный источник по теме жизненного цикла и методологии перформанса, а «Site Reliability Engineering» от Google освещает тему установки характеристик производительности (Service Level Objectives) и их мониторинга.

            — Допустим, мы поняли, что проблема есть: с чего начинать? Мне часто кажется, что многие (особенно не профессиональные перформанс-инженеры) сразу готовы расчехлять JMH, переписывать всё на unsafe и «хакать компиляторы». Потом смотреть, что получилось. Но ведь в реальности лучше не с этого начинать?

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

            Вот пара примеров для иллюстрации:

            • Пару лет назад Datadog столкнулись с проблемой, когда вставки и обновления базы данных в PostgreSQL скакали от 50 мс до 800 мс. Они использовали AWS EBS с SSD. Что это дало? Вместо тюнинга базы данных или изменения кода приложения они обнаружили, что виноват во всём троттлинг EBS: у него есть квота по IOPS, в случае превышения которой вы попадёте под ограничение производительности.
            • Недавно у меня был пользователь с проблемой огромных скачков времени отклика на сервере, которые были связаны с задержками сбора мусора. Некоторые запросы занимали более 5 секунд (и появлялись они абсолютно бессистемно), так как сборка мусора выходила из-под контроля. Внимательно изучив систему, мы обнаружили, что с распределением памяти приложения или же с тюнингом сборки мусора всё было в порядке; из-за скачка в размере рабочей нагрузки фактическое использование памяти повысилось и вызвало свопинг, что абсолютно губительно для реализации любой сборки мусора (если сборщику нужно подкачивать и откачивать память, чтобы отмечать активные объекты, — это конец).
            • Пару месяцев назад Sysdig столкнулся с проблемой изоляции контейнера: находясь рядом с контейнером Х, операции с файловой системой, выполняемые контейнером Y, были гораздо медленнее, в то время как использование памяти и загрузка процессора для обоих контейнеров были очень низкими. После небольшого исследования они обнаружили, что кэш каталогов ядра перегружался контейнером Х, что в дальнейшем вызывало коллизию хэш-таблицы и, как следствие, значительное замедление. Опять-таки, изменение кода приложения или распределения ресурсов контейнера эту проблему бы не решили.

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

            — Наверняка следует сначала посмотреть, как приложение/сервис работает в продакшне. Какие инструменты ты для этого рекомендуешь, а какие — не очень?

            Саша Гольдштейн: Мониторинг и профайлинг на проде — это набор инструментов и техник.

            Начинаем с метрик высокоуровневого перформанса, фокусируясь на использовании ресурсов (процессор, память, диск, сеть) и характеристике нагрузки (# запросов, ошибок, типов запросов, # запросы к базе данных). Есть стандартные тулы для получения этих данных для каждой операции и времени выполнения. К примеру, на Linux обычно используют инструменты вроде vmstat, iostat, sar, ifconfig, pidstat; для JVM используют JMX-based тулы или jstat. Это метрики, которые можно непрерывно собирать в базу данных, возможно с 5-ти или 30-ти секундным интервалом, чтобы можно было проанализировать скачки и при необходимости вернуться в прошлое, чтобы скоррелировать предыдущие операции по развёртыванию, релизы, мировые события или изменения рабочей нагрузки. Важно, что многие фокусируются на собирании только средних показателей; они хоть и хороши, но, по определению, не представляют полное распределение того, что вы измеряете. Гораздо лучше собирать процентили, а по возможности даже и гистограммы.

            Следующий уровень — это операционные метрики, которые обычно нельзя непрерывно собирать или хранить долгое время. Они включают в себя: лог сбора мусора, запросы сети, запросы к базе данных, классовые нагрузки и так далее. Разобраться в этих данных после того, как их где-то хранили, иногда гораздо труднее, чем, собственно, их собирать. Это позволяет, однако, задавать вопросы, вроде «какие запросы работали, пока нагрузка ЦП базы данных повышалась до 100%» или «какими были IOPS дисков и время отклика во время выполнения этого запроса». Одни лишь числа, особенно в виде средних показателей, не позволят вам провести подобного рода исследование.

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

            Для профайлинга Java-продакшна существует множество жутких тулов, которые не только дают большой оверхэд и задержки, но могут ещё и лгать вам. Несмотря на то, что экосистеме уже около 20 лет, есть лишь несколько надёжных профайлинговых техник с низким оверхэдом для JVM-приложений. Я могу порекомендовать Honest Profiler Ричарда Ворбертона, async-profiler Андрея Паньгина и, конечно, моего любимчика — perf.

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

            Профилирование Java под Linux



            — Я слышал, что под Java/Linux стек есть куча проблем с достоверностью замеров. Наверняка с этим можно как-то бороться. Как ты это делаешь?

            Саша Гольдштейн: Да, это печально. Вот как выглядит текущая ситуация: у вас есть быстрая конвейерная линия с огромным количеством разных частей, которые нужно протестировать, чтобы найти дефекты и понять скорость приложения/сервиса. Вы не можете проверить абсолютно каждую часть, поэтому ваша основная стратегия — это проверять 1 часть в секунду и смотреть, всё ли в порядке, и вам нужно это делать через «крохотное окно» над этой «лентой», потому что подходить ближе уже опасно. Вроде неплохо, не так ли? Но потом оказывается, что когда вы пытаетесь в него посмотреть, оно показывает вам не то, что происходит на конвейере прямо сейчас; оно ждёт, пока конвейер перейдёт в волшебный «безопасный» режим, и только после этого даёт вам всё увидеть. Также оказывается, что многих частей вы не увидите никогда, потому что конвейер не может войти в свой «безопасный» режим, пока они рядом; и ещё оказывается, что процесс поиска дефекта в окошке занимает целых 5 секунд, так что делать это каждую секунду невозможно.

            Примерно в таком состоянии сейчас находится множество профайлеров в мире JVM. YourKit, jstack, JProfiler, VisualVM — у всех у них одинаковый подход к профайлингу ЦП: они используют семплинг потоков в безопасном состоянии. Это значит, что они используют документированный API, чтобы приостановить все JVM-треды и взять их стек-трейсы, которые затем собирают для отчёта с самыми горячими методами и стеками.

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

            Есть исследование, показывающее, насколько это плохо, когда у каждого профайлера своя точка зрения по поводу самого горячего метода в одной и той же рабочей нагрузке (Миткович и соавт., «Evaluating the Accuracy of Java Profilers»). Более того, если у вас есть 1000 тредов в сложном стеке вызовов Spring, часто собирать стек-трейсы не получится. Возможно, не чаще 10 раз в секунду. В результате ваши данные по стекам будут отличаться от фактической рабочей нагрузки ещё больше!

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

            Существует два раздельных подхода и один гибридный:

            • Honest Profiler Ричарда Ворбертона использует внутренний недокументированный API, AsyncGetCallTrace, который возвращает стек-трейс одного треда, не требует перехода в безопасное состояние и вызывается с помощью обработчика сигнала. Изначально он был спроектирован Oracle Developer Studio. Основной подход заключается в установке обработчика сигнала и его регистрации на сигнал с установленным временем (например, 100 Гц). Затем необходимо взять стек-трейс любого треда, который в данный момент работает внутри обработчика сигнала. Очевидно, что есть непростые задачи, когда дело доходит до эффективного объединения стек-трейсов, особенно в контексте обработчика сигнала, но этот подход отлично работает. (Этот подход использует JFR, требующий коммерческую лицензию)
            • Linux perf может предоставлять богатый сэмплинг стеков (не только для инструкций ЦП, но и для других событий, таких как доступ диска и запросы сети). Проблема заключается в преобразовании адреса Java-метода в имя метода, что требует наличие JVMTI-агента, достающего текстовый файл (perf map), который perf может читать и использовать. Есть также проблемы с реконструкцией стека, если JIT использует подавление указателей фрейма. Этот подход вполне может работать, но требует небольшой подготовки. В результате, однако, вы получите стек-трейсы не только для JVM-тредов и Java-методов, но и для всех имеющихся у вас тредов, включая стек ядра и стеки C++.
            • async-profiler Андрея Паньгина сочетает в себе два подхода. Он устанавливает набор образцов perf, но также использует обработчик сигнала для вызова AsyncGetStackTrace и получения Java-стека. Объединение двух стеков даёт полную картину того, что происходит в потоке, позволяя избегать проблем с преобразованием имен Java-методов и подавлением указателей фрейма.

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

            Профилирование в контейнерах


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

            Саша Гольдштейн: С контейнерами возникают интересные проблемы, которые многие тулы полностью игнорируют и в результате вообще перестают работать.

            Вкратце напомню, что контейнеры Linux построены вокруг двух ключевых технологий: группы контроля и пространства имён. Группы контроля позволяют увеличить квоту ресурсов для процесса или для группы процессов: CPU time caps, лимит памяти, IOPS хранилища и так далее. Пространства имён делают возможной изоляцию контейнера: mount namespace предоставляет каждому контейнеру свою собственную точку монтирования (фактически, отдельную файловую систему), PID namespace — собственные идентификаторы процессов, network namespace даёт каждому контейнеру собственный сетевой интерфейс и так далее. Из-за пространства имён множеству тулов сложно правильно обмениваться данными с контейнезированными JVM-приложениями (хотя некоторые из этих проблем свойственны не только JVM).

            Прежде чем обсудить конкретные вопросы, будет лучше, если мы вкратце расскажем о разных видах observability тулов для JVM. Если вы не слышали о некоторых из них, самое время освежить ваши знания:

            • базовые тулы вроде jps и jinfo предоставляют информацию о действующих JVM-процессах и их конфигурации;
            • jstack можно использовать, чтобы достать thread dump (стек-трейс) из действующих JVM-процессов;
            • jmap — для получения head dump действующих JVM-процессов или более простых гистограмм классов;
            • jcmd используется, чтобы заменить все предыдущие тулы и отправить команды действующим JVM-процессам через JVM attach interface; он основан на сокете домена UNIX, который используется JVM-процессами и jcmd для обмена данными;
            • jstat — для мониторинга базовой информации JVM-перформанса, как загрузка класса, JIT-компиляция и статистика сборки мусора; основан на JVM, генерирующей файлы /tmp/hsperfdata_$UID/$PID с этими данными в бинарном формате;
            • Serviceability Agent предоставляет интерфейс для проверки памяти JVM процессов, тредов, стеков и так далее и может быть использован с дампом памяти и не только живых процессов; он работает, читая память процесса и структуры внутренних данных;
            • JMX (управляемые бины) может использоваться для получения информации о перформансе от действующего процесса, а также для отправления команд, чтобы контролировать его поведение;
            • JVMTI-агенты могут присоединяться к разным интересным JVM-событиям, таким как загрузка классов, компиляция методов, запуск/остановка треда, monitor contention и так далее.

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

            • большинство инструментов требуют доступ к бинарникам процесса для поиска объектов и значений. Всё это находится в mount namespace контейнера и недоступно из хоста. Частично этот доступ можно обеспечить при помощи bind-mounting из контейнера или к вводу mount namespace контейнера в профайлере во время выполнения symbol resolving (это то, что сейчас делают perf и BCC тулы, о которых я рассказывал в предыдущем интервью).
            • если у вас есть JVMTI-агент, который генерирует perf map (например, perf-map-agent), она будет написана в хранилище контейнера /tmp, используя ID процесса контейнера (например, /tmp/perf-1.map). Map-файл должен быть доступен хосту, а хост должен ждать верный ID процесса в имени файла. (Опять же, perf и BCC теперь могут делать это автоматически).
            • JVM attach interface (на который полагаются jcmd, jinfo, jstack и некоторые другие тулы) требует верных PID и mount namespace attach файла, а также сокет UNIX домена, использовавшегося для обмена данными с JVM. Эту информацию можно прокинуть при помощи jattach utility и создания attach файла, входя в пространство имён контейнера или при помощи bind-mounting соответствующих директорий на хосте.
            • использование файлов данных о производительности JVM (в /tmp/hsperfdata_$UID/$PID), которыми пользуется jstat, требует доступ к монтированию пространства имён контейнера. Это легко адресуется bind-монтированием /tmp контейнера на хосте;
            • самый простой подход к использованию JMX-based инструментов — это, пожалуй, доступ к JVM, как если бы она была удалённой — конфигурируя RMI endpoint, как вы бы делали для удалённой диагностики;
            • тулы Serviceability Agent требуют точного соответствия версий между JVM-процессом и хостом. Думаю, вы понимаете, что не стоит запускать их на хосте, особенно если он использует разное распределение и на нём установлены разные версии JVM.

            Тут можно подумать: а если мне просто положить перформанс-тулы в контейнер, чтобы все эти проблемы с изоляцией возникали не из-за меня? Хоть идея неплохая, множество тулов не будут работать и с такой конфигурацией из-за seccomp. Docker, к примеру, отклоняет системный вызов perf_event_open, необходимый для профайлинга с perf и async-profiler; он также отклоняет системный вызов ptrace, который используется большим количеством тулов для чтения объёма памяти JVM-процесса. Изменение политики seccomp для принятия этих системных вызовов ставит хост под угрозу. Также, помещая тулы профайлинга в контейнер, вы увеличиваете его поверхность атаки.



            Мы хотели продолжить разговор и обсудить влияние железа на профилирование…



            Совсем скоро Саша приедет в Санкт-Петербург, чтобы провести тренинг по профилированию JVM-приложений в продакшне и выступить на конференции Joker 2017 с докладом про BPF, поэтому, если вы хотите погрузиться в тему глубже – у вас есть все шансы встретиться с Сашей лично.
            Original source: habrahabr.ru (comments, light).

            https://habrahabr.ru/post/338928/


            Метки:  

            Материалы с VLDB, конференции о будущем баз данных

            Четверг, 28 Сентября 2017 г. 17:22 + в цитатник
            azathot сегодня в 17:22 Разработка

            Материалы с VLDB, конференции о будущем баз данных

              Конференция VLDB (Very Large Data Bases, www.vldb.org), как несложно понять из названия, посвящена базам данных. Очень большим базам данных. О чем её название не говорит, так это о том, что там регулярно выступают очень серьезные люди. Много ли вы знаете конференций, где почти каждый год докладывается Майкл Стоунбрекер (Michael Stonebraker, создатель Vertica, VoltDB, PostgreSQL, SciDB)? Не думали ли вы, что было бы здорово узнать, над чем такие люди работают сейчас, чтобы через несколько лет, когда новая база разорвет рынок, не грызть локти?


              VLDB — именно та конференция, которую вам нужно посетить, если вы думаете о будущем.
              Она вам не очень поможет, если вы выбираете из существующих баз. Там есть небольшая доля industrial докладов (Microsoft, Oracle, Teradata, SAP Hana, Exadata, Tableau (!)), но самое интересное — это исследовательские доклады от университетов. Xотя очень быстро обнаруживается, что в командах университетов есть один-два человека, работающих на Google, Facebook, Alibaba… или перешедших туда сразу после подачи статьи.


              Надеюсь, мне удалось вас базово заинтересовать, а теперь давайте пройдемся, собственно, по докладам.



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


              1. Базы будущего


              Очень скоро у нас появится дешевая энергонезависимая память (совмещение RAM+Hard Drive). Оперативная память, ядра и видеокарты стремительно дешевеют. Какими должны быть базы будущего, чтобы выиграть от всего этого технологического великолепия? Какие новые проблемы возникают?


              1.1 Distributed Join Algorithms on Thousands of Cores


              Понятно по названию: это исследование работы алгоритмов распределенного Join на системах с тысячами ядер.


              1.2 Adaptive Work Placement for Query Processing on Heterogeneous Computing Resources


              Распределение задач по разнородному кластеру.


              1.3 SAP HANA Adoption of Non-Volatile Memory


              Первые эксперименты с энергонезависимой памятью.


              2. Транзакции в распределенных (кластерных) базах


              Хорошо и легко жить на одном сервере. А вдруг базу нужно развернуть в кластер? Вдруг одну базу нужно расколоть на десятки мелких, согласно микросервисной архитектуре? Как быть с транзакциями?


              2.1 An Evaluation of Distributed Concurrency Control


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


              2.2 The End of a Myth: Distributed Transactions Can Scale


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


              3. Подмена хранения


              Модный подход сейчас — подменить у старых баз инфраструктуру хранения и подложить туда что-то быстрое. Например, in-memory key-value хранилище. Или, например, сразу два параллельных хранилища — строчное и колоночное. Или шесть хранилищ на разных физических машинах...


              3.1 Fast Scans on Key-Value Stores


              Что нужно сделать для решения OLAP-задач на key-value базе.


              3.2 PaxosStore: High-availability Storage Made Practical in WeChat


              Статья о том, как устроены базы данных у TenCent (WeChat). 800 миллионов активных пользователей — расскажите им про высокую нагрузку.


              3.3 Parallel Replication across Formats in SAP HANA for Scaling Out Mixed OLTP/OLAP Workloads


              OLTP + OLAP нагрузка на одной базе.


              4. Оптимизация запросов


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


              4.1 Runtime Optimization of Join Location in Parallel Data Management Systems


              4.2 SquirrelJoin: Network-Aware Distributed Join Processing with Lazy Partitioning


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


              5. Визуализация и анализ данных


              5.1 ASAP: Prioritizing Attention via Time Series Smoothing


              Как сгладить графики, убрав шум, но оставив аномалии.


              5.2 Effortless Data Exploration with zenvisage: An Expressive and Interactive Visual Analytics System


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


              6. Человеко-машинный интерфейс :)


              6.1 Data Vocalization: Optimizing Voice Output of Relational Data


              "Data Vocalization" звучит совершенно фантастически, но суть проста: как сжать выборку, выданную запросом, в ограниченный набор слов, чтобы вы дослушали Siri, а не разбили телефон.


              6.2 Provenance for natural language Queries


              <Лучшая статья VLDB 2017>. Да, именно так. Про то, как писать запросы к данным на естественном языке. Точнее так: как транслировать вопросы на естественном языке в запросы к данным, а результаты — обратно на человеческий язык.


              Напоследок


              Собственно, на этом всё. Казалось бы немного: я собрал тут для вас всего 14 статей. Но мне было бы очень интересно узнать, сколько людей реально прочтут их все до конца. Если возьмётесь, напишите в комментариях, сколько времени это заняло. Для тех, кто смелый, по ссылке — оставшиеся 218 статей: http://confer.csail.mit.edu/vldb2017/papers. И вот фото с доклада организаторов конференции.



              PS. VLDB 2017 была в Мюнхене, для участников был маленький Октоберфест (хороший :)). Следующая VLDB будет в Бразилии, вливайтесь! Я постараюсь пройти с докладом (в 2015 не смог).

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

              https://habrahabr.ru/post/338180/


              Метки:  

              Сахарный JavaScript

              Четверг, 28 Сентября 2017 г. 17:20 + в цитатник
              htmlacademy сегодня в 17:20 Разработка

              Сахарный JavaScript


                В чём разница между нейтив JavaScript и TypeScript?

                Браузеры не поддерживают других языков, кроме JavaScript. Поэтому любой язык будет компилироваться в JavaScript. Помните, несколько выпусков назад мы говорили с вами про обёртки? Так вот язык — это просто более удобная обёртка над какой-нибудь функциональностью. Разные языки предлагают разные возможности: зачастую это сводится к модульности, типизации или особенностям других парадигм программирования. Например:


                • Dart предлагает нам модульность и типизацию.
                • Elm — функциональное программирование и тоже типизацию.
                • Clojure — просто функциональное программирование.
                • CoffeeScript, хм-м…

                Да простит бог, тех кто придумал CoffeeScript, они не ведали что творили. Впрочем, стрелочные функции оттуда в JavaScript всё-таки попали. А вот обёртки над классами не будем ставить им в заслугу, потому что слова class и extends были зарезервированы в языке ещё задолго до появления CoffeeScript.


                Теперь посмотрим, что нам предлагает TypeScript. В TypeScript есть: модульность, типизация и некоторые фичи, которых не было в JavaScript. Dart предлагал то же самое, так почему же он не взлетел? А TypeScript используется в Angular версии 2 и 4, на которых написано полмира.


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


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


                JavaScript использует слабую типизацию. Это когда в любую переменную можно записать любое значение.


                let anyVariable = 'It’s a string';
                anyVariable = ['No,', 'I’ve', 'changed', 'my', 'mind'];

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


                let anyVariable: string = 'It’s a string';
                anyVariable = ['I', 'can’t', 'do', 'this']; // Exception

                Сторонники сильной типизации приводят два аргумента в её пользу:


                1. Во-первых, это более эффективная работа с памятью;
                2. А во-вторых, это предотвращение ошибок, связанных с преобразованием типов.

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


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


                function getMonthlyIncome(val) {
                  return val * 0.055 / 12;
                };

                Слабо типизированный JS пропустит эту строку в любом виде, даже если она не переводится в число. И в результате таких вычислений пользователь может получить NaN рублей. В TypeScript же вам придётся сначала перевести эту строку в число самостоятельно, а потом уже передать в функцию в надлежащем виде. А если вы забудете это сделать, то вы увидите ошибку ещё на этапе компиляции вашего кода.


                function getMonthlyIncome(val: number): number {
                  return val * 0.055 / 12;
                };

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


                У Facebook есть инструмент для типизации, он называется Flow. Он добавляет систему типов, похожую на систему из языка OCaml, в ваш JavaScript-код. Библиотека Google Closure Library использует хитрый механизм типизации на основе комментариев JSDoc. Вообще, написание комментариев JSDoc, даже без компиляции и хитрой системы типов, поможет вам решить некоторые проблемы. Во-первых, вы сами будете понимать интерфейс тех функций, которые пишете. А во-вторых, некоторые редакторы, которые поддерживают JSDoc, будут подсказывать вам что вы что-то не то передаёте куда-то не туда.


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


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


                Видеоверсия





                Вопросы можно задавать здесь.

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

                https://habrahabr.ru/post/338914/


                Как Алексей Моисеенков дошел до Prisma и пошел дальше

                Четверг, 28 Сентября 2017 г. 16:51 + в цитатник
                Prisma — приложение для оформления фотографий в стилистике определенных художников — стало одним из примеров резкой популярности на мировом рынке. Попутно «Призма» удостоилась как массе восхищенных похвал, так и ряду упреков: от вторичности разработки до краткосрочной популярности самого продукта. Алексей Моисеенков с начала и до сего дня — основной двигатель этого проекта. Далее — наш разговор с ним.

                Бизнес-уроки


                Ажиотаж прошел. Вы им правильно воспользовались? Может, есть обстоятельства, которые не позволили вам воспользоваться хайпом в достаточной мере?

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

                Prisma никогда бы не осталась востребованной надолго. Стилизация фото — как мода. Ни мы, никто ничего не может сделать, чтобы хайп длился вечно. Денег и связей, наверное, мы могли бы извлечь больше, методов работы с аудиторией, конечно, можно было придумать ещё массу. Стоило бы активнее работать в Азии. Но это точно не известно, неясно, как бы эти шаги отразились на популярности «Призмы».
                Читать дальше ->

                https://habrahabr.ru/post/338758/


                Метки:  

                [Из песочницы] Вышел React v16.0

                Четверг, 28 Сентября 2017 г. 16:42 + в цитатник
                dagen вчера в 16:42 Разработка

                Вышел React v16.0

                Это перевод поста Эндрю Кларка о выходе столь ожидаемой версии React. Оригинальный пост в блоге React.


                Мы с удовольствием сообщаем о выходе React v16.0! Среди изменений некоторые давно ожидаемые нововведения, например фрагменты, обработка ошибок (error boundaries), порталы, поддержка произвольных DOM-атрибутов, улучшения в серверном рендере, и уменьшенный размер файла.


                Новые типы для рендера: фрагменты и строки


                Теперь вы можете вернуть массив элементов из render-метода компонента. Как и с другими массивами, вам надо добавлять ключ к каждому элементу, чтобы реакт не ругнулся варнингом:


                render() {
                  // Нет необходимости оборачивать в дополнительный элемент!
                  return [
                    // Не забудьте добавить ключи :)
                    
              • Первый элемент
              • ,
              • Второй элемент
              • ,
              • Третий элемент
              • , ]; }

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


                Мы добавили поддержку и для возврата строк:


                render() {
                  return 'Мама, смотри, нет лишних спанов!';
                }

                Полный список поддерживаемых типов.


                Улучшенная обработка ошибок


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


                Вместо отмонтирования всего приложения при каждой ошибке, вы можете использовать error boundaries. Это специальные компоненты, которые перехватывают ошибки в своём поддереве и позволяют вывести резервный UI. Воспринимайте их как try-catch операторы, но для React-компонентов.


                Для дальнейшей информации проверьте наш недавний пост про обработку ошибок в React 16.


                Порталы


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


                render() {
                  // React не создаёт новый див. Он рендерит дочерние элементы в domNode.
                  // domNode - это любой валидный DOM-узел,
                  // вне зависимости от его расположения в DOM-дереве.
                  return ReactDOM.createPortal(
                    this.props.children,
                    domNode,
                  );
                }

                Полный пример находится в документации по порталам.


                Улучшенный серверный рендеринг


                React 16 содержит полностью переписанный серверный рендерер, и он действительно быстрый. Он поддерживает стриминг, так что вы можете быстрее начинать отправлять байты клиенту. И благодаря новой стратегии сборки, которая убирает из кода обращения к process.env (хотите верьте, хотите — нет, но чтение process.env в Node очень медленное!), вам больше не надо бандлить React для получения хорошей производительности серверного рендеринга.


                Ключевой разработчик Саша Айкин написал замечательную статью, рассказывающую об улучшениях SSR в React 16. Согласно Сашиным бенчмаркам, серверный рендеринг в React 16 примерно в 3 раза быстрее, чем в React 15. При сравнении с рендером в React 15 c убранным из кода process.env получается ускорение в 2.4 раза в Node 4, около 3-х раз в Node 6 и аж в 3.8 раза в Node 8.4. А если вы сравните с React 15 без компиляции (без убранного process.env), React 16 оказывается на порядок быстрее в последней версии Node! (Как Саша указал, к этим синтетическим бенчмаркам надо относиться осторожно, так как они могут не отображать производительность реальных приложений).


                Более того, React 16 лучше в восстановлении отрендеренного на сервере HTML, когда последний приходит в браузер. Больше не требуется начальный рендер, используемый для проверки результатов с сервера. Вместо этого он будет пытаться переиспользовать как можно больше существующего DOM. Больше не будут использоваться контрольные суммы! В целом мы не рекомендуем рендерить на клиенте отличающийся от сервера контент, но это может быть полезно в некоторых сценариях (например вывод меток времени).


                Больше в документации по ReactDOMServer.


                Поддержка произвольных DOM-атрибутов


                Вместо игнорирования неизвестных HTML и SVG атрибутов, теперь React будет просто передавать их в DOM. Вдобавок это позволяет нам отказаться от длиннющих списков разрешённых атрибутов, что уменьшает размер бандла.


                Уменьшенный размер файла


                Несмотря на все эти нововведения, React 16 меньше, чем React 15.6.1!


                • react весит 5.3 kb (2.2 kb gzipped), по сравнению с 20.7 kb (6.9 kb gzipped) ранее.
                • react-dom весит 103.7 kb (32.6 kb gzipped), по сравнению с 141 kb (42.9 kb gzipped) ранее.
                • react + react-dom вместе 109 kb (34.8 kb gzipped), по сравнению 161.7 kb (49.8 kb gzipped) ранее.

                В общей сложности размер уменьшился на 32% по сравнению с прошлой версией (30% после gzip-сжатия).


                На уменьшение размера частично влияют изменения в сборке. React теперь использует Rollup для создания "плоских" бандлов (по-видимому Эндрю Кларк тут имел ввиду "scope hoisting", что давно было в Rollup, но в Webpack появилось только в третьей версии) всех поддерживаемых форматов, что привело к выигрышу и в размере и в скорости работы. Также плоский формат бандла приводит к тому, что воздействие React на бандл приложения остаётся одинаковым, независимо от того, как вы доставляете свой код конечным пользователям, напр. используя Webpack, Browserify, уже собранные UMD-модули или любой другой способ.


                MIT лицензия


                Если вы вдруг пропустили, React 16 теперь доступен под MIT лицензией. А для тех, кто не может обновиться немедленно, мы выложили версию React 15.6.2 под MIT.


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


                React 16 — это первая версия React, построенная на основе новой архитектуры, называемой Fiber. Вы можете почитать всё об этом проекте в инженерном блоге Facebook. (Спойлер: мы полностью переписали React!)


                Fiber затрагивает большинство новых фич в React 16, такие как error boundaries или фрагменты. Через несколько релизов вы увидите несколько новых фич, так как мы будем постепенно раскрывать потенциал React.


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


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


                Ever wonder what "async rendering" means? Here's a demo of how to coordinate an async React tree with non-React work https://t.co/3snoahB3uV pic.twitter.com/egQ988gBjR


                — Andrew Clark (@acdlite) September 18, 2017

                Мы думаем, что асинхронный рендеринг — очень важная вещь, двигающая React в будущее. Чтобы сделать переход на v16.0 как можно более безболезненным, мы пока не включили какие-либо асинхронные фичи, но мы рады выкатить их в ближайшие месяцы. Следите за обновлениями!


                Установка


                React v16.0.0 доступен в npm репозитории.


                Для установки React 16 используя Yarn:


                yarn add react@^16.0.0 react-dom@^16.0.0

                Для установки React 16 используя npm:


                npm install --save react@^16.0.0 react-dom@^16.0.0

                Мы также предоставляем UMD-вариант, выложенный на CDN:


                
                

                Ссылка на документацию по детальным инструкциям по установке.


                Переход со старой версии


                Хотя React 16 включает значительные внутренние изменения, в случае обновления вы можете относится к этому релизу как к любому обычному мажорному релизу React. Мы используем React 16 в Facebook и Messenger.com с начала этого года, мы выкатили несколько бета-версий и релиз кандидатов, чтобы максимально исключить возможные проблемы. Если не учитывать некоторые нюансы, то ваше приложение должно работать с 16-й версией, если с 15.6 оно работало без каких-либо варнингов.


                Устаревшие методы


                Восстановление отрендеренного на сервере кода теперь имеет явное API. Для восстановления HTML вам надо использовать ReactDOM.hydrate вместо ReactDOM.render. Продолжайте использовать ReactDOM.render, если вы рендерите только на клиентской стороне.


                React Addons


                Как ранее было объявлено, мы прекращаем поддержку React Addons. Мы ожидаем, что последняя версия каждого дополнения (кромеreact-addons-perf; см. ниже) будет работоспособна в ближайшем будущем, но мы не будем публиковать новых обновлений.


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


                А react-addons-perf вообще не будет работать в React 16. Скорее всего мы выпустим новую версию этого инструмента в будущем. А пока вы можете использовать браузерные инструменты для измерения производительности.


                Несовместимые изменения


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


                • React 15 имел ограниченную и недокументированную поддержку error boundaries через использование unstable_handleError. Этот метод теперь переименован в componentDidCatch. Вы можете использовать codemod для автоматической миграции на новое API.
                • ReactDOM.render и ReactDOM.unstable_renderIntoContainer теперь возвращают null, если вызваны из lifecycle-метода. Вместо этого теперь используйте порталы или ссылки.
                • setState:
                  • Вызов setState с null больше не будет вызывать реконсиляцию. Это позволит определять в коллбеке надо ли вызывать перерендер.
                  • Вызов setState напрямую в рендере всегда вызывает реконсиляцию, чего раньше не было. Независимо от того, вам однозначно не надо вызывать setState из метода рендера.
                  • Коллбек setState'а (второй аргумент) теперь вызывается немедленно после componentDidMount / componentDidUpdate, вместо того чтобы ждать полного рендера дерева компонентов.
                • При замене на , B.componentWillMount будет вызываться всегда перед A.componentWillUnmount. Ранее A.componentWillUnmount мог вызываться раньше в некоторых случаях.
                • Ранее изменение ссылки на компонент вызывало всегда обнуление ссылки перед вызовом рендера. Теперь мы изменяем ссылку позднее, когда применяем изменения к DOM.
                • Небезопасно вызывать рендер контейнера, если содержимое было изменено в обход React. Ранее это работало в некоторых случаях, но никогда не поддерживалось. Мы не вызываем варнинг в этом случае. Вместо этого вы сами должны чистить дерево вашего компонента используя ReactDOM.unmountComponentAtNode. Посмотрите на этот пример.
                • Lifecycle-метод componentDidUpdate больше не получает параметр prevContext. (см.#8631)
                • Shallow рендерер больше не вызывает componentDidUpdate, т.к. ссылки на DOM недоступны. Это изменение делает его консистентным с методом componentDidMount (который тоже не вызывался в предыдущих версиях).
                • Shallow рендерер больше не имеет метода unstable_batchedUpdates.

                Сборка


                • Теперь недоступны react/lib/* и react-dom/lib/*. Даже для CommonJS окружений, React и ReactDOM теперь собраны в отдельные файлы (“flat bundles”). Если ваш проект ранее зависел от недокументированных внутренних возможностей React'а и они больше не работают, дайте нам об этом знать в новом тикете, а мы постараемся придумать способ миграции для вас.
                • Больше нет билда react-with-addons.js. Все аддоны из этого билда уже опубликованы по отдельность в npm и имеют однофайловые браузерные версии, если они нужны вам.
                • Методы и возможности, помеченные устаревшими в 15.x, теперь убраны из основного пакета. React.createClass теперь доступен как create-react-class, React.PropTypes как prop-types, React.DOM как react-dom-factories, react-addons-test-utils как react-dom/test-utils, а shallow рендерер как react-test-renderer/shallow. См. посты в блоге 15.5.0 и 15.6.0 для инструкций по миграции кода и автоматических кодемодов.
                • Имя и путь до однофайловых браузерных билдов изменились для подчёркивания различий между разработческими и боевыми билдами. Например:
                  • react/dist/react.js -> react/umd/react.development.js
                  • react/dist/react.min.js -> react/umd/react.production.min.js
                  • react-dom/dist/react-dom.js -> react-dom/umd/react-dom.development.js
                  • react-dom/dist/react-dom.min.js -> react-dom/umd/react-dom.production.min.js

                Требования к JavaScript-окружению:


                React 16 зависит от коллекций Map и Set. Если вы поддерживаете старые браузеры и устройства, в которых нет этого нативно (напр. IE < 11), используйте полифилы, такие как core-js или babel-polyfill.


                Окружение с полифилами для React 16 используя core-js для поддержки старых браузеров может выглядеть как-то так:


                import 'core-js/es6/map';
                import 'core-js/es6/set';
                
                import React from 'react';
                import ReactDOM from 'react-dom';
                
                ReactDOM.render(
                  

                Hello, world!

                , document.getElementById('root') );

                React также требует requestAnimationFrame (даже в тестовых средах). Простая заглушка для тестовых окружений может выглядеть так:


                global.requestAnimationFrame = function(callback) {
                  setTimeout(callback, 0);
                };

                Благодарности


                Как обычно, этот релиз был бы невозможен без наших контрибьюторов-волонтёров (open source contributors). Спасибо всем, кто заводил баги, открывал пулл-реквесты, отвечал в тикетах, писал документацию.


                Отдельное спасибо нашим корневым контрибьюторам, особенно за их героические усилия в последние несколько недель пререлизного цикла: Brandon Dail, Jason Quense, Nathan Hunzaker, и Sasha Aickin.

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

                https://habrahabr.ru/post/338932/


                Метки:  

                [Перевод] 30 новых ресурсов для android-разработчика (лето 2017)

                Четверг, 28 Сентября 2017 г. 16:36 + в цитатник
                MagisterLudi сегодня в 16:36 Разработка

                30 новых ресурсов для android-разработчика (лето 2017)

                • Перевод
                image

                Компания EDISON Software профессионально занимается разработкой Android-приложений. Вот некоторые крупные проекты:


                Полезные статьи на Хабре по Android:


                (Пост из серии «просмотреть и добавить в избранное»)

                1


                MaterialStepperView — библиотека для использования Steppers из Material Design Components.

                image

                2


                MultiSnapRecyclerView — библиотека для сложных RecyclerView.

                image

                3


                Garland View for Android — библиотека для создания вот таких макетов:

                image

                4


                VegaLayoutManager — кастомизированный LayoutManager - тускнеет и сжимает при прокрутке. Создан по мотивам Dribble project.

                image

                5


                ExpandableLayout -название говорит само за себя. Основан на LinearLayout.

                image

                6


                SwipeBackLayout — библиотека управления жестами.

                image

                7


                SmartCropper — «умное обрезание». Умеет выделять главное, выравнивать и отсекать задний фон.

                image

                8


                Date Range Picker — продвинутый календарь с выбором диапазона дат.

                image

                9


                StoriesProgressView — библиотека для сервиса Stories (Facebook and Instagram)

                image

                10


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

                image

                11


                Reflow Text Animator — надеюсь, все слышали о приложении Plaid. Эта библиотека, разработанная командой Shazam Engineering, представляет собой порт Plaid's ReflowText, который позволяет легко переходить между текстовыми редакторами — независимо от их размера или стиля.

                image

                12


                AdaptiveIconPlayground — это не библиотека, а автономное приложение для Android, разработанное Ником Батчером для экспериментов с адаптивными иконками.

                image

                13


                Tivi — приложение, которое отслеживает телешоу и коннектится с Track.tv. Он разработан Крисом Банесом.

                image

                14


                RxIdler — это IdlingResource для Espresso, который упаковывает RxJava Scheduler, разработанный Square Engineering. Он поддерживает RxJava 1 и RxJava 2.

                15


                MRichEditor — Это редактор «rich text» (на основе summernote).

                image

                16


                Android Clean Architecture Boilerplate — приложение для шаблонов, которое демонстрирует чистый архитектурный подход к приложениям Android, разработанным Buffer team и Джо Берчем.

                image

                17


                RxJava2Debug — если вы используете RxJava, вы знаете, что иногда трудно прочитать исключения и найти проблему в вашем Rx потоке.

                18


                Resizer — библиотека для масштабирования изображения. Он позволяет изменять размер файла изображения, сохраняя пропорции.

                Создана по следам Compressor.

                Спецификация библиотеки:
                Minimum SDK: API 21

                Default settings:
                targetLength: 1080
                quality: 80
                outputFormat: JPEG
                outputDirPath: the external files directory of your app

                Supported input formats:
                BMP
                GIF
                JPEG
                PNG
                WEBP

                Supported output formats:
                JPEG
                PNG
                WEBP

                Supported quality range: 0~100
                The higher value, the better image quality but larger file size
                PNG, which is a lossless format, will ignore the quality setting


                19


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

                image

                20


                RxGps — еще одна библиотека от Florent Champigny. Легко определяет текущее местоположение. Совместима с RxJava2. Она также автоматически запрашивает разрешения на запуск в режиме реального времени и проверяет, доступны ли вам игровые сервисы.

                21


                MapMe — библиотека для работы с Картами. MapMe упрощая управление маркерами и аннотациями.

                image

                22


                RevelyGradient — библиотека для простого управления градиентом.

                image

                23


                LiteUtilities — библиотека, написанная на Kotlin, которая помогает улучшить ваш код.

                • RecyclerUtils — Упрощает работу с «адаптерами».
                • ScrollUtils — Легко скрыть/показать FloationActionButton при прокрутке при использовании RecyclerView или NestedScrollView.
                • ToastUtils — Создание «тостов».
                • SPUtils — Простой DSL для Shared Preferences.
                • ValidatorUtils — Быстрая и простая проверка текста
                • LogUtils — Легко и просто работать с логами.


                24


                KOIN — фреймворк для внедрения зависимостей.

                image

                25


                koptional — тип Optional для Kotlin.

                26


                Parallax — параллакс View для Android, имитирующий Apple TV App Icons.

                image

                27


                droid-vizu — кастамизированные эффекты визуализации.

                image

                28


                Drone — менеджер библиотеки, предоставленный C'esar Ferreira. Написано в отместку сообществу node.js с их быстрыми и надежными менеджерами зависимостей.

                image

                29


                From-design-to-Android-part2 — крутейшая статья про дизайн для Android.

                image

                30


                Reagent — то место Джейк Уортон (Jake Wharton) проводит эксперименты для будущих библиотекю
                Original source: habrahabr.ru (comments, light).

                https://habrahabr.ru/post/338904/


                Метки:  

                Аналитика в госсекторе: особенности больших систем хранения данных

                Четверг, 28 Сентября 2017 г. 16:26 + в цитатник
                Принято считать, что информационные технологии в государственных ведомствах приживаются тяжелее, и для этого мнения есть ряд объективных причин. Однако, как говорил Альф: «Вы не любите котов? Значит, вы не умеете их готовить!». И сегодня мы хотим поговорить о том, как отличаются проекты в госкомпаниях с точки зрения бизнес-IT интегратора, и для каких целей госы создают большие хранилища для аналитических проектов.

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

                Читать дальше ->

                https://habrahabr.ru/post/338908/


                [Из песочницы] Моделирование смешанных схем на System Verilog

                Четверг, 28 Сентября 2017 г. 15:55 + в цитатник
                AlexTinker вчера в 15:55 Разработка

                Моделирование смешанных схем на System Verilog

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


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


                Так как мы живем в аналоговом мире, то даже цифровая микросхема должна уметь с этим миром общаться. Цифровые микросхемы содержат на кристалле десятки больших аналоговых блоков, таких как АЦП, ЦАП, ФАПЧ, блоки вторичного питания и т.д. Исключением из этого правила, вероятно, являются только большие процессоры, типа Core i и т.п., где все это хозяйство вынесено в чипсет.


                Традиционно для моделирования аналоговых блоков используются spice симуляторы, такие как pi-spice, mmsim, hspice и т.д. В таких симуляторах схема описывается системой дифференциальных уравнений огромной размерности (или матрицей, ее представляющей). Spice симуляторы на каждом шаге вычислений находят решение этой системы уравнений численными методам. Конечно, используются методы ускорения этих вычислений, такие как: разбиение матрицы на подматрицы, распараллеливание на некоторое количество потоков и вычислительных ядер, переменный шаг вычислений и т.д.


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


                Представление аналоговых сигналов


                Для представления аналоговых сигналов хорошо подходит тип с плавающей точкой. В System Verilog это типы shortreal (эквивалентен float в С) и real. Необходимо отметить, что это типы данных с памятью. Значение в них обновляется только в момент присваивания, т.е. это тип аналогичный reg, но в котором запомненное значение представляется не 0 или 1, а напряжением или током, представленном, в свою очередь, в виде числа с плавающей точкой.
                Теперь, нам очень нужен тип аналогичный wire, который обновляется непрерывно, а не только в момент записи. Надо сказать, что в System Verilog такого типа нет. Ходят слухи, что при обсуждении стандарта, было некое движение, дабы вставить в него этот функционал, но оно не реализовалось ни во что конкретное. Тем не менее, если вы используете симулятор ncsim, то в нем есть модификатор var, который делает из типа real и других типов аналог wire. Пример:


                real a;
                var real b;
                
                assign a = in1+in2; //тут будет ошибка
                assign b = in1+in2; // это будет работать, b – всегда будет равно in1+in2

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

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


                Если ваш симулятор не поддерживает var, то можно сделать так:


                real b;
                
                always @( * ) // симулятор входит в этот always при любом изменении in1 или in2
                          b <= in1+in2;

                Запись менее удобная, тем не менее вполне рабочая.


                Преобразование типов данных


                В verilog встроены следующие функции для преобразования данных


                $itor() // integer to real
                $rtoi() // real to integer
                $bitstoreal() //reg [  : ] to real
                $realtobits() // real  to reg [  : ] 

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


                reg [7:0] code;
                int       a;
                real      voltage;
                
                always @( * ) 
                begin
                        a       = {{24{code[7]}}, code[7:0]}; // расширяем знак до размера int 
                        voltage = a;
                end

                Упрощенные модели аналоговых блоков на Verilog


                Усилитель с аддитивным белым шумом


                module amp(input var real in, output real out);
                   parameter k = 10; //коэффициент усиления 
                   parameter seed = 60;
                   parameter noise_power = -20; //мощность шума в dB
                   real noise;
                
                   always @(*)
                   begin
                          noise = $sqrt(10**(noise_power/10))* $itor($dist_normal(seed, 0 , 100_000))/100_000;
                          out   = in * k + noise;
                   end
                endmodule

                ЦАП c ФНЧ


                `timescale 1ns / 1ps
                module DAC(input signed [7:0] DAC_code, output real out);
                
                   parameter fs     = 10e-9;
                   parameter ffilt  = fs/64;    //частота расчета фильтра
                   parameter CUTOFF = 100e6;    //частота среза фильтра
                   parameter a      = ffilt/(ffilt+(1/(2*3.141592* CUTOFF)));
                
                   real DAC_out;
                
                   //ЦАП
                   always @( * ) 
                           DAC_out <= $bitstoint(DAC_code[7:0]);
                
                   //ФНЧ 1го порядка
                    always #(0.5*ffilt)
                           out  <= a* DAC_out + (1-a)*out;
                
                endmodule

                АЦП c учетом нелинейности


                module ADC (input real in, input clk, output reg [7:0] ADC_code)
                  real adc_tf[0:255];
                  real min_dist;
                  int i,j;
                  int dnl_file;
                
                  initial
                  begin   
                      dnl_file=$fopen("DNL_file","r"); 
                      if(dnl_file==0)
                        $stop;     
                      for(i=0;i<256;i=i+1)
                        $fscanf(dnl_file, "%f;", adc_tf[i]);//считываем из файла характеристику АЦП
                  end
                
                  always @(posedge clk) 
                  begin
                    min_dist = 10;
                    for(j=0;j<256; j=j+1) //находим ближайший к входному сигналу код
                           if($abs(in- adc_tf[j]) < min_dist)
                           begin
                                min_dist = delta_abs;
                                ADC_code[7:0]=j;
                           end
                  end
                endmodule

                Многофазный источник тактовой частоты (ФАПЧ)


                module MPLL (input en, input [5:0]phase, output clk_out);
                  parameter REFERENCE_CLOCK_PERIOD=10e-6;
                  parameter PHASES_NUMBER=64;
                
                  reg [PHASES_NUMBER-1:0]PLL_phase=64'h00000000_FFFFFFFF; //ГУН на кольцевом генераторе
                
                  always #(REFERENCE_CLOCK_PERIOD/PHASES_NUMBER)  
                     if(en===1) 
                           PLL_phase[PHASES_NUMBER-1:0] <= {PLL_phase[PHASES_NUMBER-2:0], PLL_phase[PHASES_NUMBER-1]}; //сдвигаем кольцевой генератор по кругу
                
                  assign clk_out = PLL_phase[phase]; //мультиплексор клока 
                endmodule

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


                Еще ускоряемся


                К сожалению, современные системы уже настолько сложны, что данного ускорения бывает недостаточно, в этом случае приходится прибегать к распараллеливанию. Многопоточных Verilog симуляторов, насколько я знаю, пока не изобрели, поэтому придется в рукопашную.
                В SystemVerilog введен новый механизм для обращения к внешним программным модулям – Direct Programming Interface (DPI). Т.к. этот механизм проще, по сравнению с другими двумя, будем использовать его.


                В начале модуля, где мы хотим вызвать внешнюю функцию нужно вставить строчку import.
                import "DPI-C" function int some_funct(input string file_name, input int in, output real out);
                Далее можно в Verilog ее использовать обычным образом, например, так:


                 always @(posedge clk)           
                     res1 <= some_funct (“file.name”, in1, out1);

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


                Пример
                #include 
                typedef struct 
                {
                 //work specific
                   double in; // данные для расчета
                   double out;   //результат расчета
                   …
                 //thread specific
                   char processing;               //флаг разрешения расчета
                   pthread_mutex_t mutex;
                   pthread_cond_t  cond_start;
                   pthread_cond_t  cond_finish;       
                   void *next_th_params;
                   pthread_t tid;
                }th_params;
                
                static th_params th_pool[POOL_SIZE];

                Расчётная функция:


                void* worker_thread(void *x_void_ptr)
                {
                  th_params *x_ptr = (th_params *)x_void_ptr;
                  while(1)  //бесконечный цикл
                  {
                       // ждем поступления новых данных
                      pthread_mutex_lock (&x_ptr->mutex);         //блокируем        
                      x_ptr->processing = 0;                                //Окэй, рэди фор ворк
                      pthread_cond_signal(&x_ptr->cond_finish);  //даем гудок, что закончили
                      while(x_ptr->processing == 0)
                          pthread_cond_wait(&x_ptr->cond_start, &x_ptr->mutex);  //ждем ответного гудка
                      x_ptr->processing = 1;                               //ставим флаг - занят
                      pthread_mutex_unlock(&x_ptr->mutex);     //разблокируем
                      // здесь что-то считаем, вероятно ассемблерная вставка SSE2
                      …
                  }
                }
                

                Функция запуска расчётных функций


                void init(th_params *tp)
                { 
                    int i=0;
                    for(;i<12;i++)
                    {
                        pthread_attr_t attr;
                        pthread_attr_init(&attr);
                        pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
                        pthread_create(th_pool->tid,  &attr, &worker_thread, tp);
                    }
                }

                Функция, раздающая работу расчётным функциям (ее будем вызывать из Verilog постоянно)


                int ch(double in, double *out)
                {   
                   int i;               
                   for(i=0;i<12;i+=1)
                   {
                         //ждем если рабочие функции еще не досчитали
                         pthread_mutex_lock(&th_pool[i].mutex);                                //блокируем
                         while(th_pool[i].processing == 1)      
                                pthread_cond_wait(&th_pool[i].cond_finish, &th_pool[i].mutex); //ждем гудок
                         pthread_mutex_unlock(&th_pool[i].mutex);                            //разблокируем
                   }  
                
                   //присваиваем результаты в массив на выходе для передачи в Verilog
                   for(i=0;i<12;i+=1) 
                        out[i] = th_pool[i].out;
                
                   for(i=0;i<12;i+=1)
                   {
                        pthread_mutex_lock   (&th_pool[i].mutex);       //блокируем     
                        th_pool[i].in          = in;                                   //передаем расчетной функции данные
                        th_pool[i].processing  = 1;                               //ставим флажок разрешения расчета
                        pthread_cond_signal  (&th_pool[i].cond_start);  //даем ответный гудок, что бы проснулась
                        pthread_mutex_unlock (&th_pool[i].mutex);      //разблокируем   
                   }
                }

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


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

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

                https://habrahabr.ru/post/338922/


                DevFest North в Питере уже 30 сентября

                Четверг, 28 Сентября 2017 г. 15:49 + в цитатник
                DaryaGhor сегодня в 15:49 Разработка

                DevFest North в Питере уже 30 сентября

                  Всего два дня осталось до конференции DevFest North, организованной Google Developer Groups Санкт-Петербурга и Петрозаводска. И до 29 сентября 23:59 вы все еще можете купить билеты и стать частью крутого IT-события!
                  Мы уже публиковали описание докладов в предыдущей статье, а сегодня мы решили подробнее познакомить вас с человеком, который проведет открывающий keynote – Royi Benyossef – и взяли у него небольшое интервью.
                  image
                  Royi Benyossef
                  Google Developer Expert Android
                  Samsung Next
                  Тель-Авив

                  Напомним, Royi на DevFest North расскажет про то, как можно использовать возможности вашего устройства (в том числе, его сенсоров) и покажет некоторые приемы UX для того, чтобы улучшить производительность и удобство использования вашего приложения.

                  • Все мы помним проблемы, с которыми сталкивались разработчики приложений для начальных версий Android: неработающие эмуляторы, отсутствие рекомендаций по разработке от Google, недостаточное количество инструментов и другое. You^a€, сотрудником которой ты был, работала с Android-ом еще с первых его версий. Можешь рассказать про свой опыт разработки в это время?

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


                  • Хорошо, что те времена уже прошли! Платформа Android развивается очень быстро. А по-твоему, какие наиболее важные изменения произошли за последние три года?

                  Мой ответ может показаться странным, но я думаю, что самые важные изменения в экосистеме разработки – это релизы support библиотек для тестирования, в частности Espresso. Вероятно, ручное тестирование приложений – это самая долгая и самая дорогая часть разработки. Инструменты для автоматизации помогают значительно сократить зависимость разработчиков от ручного тестирования и позволяют ускорить процесс релиза новых версий приложений (на DevFest North будет отличный доклад про детали процессов тестирования от Богдана Муквича – примеч. ред.)


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

                  Я не очень люблю проводить такие сравнения, но если ты настаиваешь, то могу сказать, что в целом разработка под Android предоставляет больше возможностей, но здесь можно придумать такую метафору, что при разработке под Android у вас больше веревок, однако вы можете неожиданно понять, что они превратились в петлю у вас на шее! С другой стороны, интеграция дизайна в iOS – это более легкий процесс, однако развитие IDE и библиотек (в частности, ConstraintLayout) выравнивает эти платформы.


                  • Жестокая метафора! А что ты думаешь про кроссплатформенные средства разработки? Сможет ли React Native захватить мир?

                  В принципе, если UI/UX приложения не отличается большой требовательностью, то, разумеется, разработка кроссплатформенного приложения будет дешевле (примерно 1,5 стоимости разработки приложения под одну платформу). Поэтому фреймворки как React могут быть весьма полезны.
                  Однако написать кроссплатформенное приложение, UX которого не будет уступать нативным приложениям, очень сложно. Кроме того, таких разработчиков все же ощутимо меньше. Поэтому, если вы хотите как-то сократить расходы, то можно попробовать пойти другим путем. Если нанять очень хороших разработчиков и использовать инструменты автоматизации тестирования (о важности которых я уже говорил до этого), то мы можем сократить время и стоимость ручного тестирования, а также уменьшить число багов. И в конечном итоге это позволит сократить расходы на з/п, которые являются основными в ходе разработки.


                  • А какие рекомендации ты можешь дать начинающим Android-разработчикам, чтобы повысить свои навыки?

                  Честно говоря, я не очень хорошо разбираюсь в теоретическом обучении Android-разработчиков, так как я в основном учился на различных блогах, просматривая код на Github и вопросы на StackOverflow, когда многие современные ресурсы еще не были доступны. Как один из вариантов для начала – это курсы на Udacity, они помогут понять основы и втянуться в разработку. Однако стать действительно хорошим разработчиком можно только после того, как вы долго пробудете не очень хорошим разработчиком :) И других путей здесь нет. Пишите свои проекты, изучайте инструменты, фреймворк Android и библиотеки, общайтесь в чатах и на форумах, становитесь частью сообщества. И после того, как вы сделаете 2-3 хороших проекта, у вас будет как необходимый опыт, так и портфолио.


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

                  Конечно, всегда есть риск “застрять” на определенном уровне, когда текущих знаний более чем достаточно для работы над проектом. Однако нужно всегда стараться быть на волне, для этого проще всего следить за блогами известных разработчиков, таких как Romain Guy, Chet Haas, а также за блогами и выступлениями Google Developer Experts.


                  • И последний вопрос про развитие – как повышать свой уровень опытным разработчикам и как достичь почетного звания сеньора?

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


                  • Спасибо за ответы! Можешь в конце нашей беседы дать какой-то совет всем Android-разработчикам?

                  Сегодня уже очень многие умеют разрабатывать простые Android-приложения, это по сути поставлено на поток. И сейчас идеальное время, чтобы расширять свой кругозор, поработать с бэкендом, облачными сервисами (Google Cloud / Firebase) и машинным обучением, так как будущее заключается именно в этих технологиях. Тем более что возможностей для этого более чем достаточно – мы можем легко использовать сервисы Google, AWS, MS и многие другие.


                  Спасибо Royi за ответы!
                  На DevFest North приглашены еще много классных спикеров, с которыми вы сможете познакомиться на конференции.
                  Мы ждем вас на DevFest North уже 30 сентября в конгрессном центре “ПетроКонгресс”.
                  Конференция пройдет с 10-00 до 19-00.
                  Начало регистрации в 9-00.
                  Подробнее смотрите на сайте DevFest North 2017!
                  Original source: habrahabr.ru (comments, light).

                  https://habrahabr.ru/post/338920/


                  Метки:  

                  Как мы строили свой мини ЦОД. Часть 5 — пережитый опыт, обрывы, жара

                  Четверг, 28 Сентября 2017 г. 15:23 + в цитатник
                  TakeWYN сегодня в 15:23 Администрирование

                  Как мы строили свой мини ЦОД. Часть 5 — пережитый опыт, обрывы, жара

                    Здравствуйте! В прошлых статьях (часть один, два, три, четыре) мы рассказывали о том, как мы строили свой мини дата-центр.

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



                    Как Вы знаете из прошлых статей, нам необходимо было в сжатые сроки «протянуть» свою оптическую линию к новому аплинку, для защиты от DDoS атак (поскольку сервис находился под массированными DDoS атаками конкурентов). Так как делали мы это крайне быстро — то и допустили ряд ошибок, за которые впоследствии расплатились сполна.

                    Первый обрыв


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



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

                    Деревья пилили жители, чтобы освободить дорогу и выехать на работу.



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





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



                    Приступили к работе.





                    Зачищаем оптическое волокно.



                    Свариваем последнюю жилу.



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

                    Второй обрыв


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

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







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

                    Третий обрыв


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

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



                    Нам данная ситуация весьма надоела и мы решили действовать наверняка. Вызвали автовышку и начали крепить кабель так, чтобы его смог сорвать лишь падающий самолёт (не дай бог конечно).









                    Сварка волокна прошла успешно и интернет был подан.

                    Четвертый обрыв


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

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







                    Жара


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

                    В середине лета, в Украине началась аномальная жара. Температура в тени поднималась до 45 градусов по цельсию. У нас стоит мощный, дорогой, канальный кондиционер, который, к сожалению, обслуживался не так часто, как это требовалось. И вот, когда неделю-другую, стояла жара под 45 градусов, а ночью 30 — у нас начались приключения.



                    Сначала, у нас начал «взлетать» ЦОД из-за блейд систем HP C7000. Там стоят куллеры по типу «турбин» и звучат они очень спецефично. Температура была (в самой жаркой точке) около 40 градусов, на блейд-системах около 30.



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



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



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





                    Тест вытяжки, поглощение листа А4:



                    Делали даже так:



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

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

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



                    Признаюсь честно, относился я к этому скептически, да и выглядело глупо. Но спустя час работы температура в дата-центре опустилась с 29-30 до 22-24 градусов! Безусловно, бытовой вентилятор был временной мерой и через 2 дня мы установили вентиляционную систему, которая гоняла воздух по серверной в промышленных масштабах и помогала вытяжке еще лучше. Но вентилятор и совет коллеги, помог нам понять суть проблемы, которую не поняли с десяток фирм-подрядчиков.

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

                    Спасибо за внимание!
                    Original source: habrahabr.ru (comments, light).

                    https://habrahabr.ru/post/337658/


                    Метки:  

                    Какие конференции работают и как туда ездить

                    Четверг, 28 Сентября 2017 г. 14:54 + в цитатник
                    Milfgard сегодня в 14:54 Управление

                    Какие конференции работают и как туда ездить



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

                      Самое приятное – то, что люди, встречаясь лично, делятся ноу-хау и числами. Не знаю, как в других областях, а у нас в рознице все всё сразу узнают. Где-то сделали скидку по аренде в торговом центре – через месяц никто за обычную цену не возьмётся уже. Кто-то из сервисов облажался – репутация на всю жизнь. Кто-то приходит и говорит «А вот мы сделали успешный проект для того-то» — можно прямо взять и позвонить, и выяснится, что третья черепаха врёт. Естественно, это не везде так, но очень четкий тренд есть.

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

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

                      Общий канал


                      У нас принято так: есть письмо примерно на дюжину адресов — те, кому надо быть в курсе внутри компании. Когда кто-то идёт на конференцию, он не просто сидит, а протоколирует, что происходит. Через свою личную точку зрения типа: «Скучное выступление про сервисы доставки, ничего нового», или – «Они сделали удачный проект, можно поспрашивать вот у того-то детали, вот телефон и почта». Но в целом – это тезисы выступления и озвученные идеи. Письмо пишется прямо во время конференции, потом отправляется.

                      Вот примеры выдержек:

                      Сначала я попал на какого-то чувака, который рассказывал про то, что 65% людей отваливаются на оплате картой. Типа, ужас-ужас, денег нет только у 11%, у остальных — блок от фрода, херовый интерфейс, косой 3D-секьюр, пользуйтесь нашим продуктом. Это все заказы с оплатой картой, которые не были сделаны. Самое полезное в выступлении — это факт о том, что 7 лет назад за подтверждением транзакции надо было ходить к банкомату, а сейчас код приходит на телефон.

                      Наш общий друг *** выступает, слегка принял перед выступлением. Ликбезы…

                      Ещё один чувак рассказывает про оперативную реакцию. Каждый заказ на сайте порождает SMS на нокию, прикрученную скотчем в колл-центре. Запищала противно — обновили заказы. Они же по средам анализируют все звонки (среды же) по чеклисту, чтобы понять, когда КЦ расслабился…

                      Выступает ***, SEO. Говорит, по куче товаров до 50% запросов — уникальные. В смысле, что люди ищут редкостную фигню вроде «настольная игра монополия с фигуркой машины». Советует *** для семантики (платная), говорит, видит связанные запросы, которые не видит Яндекс. Я написал, попросил триал. Много говорили про кластеризацию запросов, но я не понял практических выводов…

                      Аааа: «Было слишком много заказов, и нам пришлось закрыть магазин»…

                      Дима, увидишь Артема — скажи, что уходя со стенда, надо забирать личные визитки и вывеску, а не только баннер. Я там постоял, продавал его платформу потом, обещал скидки 80%, раздал оставшиеся визитки.

                      Обсуждение


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

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

                      Недавно мы их пересматривали – и поняли как за 6 лет поменялось мировоозрение. На первых конференциях серьёзных парней мы искренне удивлялись тому, что управляющий каждого магазина знает экономику своего магазина (сейчас у нас так), что развитие концентрируется на регионах присутствия — то есть в новые регионы лезут только при острой необходимости (сейчас у нас тоже так, но путь был тернист), что существует политика, какого типа магазин должен быть первым в регионе – например, не начинать с островка в торговом центре (сейчас у нас тоже так). Что нужно заключать долгосрочные договора с ТЦ — это выгодно всем. Что оплата бывает фикс + % с оборота (тогда мы не знали, а теперь это постоянная практика). Есть куча параметров, котрые неплохо бы измерять (выручка на сотрудника, на метр, и т.д.) — все они нужны.

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

                      Выбор конференции


                      Первоначально критерий выбора был простой – куда нас зовут выступать, там мы и слушаем. Билеты на нормальную конференцию стоят 15-35 тысяч рублей (за день-два), поэтому всё воспринималось как непрофильные расходы. А спикера пускают за так, и ещё кормят.

                      Выяснилось, что конференции бывают двух типов – «лишь бы собрать» и «для пользы дела». Объясню разницу – когда Борис Черток в «Ракеты и люди» рассказывал про конференцию советских ракетчиков, это был второй полезный тип:

                      Для меня эта конференция поначалу представлялась неизбежной потерей времени, отрывающей от работы и писания этих мемуаров. Но по мере разработки программы стало очевидным, что она будет необычной.
                      В программу двух пленарных заседаний и секции «История ракетно-космической техники» были заявлены доклады, содержание которых показалось бы совершено немыслимым еще два-три года назад. Сенсационность докладов, в частности, представленных на исторической секции, состояла в том, что содержание некоторых из них до последних лет имело гриф «совершенно секретно», и любой из них, конечно же, не мог быть доложен аудитории, в которой присутствовали американские ученые и корреспонденты зарубежной прессы…
                      Однако не только за рубежом, но даже среди наших, допущенных к совершенно секретным работам ракетных специалистов очень узкий круг знает о том, что у знаменитой «семерки» был сильный конкурент по доставке ядерного заряда — составная крылатая межконтинентальная ракета «Буря». Летные испытания «Бури» начались раньше, чем полетела Р-7, но были прекращены в 1959 году.
                      Почти никто из наших ракетно-космических специалистов не знает и того, что у современного, всем известного крылатого корабля «Буран» был совершенно секретный, тоже крылатый тезка. Он, не успев сделать и одного полета, был остановлен в производстве после первого успешного полета баллистической «семерки».
                      Назначение космических аппаратов знаменитой серии «Космос» до самого последнего времени оставалось для широкой общественности далеко не ясным. То есть в принципе каждый, кто интересовался космической техникой, понимал, что существуют средства всяческой космической разведки. Мы клеймили американцев, объявляя, что они («ах, какие нехорошие!») запустили очередной «спутник-шпион» для наблюдения за территорией Советского Союза, стран Варшавского договора, наблюдения за «горячими» точками.
                      О своих спутниках аналогичного назначения мы молчали. Ну не так, чтобы совсем: начиная с 1962 года регулярно появлялись сообщения ТАСС, что запущен ИСЗ «Космос №…». Так, например, если взять на выбор 1986 год, по официальным сообщениям, запущены спутники серии «Космос» с № 1715 по № 1810! 95 спутников, которые, согласно сообщениям ТАСС, за некоторыми исключениями, предназначены «для продолжения исследования космического пространства». А всего мы к марту 1992 года довели число таинственных «Космосов» до 2182!
                      В докладах на конференции со значительной части «Космосов» было снято плотное покрывало секретности. Часть «Космосов», в особенности относившиеся к первой тысяче, имели самое непосредственное отношение к деятельности нашего коллектива.

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

                      Мы как-то очень много обсуждали бизнес-планы конференций с Лёхой 23derevo из JUG пару лет назад. Поскольку его конференции самоокупаемые, то есть зависят от продаж билетов и интересности, он очень много времени уделяет не только подбору выступающих, но и их подготовке. За три месяца до конференции – прослушивание доклада, советы. Если надо – тренинги по выступлениям. Если надо – перерисовка презентации в более подходящую под большой экран. И так далее. Там чувствуется работа за год – одна конференция кончилась, сразу уже есть план следующей, и понимание, кто про что будет говорить. Знакомые мне региональные конференции собираются куда позже и второпях. Что меня ещё удивляет – на тяп-ляп конференциях всегда что-то не успевают и жуткая спешка. На нормальных есть всё – от карточки проезда до программы по минутам в духе «13:07 – 13:22 — Иван Иванов из проекта такого-то, вот тезисы». Естественно, по таким куда проще ориентироваться. Ещё в конце часто расшаривают презентации или вообще дают распечатки всего, это тоже бывает жутко удобно.

                      Отдельные примеры хороших открытых конференций – это «Оборота» в Москве, например, «Электронная торговля» в середине октября (чертовски правильные парни, большинство московских советов оттуда), скоро будут «Секреты Сервиса» в Новосибирске, позапрошлый OBR в Екатеринбурге был просто прекрасен. «Оборот» особенно рекомендую – там много космических вещей, которые, возможно, не повторить сразу, но есть возможность и правильно пообщаться, и задать все нужные вопросы в кулуарах, и послушать про тренды. Люди там очень откровенные (настолько, что по результатам выступлений назначили пару выездных проверок как-то), подбор спикеров всегда удачный, доклады отбираются так, чтобы получался хороший эксклюзив плюс инсайдерская информация о компании докладчика. «Электронная торговля» — одна из немногих конференций, откуда мы вытаскиваем огромное количество пунктов ко внедрению в новых письмах. Хорошие делает Redis Retail Club (последнее крутое — Digitale в Петербурге), отличные «Бизнес со смыслом». Интересные вещи бывают у Retail Forum Russia.

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

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

                      Вот примерно так. Так что не сбрасывайте со счётов этот странный формат «живого видео». Он полезный. Но только если не делать как все. Критерий «не как все» — за конференцию уходит 50 визиток. И все по делу.
                      Original source: habrahabr.ru (comments, light).

                      https://habrahabr.ru/post/338528/


                      Метки:  

                      Avamar не копирует дважды

                      Четверг, 28 Сентября 2017 г. 14:51 + в цитатник

                      Метки:  

                      [Из песочницы] Из жизни оборотней

                      Четверг, 28 Сентября 2017 г. 14:44 + в цитатник
                      kate_amell сегодня в 14:44 Управление

                      Из жизни оборотней



                      Привет! Меня зовут Катя. Я будущий разраб-оборотень! Не в смысле, что у меня отрастают клыки и шерсть на спине каждое полнолуние, просто все чаще я думаю о том, что разработка ПО — не совсем мое призвание. Позади три курса в МГТУ им.Н.Э.Баумана. Впереди еще три года учебы. Сейчас экватор…

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

                      Черное или белое?


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



                      Сколько времени вам нужно, чтобы решить какой кофе выпить сегодня? А добавить корицу? Секунд 10? Теперь возьмём популярную видео игру, в которой за 60 секунд нужно из подручных средств собрать комплект в свой постапокалиптический бункер. На мой взгляд, выбор будущей профессии в твои семнадцать сродни этому комплекту — никогда не знаешь, что стоит взять с собой: ружьё или поделку из макарон, которая так дорога твоему сердцу. Отстреливаться от тараканов-мутантов дело, конечно, благородное, но патронов на долго не хватит. И тогда, если вы всё-таки возьмёте своё «творение», то, во-первых, будет возможность предаваться меланхолии о временах, когда на небе сияло солнышко, а не ядерный гриб; и во-вторых, когда закончатся припасы вы не сразу попытаетесь отгрызть ногу товарищу по несчастью, а всё-таки принесёте эту «макаронную» жертву.



                      Итак, у Вас есть 60 секунд для того, чтобы решить — чем вы будете заниматься всю оставшуюся жизнь. Какие варианты? Бонус! Ситуация осложняется тем, что вы молоды и амбициозны, не разочарованы неудачами и считаете, что жизнь — постоянный праздник.



                      Некоторые по-обывательски думают на уровне: «Кто там неплохо зарабатывает и не прилагает особых усилий, туда и пойду». Депутаты? Ну сюда точно не получится. Гос. управление вещь интересная, но скорее всего ты проведёшь лучшие годы в каком-нибудь российском «Париже», стараясь организовать деревенское самоуправление так, чтобы тебе хватало на «Русский стандарт» с «Финляндией», и при этом сосед Петрович не догадался, что-деньги-то на водку из его кармана. Так-так, а кто ещё? Дипломаты? Ну нет, необходимо слишком хорошо учиться, да ещё отправят в какую-нибудь Паттайю, и сиди там, жуй бананы, сидя в кресле, которое аккурат стоит напротив подобия мусорного бака. Так-так, кто там остался? Программисты? Ага, Цукерберг. Так, хорошо. Дуров? Тоже неплохо. Чем они занимаются? Придумали идею, запрограммировали её и вуаля. Деньги не особо пыльные, говорят, можно выучить какие-то языки и дело пойдёт. Да и интернет завален разными статьями «программирование за 21 день». Отлично, решено! Как говорится: «Ставки сделаны! Ставок больше нет!».

                      Невыдуманные истории


                      Итак, вы потратили два года на подготовку к экзаменам. Выбрали направление — software development. Определились с вузом. Поступили. Заветное заявление о зачислении ещё не подписано, а вы уже воображаете, как сидите где-нибудь на берегу Тихого океана за монитором последнего MacBook, и деньги сами по себе прилетают на ваш банковский счёт.

                      Но после первого месяца, когда вы увидели, что программирование ведёт мужчина, который застал пусть и не самую первую модель ENIAC, но все же работал на ней, и считает, что лучший язык в мире — pascal abc, к вам закралось сомнение — а вдруг вы не сможете полететь на Гавайи? Что-то явно пошло не так. Такими темпами до Тихого океана точно не долететь, а скорее всего вы будете сидеть на берегу Москва-реки и писать никому не нужную лабораторную работу, сетуя на то, что жизнь повернулась к вам откровенно не тем местом. Хорошо, этого всего один месяц, дальше будет лучше. Но с каждым днём, неделей, месяцем и соответственно семестром, становится скорее хуже, океан сначала стал морем, потом рекой, а под конец и вовсе превратился в лужу возле дома.

                      Здесь можно обвинить кого и что угодно — от идиотской программы, которая устарела ещё году так в 2010, до преподавательского состава, ведь у большинства в памяти ещё свежи воспоминания о тех блаженных днях, когда скомпилированный код нужно ждать два дня. Хотя были и те, кто, будто «луч света в тёмном царстве» старались не только провести поскорее семинар и уехать домой пить травяной чай, но и заинтересовать. Но как говорится, один в поле не воин. И вот, семестра с 3 я окончательно осознала — это все не мое. Ну не нравится сидеть за экраном монитора по пять часов, чтобы написать программу заливки фигуры методом выборочного пикселя. И это изо дня в день, из года в год. Мысль о том, что все не то и не так бьет молоточком по мозгам.



                      Шаг навстречу неизвестности


                      И что же теперь? Чего же я хочу? Может оставить все, как есть? А если все-таки не суждено мне быть программистом, то кем тогда? Вопрос реально актуальный. Если откатиться немного назад то, мне всегда нравилось общаться с людьми. На фоне этого, у нас в небольшой дружеской компании даже появилась своеобразная игра – «зацепи». Нужно было привлечь внимание человека, на которого укажет жребий. Никаких правил и ограничений. Получалось это у меня достаточно неплохо. Находить общий язык с людьми я могу.

                      Недавно я совершенно случайно наткнулась на статью о пиаре, подумалось: «Эврика! Это же точно мое!». Сначала расстроилась, мол, ну вот, как так, я из программистов в пиарщики пойду? А потом все-таки решила попробовать пройти стажировку. Так я оказалась в PR-департаменте Parallels. С одной стороны — это ИТ-компания, а с другой, есть возможность посмотреть на работу пиарщиков изнутри. Собственно, эту статью можно считать вступительным экзаменом. Надеюсь не сильно потопчитесь на мне в комментариях. Неужели вы с самого детства знали, что будете программистами? А может быть кто-то, также как я, прямо сейчас жалеет о том, что вовремя не перешел из физиков в лирики?



                      Польза и подарочки


                      Знаю, что на Хабре любят всяческую пользу и лайфхаки. Покопалась по теме профориентации и наткнулась на несколько интересных ресурсов. Вот здесь, например, можно бесплатно пройти тест на профориентацию.
                      Original source: habrahabr.ru (comments, light).

                      https://habrahabr.ru/post/338912/


                      Метки:  

                      Поиск сообщений в rss_rss_hh_new
                      Страницы: 1437 ... 1164 1163 [1162] 1161 1160 ..
                      .. 1 Календарь