Русский
Русский
English
Статистика
Реклама

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

Выбор архитектурного стиля (часть 2)

17.09.2020 12:16:31 | Автор: admin
Привет, хабр. Сегодня я продолжаю серию публикаций, которую написал специально к старту нового потока курса Software Architect.



Введение


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

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

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

Компонентно-ориентированная архитектура


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

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

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

Самая главная проблема такого монолита заключается в том, что разделение на модули является чисто логическим и может быть легко нарушено разработчиками. Может появиться модуль core, который постепенно превращается в помойку, может расти граф зависимостей между модулями и так далее. Для избежания таких проблем разработка должна вестить либо очень зрелой командой, либо под руководством архитектора, который на full time занимается code review и бьет нарушающих логическую структуру разработчиков по рукам.

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

Сервис-ориентированная архитектура


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

Сервис-ориентированная архитектура (SOA = service oriented architecture) решает все обозначенные проблемы монолита: при изменении затрагивается только одна служба, а четко определенный API поддерживает хорошую инкапсуляцию компонент.

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

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

Сервис-ориентированная архитектура неплохо поддерживается архитектурным коммьюнити и вендорами. Отсюда следует наличие множества курсов и сертификаций, хорошо проработанных паттернов. К последним относится, например, не безызвестная сервисная шина предприятия (ESB = enterprise service bus). При этом ESB это багаж от вендоров, она не обязательно должна использоваться в SOA.

Пик популярности сервис-ориентированной архитектуры приходился примерно на 2008 год, после чего она пошла на спад, который стал существенно более резким после появления микросервисов (~2015 год).

Заключение


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

Подробнее..

Паттерн сага как способ обеспечения консистентности данных

18.09.2020 14:13:41 | Автор: admin
Всем привет. Уже сейчас в OTUS открывает набор в новую группу курса Highload Architect. В связи с этим я продолжаю серию своих публикаций, написанных специально для этого курса, а также приглашаю вас на свой бесплатный демо урок по теме: Индексы в MySQL: best practices и подводные камни. Записаться на вебинар можно тут.



Введение


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

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

Паттерн Сага


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

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

Типов транзакций в саге несколько, целых четыре:

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

Организовывать сагу можно с помощью хореографии или оркестрации.

В случае с хореографической саги выделенный оркестратор отсутствует. На примере сервиса заказов и пользователей она может выглядеть так: сервис заказов получает запрос и создает заказ в состоянии PENDING, а затем публикует событие Заказ создан. Обработчик событий в сервисе пользователей обрабатывает данное событие, пытается зарезервировать товар и публикует результат в виде события. Сервис заказов обрабывает данное событие, подтверждая или отменяя заказ в зависимости от прочитанного результата.

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

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

Сага позволяет добиться ACD-модели (Atomicity + Consistency + Durability в терминах ACID), но одну букву мы потеряли. Недостаток буквы I приводит к известным проблемам недостатка изолированности. К ним относятся: потерянные обновления (lost updates) одна сага перезаписывает изменения, внесенные другой, не читая их при этом, грязное чтение (dirty reads) транзакция или сага читают незавершенные обновления другой саги, нечеткое/неповторяемое чтение (fuzzy/nonrepeatable reads) два разных этапа саги читают одни и те же данные, но получают разные результаты, потому что другая сага внесла изменения. Существует ряд паттернов, позволяющих пофиксить те или иные аномалии: семантическая блокировка, коммутативные обновления, пессимистическое представление, повторное чтение значения, файл изменений и по значению. Вопрос обеспечения изоляции остается открытым.

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

Заключение


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

Подробнее..

Выбор архитектурного стиля (часть 3)

28.09.2020 02:15:18 | Автор: admin
Привет, Хабр. Сегодня я продолжаю серию публикаций, которую написал специально к старту нового потока курса Software Architect.



Введение


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

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

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

Отношение архитектур


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

Характеристики микросервисной архитектуры


Основными характеристиками микросервисной архитектуры являются:

Организация в соответствии с бизнес-возможностями
(Organized around Business Capabilities)
Продукты, а не проекты (Products not Projects)
Умные точки входа и глупые каналы (Smart endpoints and
dumb pipes)
Децентрализованное управление (Decentralized Governance)
Децентрализованное управление данными (Decentralized
Data Management)
Автоматизация инфраструктуры (Infrastructure Automation)
Страховка от сбоев (Design for failure)
Архитектура с эволюционным развитием (Evolutionary
Design)

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

Организация в соответствии с бизнес-возможностями (Organized around Business Capabilities)


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

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

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

Продукты, а не проекты (Products not Projects)


Проектный подход при котором команда передает разработанную функциональность другим командам в случае микросервисной архитектуры совершенно не подходит. Команда должна поддерживать систему на протяжении всего ее жизненного цикла. Компания Amazon, один из флагманов внедрения микросервисов, заявляла: вы создаете продукт, и вы же запускаете его (you build, you run it). Продуктовый подход позволяет команде почувствовать потребности бизнеса.

Умные точки входа и глупые каналы (Smart endpoints and dumb pipes)


SOA архитектура большое внимание уделяла каналам связи, в частности Enterprise Service Bus (сервисная шина предприятия). Что зачастую приводит к Erroneous Spaghetti Box, то есть сложность монолита переходит в сложность связей между сервисами. В микросевисной архитектуре используются только простые способы взаимодействия.

Децентрализованное управление (Decentralized Governance)


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

Децентрализованное управление данными (Decentralized Data Management)


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

Автоматизация инфраструктуры (Infrastructure Automation)


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

Страховка от сбоев (Design for failure)


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

Архитектура с эволюционным развитием (Evolutionary Design)


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

Заключение


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



Читать часть 2
Подробнее..

Как построить надежное приложение на базе Event sourcing?

15.09.2020 14:04:30 | Автор: admin

Привет! В этой статье я хочу рассказать, как из модного микросервисного приложения можно сделать рабочую, управляемую систему с помощью трех проверенных годами методик: на примере проекта внутренней performance-based рекламы Joom.



The Project


Проект JoomAds предлагает продавцам инструменты продвижения товаров в Joom. Для продавца процесс продвижения начинается с создания рекламной кампании, которая состоит из:


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

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



Рис. 1


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


JoomAds API может изменять часть состояния при регистрации покупок успешно прорекламированных товаров, корректируя остаток бюджета рекламных кампаний (Рис. 1). Настройками кампаний управляет сервис кампаний JoomAds Campaign, метаданными продукта сервис Inventory, данные ранжирования расположены в хранилище аналитики (Рис. 2).


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



Рис. 2
JoomAds API выступает в роли медиатора данной микросервисной системы.


Pure Microservices equals Problems


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


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


Быстродействие


Любые внешние коммуникации (например, поход за метаданными товара в Inventory) это дополнительные накладные расходы, увеличивающие время ответа медиатора. Такие расходы не проблема на ранних этапах развития проекта: последовательные походы в JoomAds Campaign, Inventory и хранилище аналитики вносили небольшой вклад во время ответа JoomAds API, т.к. количество рекламируемых товаров было небольшим, а рекламная выдача присутствовала только в разделе Лучшее.


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


Например, поиск товаров Inventory не был рассчитан на высокие частоты запросов, но он нам нужен именно таким.


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


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


Отказ любой зависимости JoomAds API ведет к некорректной или неповторяемой рекламной выдаче, либо к ее полному отсутствию.


Сложность поддержки


Микросервисная архитектура позволяет снизить сложность поддержки узкоспециализированных приложений, таких как Inventory, но значительно усложняет разработку приложений-медиаторов, таких как JoomAds API.


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


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


Эти наблюдения привели нас к осознанию необходимости изменений. Новый JoomAds должен генерировать экономически эффективную и согласованную рекламную выдачу при отказе JoomAds Campaign, Inventory или хранилища аналитики, а также иметь предсказуемое быстродействие и отвечать на входящие запросы быстрее 100 мс в 95% случаев.


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


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


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


Monolith over microservices (kind of)


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


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


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


Materialization


Совместить лучшие качества микросервисов и монолитной архитектуры нам позволил подход, именуемый Materialized View. Материализованные представления часто встречаются в реализациях СУБД. Основной целью их внедрения является оптимизация доступа к данным на чтение при выполнении конкретных запросов.


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


Например, для запросов состояния продукта по его идентификатору (см. Рис. 3) или запросов состояния множества продуктов по идентификатору рекламной кампании.



Рис. 3


Материализованное представление данных расположено во внутреннем хранилище JoomAds API, поэтому замыкание входящих коммуникаций на него положительно сказывается на производительности и отказоустойчивости системы, т.к. доступ на чтение теперь зависит только от доступности / производительности хранилища данных JoomAds, а не от аналогичных характеристик внешних ресурсов. JoomAds API является надежным монолитным приложением!


Но как обновлять данные Materialized View?


Data Sourcing


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


  • Изолировать клиентскую сторону от проблем доступа к внешним ресурсам.
  • Учитывать возможность высокого времени ответа компонентов инфраструктуры JoomAds.
  • Предоставлять механизм восстановления на случай утраты текущего состояния Materialized View.

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


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


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


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


Event Sourcing


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


В результате адаптации Event Sourcing подхода в инфраструктуре JoomAds появились три новых компонента: хранилище материализованного представления (MAT View Storage), конвейер материализации (Materialization Pipeline), а так же конвейер ранжирования (Ranking Pipeline), реализующий поточное вычисление потоварных score'ов ранжирования (см. Рис. 4).



Рис. 4


Discussion, Technologies


Materialized View и Event Sourcing позволили нам решить основные проблемы ранней архитектуры проекта JoomAds.


Специализированные Materialized View значительно повысили надежность и быстродействие клиентских запросов. Обновление данных с использованием Event Sourcing подхода повысило надежность коммуникации с внешними сервисами, предоставило инструменты контроля консистентности данных и позволило избавиться от неэффективных запросов к внешним ресурсам.


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


В нашем случае MAT View целиком хранится в одной таблице Cassandra: добавление колонок в таблицы Cassandra безболезненная операция, удаление MAT View осуществляется удалением таблицы. Таким образом, крайне важно выбрать удачное хранилище для реализации Materialized View в вашем проекте.


Event Sourcing предъявляет серьезные требования к своим пользователям. Генерация событий изменения данных во всех интересующих подсистемах в заданном формате с возможностью установления хронологического порядка следования это сложная организационно-техническая задача, которую крайне трудно реализовать в середине жизненного цикла ПО. Можно назвать удачей, что хранилище данных Inventory уже имело функцию генерации событий на обновление метаданных продуктов.


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


Вместо этого мы воспользовались популярными open-source решениями, развивающимися при участии Apache Software Foundation: Apache Kafka и Apache Flink.


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


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


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


Takeaway


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


P.S. Этот пост был впервые опубликован в блоге Joom на vc, вы могли его встречать там. Так делать можно.

Подробнее..

Перевод Текстовый индекс по котировкам в памяти на Go

09.09.2020 14:04:11 | Автор: admin

Недавно понадобилось реализовать поиск по началу строки, по сути WHERE name LIKE 'начало%'. Это был поиск по названию биржевых символов (AAPL, AMZN, EUR/USD и пр.). Хотелось, чтобы поиск работал быстро, и не нагружал лишний раз БД. В итоге пришел к реализации поиска по дереву в памяти, об этом и расскажу.

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

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

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

Структура дерева

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

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

  • AAA (BetaShares Australian High Interest Cash ETF, ASX),

  • AAA (All Active Asset Capital LTD, LSE).

Тогда мы запишем в Index.Data одну запись, внутри которой в Index.Data будет список из двух символов AAA.

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

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

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

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

  1. Все разделители заменяются на пробелы.

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

  3. Обрезаются пробелы по краям строк.

  4. Строки переводятся в нижний регистр.

  5. Схожие символы приводятся к единому формату, например, a.

  6. Исключаются стоп-слова (опционально).

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

Затем отсортированные данные группируются по ключу, чтобы положить в Index.Data готовый список.

Теперь все готово для добавления данных в индекс. Наша задача теперь сформировать Index.Children таким образом, чтобы на каждом уровне дерева лежал очередной кусочек ключа (в нашем случае кусочек ключа это символ индексируемой строки). Например, если мы добавляем в индекс символ AAPL, то мы сформируем такую древовидную структуру (красная линия):

Тут на красной линии расположены на каждой вершине дерева элементы [A], [A], [P], [L]. В квадратных скобках обозначены ключи, которые используются на каждой вершине для индексации. Без скобок обозначены полные ключи, которые получаются при проходе от корня до вершины.

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

Таким же образом последовательно добавляем в индекс все значения.

Поиск по дереву

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

  1. Выполним предварительную обработку искомой строки тем же алгоритмом как обрабатывали ключи перед индексацией (см. выше).

  2. Найдем в дереве элемент, в котором ключ совпадает с искомой строкой.

  3. Далее последовательным перебором дочерних вершин получаем искомый результат.

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

  • АА

  • ААА

  • АAAL

  • AAALF

  • AAAP

  • AAB

  • AAP

  • AAPJ

  • AAPL

  • AAPT

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

Поиск подходящего биржевого символа

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

  • Если вводят в поиске EUR, то в выдаче должны быть EUR, EUR/USD, USD/EUR. То есть поиск должен работать не только с начала строки, но и с начала каждого слова в строке.

  • Поиск должен работать не только по названию символа, но также и по названию компании. Например, при вводе в поиске APL, надо выдать в результатах APL, AAPL (Apple).

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

Чтобы для EUR, в выдачу попали не только EUR, EUR/USD, но и USD/EUR, решил класть в индекс по несколько экземпляров значений с разными ключами: подстроки начиная с каждого слова индексируемой строки. Например, при индексации строки USD/EUR, в индекс попадают следующие ключи: usd eur, eur. При индексации строки Grupo Financiero Galicia SA в индекс попадают ключи Grupo Financiero Galicia SA, Financiero Galicia SA, Galicia SA, SA.

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

  1. Поиск символов по точному совпадению с искомой строкой.

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

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

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

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

  1. Индекс SearchSymbolIndex по символам со всех финансовых рынков.

  2. Индекс SearchPopularIndex только по популярным символам (10% от всех).

  3. Индекс SearchInstrumentIndex по названию компании.

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

var searchedData []searchindex.SearchDatasearchedData = r.SearchSymbolIndex.Search(searchindex.SearchParams{    Text: key,    OutputSize: outputSize,    Matching: searchsymbol.Strict,})searchedData = r.SearchPopularIndex.Search(searchindex.SearchParams{    Text: key,    OutputSize: outputSize,    Matching: searchsymbol.Beginning,    StartValues: searchedData,})searchedData = r.SearchSymbolIndex.Search(searchindex.SearchParams{    Text: key,    OutputSize: outputSize,    Matching: searchindex.Beginning,    StartValues: searchedData,})searchedData = r.SearchInstrumentIndex.Search(searchindex.SearchParams{    Text: key,    OutputSize: outputSize,    Matching: searchindex.Beginning,    StartValues: searchedData,})

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

searchindex.Strict поиск точных совпадений.

searchindex.Beginning поиск совпадений по началу строки.

Итого

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

Больших бенчмарков по производительности не делал, но на моих данных в 55000 строк создание трех индексов занимает примерно 2 секунды, это с учетом выборки из БД и дополнительных действий. А поиск в 4 последовательные итерации в трех индексах выполняется за 100-200 наносекунд (это если исключить время на обработку http запроса и считать только время поиска), что для моей задачи более чем достаточно.

Код в виде готового пакета: https://github.com/twelvedata/searchindex

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

Подробнее..

Разработка python module, чтобы продакшн радовал

10.09.2020 16:11:14 | Автор: admin
Всем привет! Я представляю команду разработчиков некоммерческой организации CyberDuckNinja. Мы создаём и поддерживаем целое семейство продуктов, которые позволяют облегчить разработку backend-приложений и сервисов машинного обучения.
Сегодня хотелось бы затронуть тему интеграции Python в C++.



Все началось со звонка друга в два часа ночи, который пожаловался: У нас под нагрузкой ложится продакшн ... В разговоре выяснилось, что код продакшена написан с использованием ipyparallel (пакет Python, который позволяет производить параллельные и распределённые вычисления) для обсчета модели и получения результатов в режиме онлайн. Мы решили разобраться в архитектуре ipyparallel и провести профайлинг под нагрузкой.

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

Приятной особенностью оказалось, что эти модули взаимодействуют через pyzmq. Благодаря хорошей архитектуре engine удалось заменить реализацию сетевого взаимодействия на наше решение, построенное на cppzmq. Эта замена открывает бесконечный простор для разработки: ответную часть можно написать в C++ части приложения.

Это позволило сделать пулы engine теоретически еще шустрее, но всё-таки не решило задачи с интеграцией библиотек внутрь Python-кода. Если для интеграции своей библиотеки придется сделать слишком много, то такое решение не будет востребовано и останется на полке. Оставался вопрос, как нативно внедрить наши наработки в текущую кодовую базу engine.

Нам нужны были какие-то разумные критерии, чтобы понять, какой выбрать подход: лёгкость разработки, декларирование API только внутри C++, отсутствие дополнительных обёрток внутри Python или нативность использования всей мощности библиотек. А чтобы не запутаться в нативных (и не очень) способах протаскивания С++ кода в Python, мы сделали небольшой ресёрч. На момент начала 2019 года в интернете можно было найти четыре популярных способа расширения Python:
  1. Ctypes.
  2. CFFI.
  3. Cython.
  4. CPython API.

Мы рассмотрели все варианты интеграции.

1. Ctypes


Ctypes это Foreign Function Interface, позволяющий загружать динамические библиотеки, которые экспортируют интерфейс на языке Cи. С его помощью можно пользоваться из Python библиотеками на Cи, например, libev, libpq.
Например, есть библиотека написанная на языке C++, имеющая интерфейс:
extern "C"{    Foo* Foo_new();    void Foo_bar(Foo* foo);}

Пишем к нему обёртку:
import ctypeslib = ctypes.cdll.LoadLibrary('./libfoo.so')class Foo:    def __init__(self) -> None:        super().__init__()        lib.Foo_new.argtypes = []        lib.Foo_new.restype = ctypes.c_void_p        lib.Foo_bar.argtypes = []        lib.Foo_bar.restype = ctypes.c_void_p        self.obj = lib.Foo_new()    def bar(self) -> None:        lib.Foo_bar(self.obj)

Делаем выводы:
  1. Невозможность взаимодействия с API интерпретатора. Ctypes является способом взаимодействия с Cи библиотеками на стороне Python, но не предоставляет способ взаимодействия C/C++ кода с Python.
  2. Экспортирование интерфейса в стиле Cи. Сtypes может взаимодействовать с ABI библиотеками этом в стиле, но любой другой язык должен экспортировать свои переменные, функции, методы через Cи-обёртку.
  3. Необходимость написание обёрток. Их приходится писать как на стороне C++ кода для совместимости ABI с Си, так и на стороне Python, чтобы уменьшить количество boilerplate кода.

Сtypes нам не подходит, пробуем следующий способ CFFI.

2. CFFI


CFFI аналогичен Ctypes, но имеет некоторые дополнительные возможности. Продемонстрируем пример с той же библиотекой:
import cffiffi = cffi.FFI()ffi.cdef("""    Foo* Foo_new();    void Foo_bar(Foo* foo);""")lib = ffi.dlopen("./libfoo.so")class Foo:    def __init__(self) -> None:        super().__init__()        self.obj = lib.Foo_new()    def bar(self) -> None:        lib.Foo_bar(self.obj)

Делаем выводы:
У CFFI всё те же минусы, за исключением того, что обёртки становятся немного жирнее, так как требуется указать библиотеке определение её интерфейса. CFFI тоже не подходит, перейдём к следующему способу Cython.

3. Cython


Cython это саб/мета язык программирования, позволяющий писать расширения на смеси C/C++ и Python и загружать результат в виде динамической библиотеки. На этот раз есть библиотека, написанная на языке C++ и имеющая интерфейс:
#ifndef RECTANGLE_H#define RECTANGLE_Hnamespace shapes {    class Rectangle {        public:            int x0, y0, x1, y1;            Rectangle();            Rectangle(int x0, int y0, int x1, int y1);            ~Rectangle();            int getArea();            void getSize(int* width, int* height);            void move(int dx, int dy);    };}#endif

Тогда определяем этот интерфейс на языке Cython:
cdef extern from "Rectangle.cpp":    pass# Declare the class with cdefcdef extern from "Rectangle.h" namespace "shapes":    cdef cppclass Rectangle:        Rectangle() except +        Rectangle(int, int, int, int) except +        int x0, y0, x1, y1        int getArea()        void getSize(int* width, int* height)        void move(int, int)

И пишем к нему обёртку:
# distutils: language = c++from Rectangle cimport Rectanglecdef class PyRectangle:    cdef Rectangle c_rect    def __cinit__(self, int x0, int y0, int x1, int y1):        self.c_rect = Rectangle(x0, y0, x1, y1)    def get_area(self):        return self.c_rect.getArea()    def get_size(self):        cdef int width, height        self.c_rect.getSize(&width, &height)        return width, height    def move(self, dx, dy):        self.c_rect.move(dx, dy)    # Attribute access    @property    def x0(self):        return self.c_rect.x0    @x0.setter    def x0(self, x0):        self.c_rect.x0 = x0    # Attribute access    @property    def x1(self):        return self.c_rect.x1    @x1.setter    def x1(self, x1):        self.c_rect.x1 = x1    # Attribute access    @property    def y0(self):        return self.c_rect.y0    @y0.setter    def y0(self, y0):        self.c_rect.y0 = y0    # Attribute access    @property    def y1(self):        return self.c_rect.y1    @y1.setter    def y1(self, y1):        self.c_rect.y1 = y1

Теперь можем использовать этот класс из обычного Python-кода:
import rectx0, y0, x1, y1 = 1, 2, 3, 4rect_obj = rect.PyRectangle(x0, y0, x1, y1)print(dir(rect_obj))

Делаем выводы:
  1. При использовании Cython всё также приходится писать обёрточный код на стороне C++, но уже не нужно экспортировать интерфейс в стиле Cи.
  2. По-прежнему нельзя взаимодействовать с интерпретатором.

Остаётся последний способ CPython API. Пробуем его.

4. CPython API


CPython API API, которое позволяет разрабатывать модули для интерпретатора Python на C++. Лучше всего использовать pybind11, высокоуровневую библиотеку на С++, которая делает работу с CPython API удобной. С её помощью можно легко экспортировать функции, классы, преобразовать данные между памятью python и нативной памятью в С++.

Итак, возьмём код из предыдущего примера и напишем к нему обёртку:
PYBIND11_MODULE(rect, m) {    py::class_<Rectangle>(m, "PyRectangle")        .def(py::init<>())        .def(py::init<int, int, int, int>())        .def("getArea", &Rectangle::getArea)        .def("getSize", [](Rectangle &rect) -> std::tuple<int, int> {            int width, height;            rect.getSize(&width, &height);            return std::make_tuple(width, height);        })        .def("move", &Rectangle::move)        .def_readwrite("x0", &Rectangle::x0)        .def_readwrite("x1", &Rectangle::x1)        .def_readwrite("y0", &Rectangle::y0)        .def_readwrite("y1", &Rectangle::y1);}

Обёртку написали, теперь ее надо собрать в бинарную библиотеку. Нам потребуются две вещи: система сборки и пакетный менеджер. Возьмём для этих целей CMake и Conan соответственно.

Чтобы сборка на Conan заработала, надо установить сам Conan подходящих способом:
pip3 install conan cmake

и прописать дополнительные репозитории:
conan remote add bincrafters https://api.bintray.com/conan/bincrafters/public-conanconan remote add cyberduckninja https://api.bintray.com/conan/cyberduckninja/conan

Опишем в файле conanfile.txt зависимости проекта на библиотеку pybind:
[requires]pybind11/2.3.0@conan/stable[generators]cmake

Добавим файл CMake. Обратите внимание на включенную интеграцию с Conan при выполнении CMake будет запущена команда conan install, устанавливающая зависимости и формирующая переменные CMake с данными о зависимостях:
cmake_minimum_required(VERSION 3.17)set(project rectangle)set(CMAKE_CXX_STANDARD 17)set(CMAKE_CXX_STANDARD_REQUIRED YES)set(CMAKE_CXX_EXTENSIONS OFF)if (NOT EXISTS "${CMAKE_BINARY_DIR}/conan.cmake")    message(STATUS "Downloading conan.cmake from https://github.com/conan-io/cmake-conan")    file(DOWNLOAD "https://raw.githubusercontent.com/conan-io/cmake-conan/v0.15/conan.cmake" "${CMAKE_BINARY_DIR}/conan.cmake")endif ()set(CONAN_SYSTEM_INCLUDES "On")include(${CMAKE_BINARY_DIR}/conan.cmake)conan_cmake_run(        CONANFILE conanfile.txt        BASIC_SETUP        BUILD missing        NO_OUTPUT_DIRS)find_package(Python3 COMPONENTS Interpreter Development)include_directories(${PYTHON_INCLUDE_DIRS})include_directories(${Python3_INCLUDE_DIRS})find_package(pybind11 REQUIRED)pybind11_add_module(${PROJECT_NAME} main.cpp )target_include_directories(    ${PROJECT_NAME}    PRIVATE    ${NUMPY_ROOT}/include    ${PROJECT_SOURCE_DIR}/vendor/General_NetSDK_Eng_Linux64_IS_V3.051    ${PROJECT_SOURCE_DIR}/vendor/ffmpeg4.2.1)target_link_libraries(    ${PROJECT_NAME}    PRIVATE    ${CONAN_LIBS})

Все приготовления выполнены, давайте собирать:
cmake . -DCMAKE_BUILD_TYPE=Release cmake --build . --parallel 2

Делаем выводы:
  1. Мы получили собранную бинарную библиотеку, которую можно впоследствии загрузить в интепретатор Python его средствами.
  2. Стало гораздо проще экспортировать код в Python по сравнению со способами выше, а обёрточный код стал компактнее и пишется на том же языке.

Одна из возможностей cpython/pybind11 это загрузка, получение или выполнение функции из runtime python, находясь в рантайме С++ и наоборот
Давайте посмотрим на простом примере:
#include <pybind11/embed.h>  // подключаем  работу с интерпретаторомnamespace py = pybind11;int main() {    py::scoped_interpreter guard{}; // инициализируем python vm    py::print("Hello, World!"); // печатаем  на консоль Hello, World!}

Скомбинировав возможность встраивать интерпретатор python в приложение на С++ и механизм Python модулей, мы придумали интересный подход, при помощи которого код ipyparalles engine не чувствует подмену компонентов. Для приложений мы выбрали архитектуру, в которой жизненные и событийные циклы начинаются в коде на C++, а уже потом стартует интерпретатор Python в рамках того же процесса.

Для понимания давайте разберём, как работает наш подход:
#include <pybind11/embed.h>#include "pyrectangle.hpp" // подключаем С++ реализацию rectangleusing namespace py::literals;//  с помощью этого встроенного  скрипта  загружаем собранный модуль rectangleconstexpr static char init_script[] = R"__(    import sys    sys.modules['rect'] = rect)__";//  с помощью этого встроенного  скрипта  загружаем пользовательский  код rectangleconstexpr static char load_script[] = R"__(    import sys, os    from importlib import import_module    sys.path.insert(0, os.path.dirname(path))    module_name, _ = os.path.splitext(path)    import_module(os.path.basename(module_name)))__";int main() {    py::scoped_interpreter guard; //инициализируем интерпретатор     py::module pyrectangle("rect");  создаем модуль     add_pyrectangle(pyrectangle); //инстанируем расширение модуля    py::exec(init_script, py::globals(), py::dict("rect"_a = pyrectangle)); //делаем это расширение доступным для импорта из кода Python.    py::exec(load_script, py::globals(), py::dict("path"_a = "main.py")); //запускаем скрипт main.py    return 0;}

В приведенном выше примере модуль pyrectangle пробрасывается в интерпретатор Python и становится доступным для импорта под именем rect. Продемонстрируем на примере, что для пользовательского кода ничего не поменялось:
from pprint import pprintfrom rect import PyRectangler = PyRectangle(0, 3, 5, 8)pprint(r)assert r.getArea() == 25width, height = r.getSize()assert width == 5 and height == 5

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

Таким образом, ctypes и CFFI для нас не подходят из-за необходимости экспорта интерфейсов библиотеки в стиле Cи, а также из-за необходимости писать обёртки на стороне Python и, в конечном итоге, использования CPython API, если необходимо встраивание. Cython лишён недостатка с экспортом, но сохраняет все остальные недостатки. Pybind11 поддерживает возможность встраивания и написания обёрток только на стороне С++. Также он имеет широкие возможности для управления структурами данных и вызова функций и методов Python. В итоге мы остановились на pybind11 как на высокоуровневой обертке на C++ для CPython API.

Скомбинировав применение embed python внутри C++ приложения с механизмом модулей для быстрых пробросов данных и переиспользовав кодовую базу ipyparallel engine, мы получили rocketjoe_engine. Он идентичен по механикам с оригиналом и работает шустрее за счет уменьшения кастов на сетевые взаимодействия, обработку json и другие промежуточные действия. Теперь это позволяет держать нагрузки на продакшене у моего друга, за что я и получил первую звездочку в проекте GitHub.

Если вы заинтересовались пакетным менеджером Conan, то узнать о нем больше можно на предстоящей конференции Moscow Python Week в докладе про пакетирование проектов на C++, а также про особенности разработки на Python и самого пакетного менеджера Conan вместе с его инфраструктурой.

Moscow Python Week стартует уже через 4 дня она будет с 14 по 17 сентября. Программа готова, и ещё на конференции пройдёт первый Чемпионат России по Python: можно проверить уровень своего мастерства и получить независимую оценку своих скиллов среди Python-разработчиков всей страны. Участие бесплатное, но надо знать стандартную библиотеку Python.
Билеты на саму конференцию здесь.
Подробнее..

Проекты Центра разработки Intel в России. Intel Integrated Performance Primitives

22.09.2020 10:23:41 | Автор: admin
Наш рассказ об очередном проекте Intel, сделанном в России. Это библиотека Intel Integrated Performance Primitives набор готовых к употреблению, высоко оптимизированных под различные архитектуры Intel, к тому же совершенно бесплатных базовых функций для работы с изображениями, сигналами и произвольными данными. Каким образом зародился этот проект, как развивался, что происходит в Intel IPP сейчас в статье под катом. А начнем мы, как это принято в резюме, с современности.

На КДПВ вход на этаж IPP в офисе Intel в Нижнем Новгороде

Что такое Intel IPP сейчас


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

  • Обработка изображений;
  • Сжатие данных;
  • Обработка сигналов;
  • Криптография.

Текущая версия Intel IPP 2020 Update 2 содержит более 2 500 примитивов обработки изображений, 1 300 примитивов обработки сигналов, 500 компьютерного зрения и 300 криптографии.

Библиотека постоянно совершенствуется оптимизируется под новые платформы, добавляется новая функциональность и неизбежно удаляется старая, малоиспользуемая.
Intel IPP работает на любом устройстве с х86 архитектурой под управлением Linux, macOS, Windows и Android. То есть, поддерживаются процессоры не только Intel, но и других производителей, причём, работает IPP на них быстро, хотя, конечно, и не так супербыстро, как на устройствах Intel.

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

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

Для интересующихся скоростью работы на Intel Xeon и Core некоторые бенчмарки.

В настоящее время IPP доступна в составе Intel Parallel Studio XE, Intel System Studio, и просто сама по себе. И абсолютно бесплатно для персонального и коммерческого использования.

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

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

Все компоненты библиотеки используются миллионами пользователей в сотнях тысяч приложений по всему миру. В том числе и внутри самой компании в различных подразделениях Intel. Intel IPP обеспечивает значительное ускорение работы библиотеки OpenCV. Кстати, специальная сборка Intel IPP c функциями, используемыми OpenCV, выпущенная в 2015 году, стала первой бесплатной свободно распространяемой версией IPP.

Intel IPP можно встретить в приложениях для распознавания и улучшения изображений во всех областях, включая медицину; принтерах, в том числе и 3D; цифровом видеонаблюдении; автономных автомобилях; распознавании и синтезе речи; сжатии данных; телекоммуникации и играх. IPP работает везде от серверов до носимых устройств. Можно утверждать, что если бы была изобретена машина времени, то ее софт обязательно использовал бы Intel IPP.

История IPP


Начиналось всё с библиотек Intel по обработке сигналов (SPL) и обработки изображений (IPL), разрабатывавшихся по заказу Intel в федеральном ядерном центре ВНИИЭФ в Сарове (мы писали про это в рассказе про OpenCV).

В 1996 (или 1997 году по свидетельствам разных очевидцев) в головном офисе Intel Санта Клара прошло совещание по поводу дальнейших планов развития SPL и IPL с участием американских кураторов проекта и приглашенных специалистов из Сарова, среди которых был будущий архитектор, вдохновитель и руководитель команды IPP Борис Сабанин, а также Сергей Кириллов, в настоящее время возглавляющий работу над IPP-криптографией.

Саровская команда привезла свой список предложений, и одним из них было открытие для пользователей интерфейсов низкоуровневых функций IPL и SPL, так как они все равно уже были реализованы и оптимизированы, меж тем некоторым пользователям форматы данных IPL были неудобны, у них в готовых продуктах уже были свои сложившиеся форматы изображений. Предлагаемый прототип интерфейса IPP, использующий более простые в сравнении с IPL/ISL структуры, был создан Борисом Сабаниным в ходе обсуждения буквально на салфетке. Но в то время предложение российской стороны хотя и не было отвергнуто, но и не получило особой поддержки оно оказалось в середине списка с малым приоритетом. Но через пару лет кто-то в Intel вспомнил об этом (скорее всего, Шин Ли, впоследствии ставший евангелистом Intel IPP) и планы поменялись.


Книга про Intel IPP, написанная в 2004 Stewart Taylor, участником исторического совещания по созданию IPP (в то время только что нанятым в Intel бакалавром Стэнфорда)

Так началась работа над библиотеками Intel Performance Primitives, которые позднее были переименованы в Integrated Performance Primitives.

Внутренний вариант IPP, назовем его 1.0, был создан в 1999 году. Это был скорее Proof of Concept, прототип для подтверждения жизнеспособности концепции. Он не выпускался как продукт, но позволил определить и уточнить концепцию, архитектуру и спецификации IPP. Первая публичная версия сразу носила номер 2.0 и вышла в апреле 2002 года.

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


Картина Мост к саровской башне авторства Бориса Сабанина, известного не только IPP (здесь можно посмотреть другие картины Сабанина, в том числе его автопортрет)

Но наследием IPL/ISL дело не ограничилось. Практически сразу появились примитивы для криптографии и компрессии данных. Начались эксперименты в области Computer Vision, которые позже переросли в проект с OpenCV, использующий ускорение алгоритмов с помощью примитивов в ippCV домене.

Конечно же, это был не единственный эксперимент и ответвление в истории создания библиотек. IPP шли в ногу со всем Intel. Соответственно, например, пятая версия IPP помимо x86 поддерживала и процессор Intel XScale (ARM архитектура) и Intel Itanium (IA-64)! В разные годы IPP включали такие компоненты как трассировка лучей (realistic rendering), операции с небольшими матрицами (small matrix operations), проверка целостности данных (data integrity), видео и аудио кодеки.

Эту функциональность при желании можно использовать и сейчас с помощью пакета IPP Legacy Libraries, доступного для скачивания.

Более того, видеокодеки IPP в дальнейшем послужили основой другого известного продукта Intel Intel Media SDK, а трассировка лучей была реализована в проекте с открытом кодом Intel Embree.

Из интересных технологических опытов в области IPP-строения можно отметить пример Windows-драйвера для демонстрации возможности работы IPP в режиме ядра, а также версию IPP для работы на интегрированных Intel GPU, написанную на C for Metal.

Любопытно, что номера версий IPP сначала шли по порядку от 1 до 9, а потом начали обозначаться годом выхода 2017-2020.


Команда разработчиков Intel IPP в 2003 году

За время существования семейства библиотек IPP в работе над ними принимало участие более 100 человек в Сарове, Нижнем Новгороде и Москве. Сейчас штаб-квартира IPP находится в Нижнем Новгороде и выглядит очень привлекательно!


Оформление этажа IPP в Intel

IPP совсем не примитивная библиотека!


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

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

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

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

Поскольку приложения бывают разные, и IPP сделаны тоже разные. А именно, в комплект поставки IPP входят по два набора библиотек в 32 и 64-битной версиях: одна чисто однопоточная внутри, а вторая с внутренним распараллеливанием значительного количества функций при помощи OpenMP (точный список функций прилагается в сопроводительных документах). Кроме того, для библиотек обработки изображения есть и еще одна версия мнопоточный слой (Threading Layer), представляющая собой надстройку над однопоточной IPP и использующая либо OpenMP либо Intel TBB для внешнего распараллеливания работы над изображениями, разделяемыми для этого на фрагменты (тайлы). Исходные коды IPP Threading Layer доступны в пакете IPP и нужны тем, кто хочет получить максимально возможный контроль над параллельной работой своего кода.

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

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

В результате была реализована C++ надстройка над IPP, которая строила графы конвейеров, нарезала картинки на кусочки, и потом запускала параллельный цикл, который в каждом потоке выполнял не одну операцию, а весь IPP конвейер на отдельном тайле. В конце, конечно результаты склеивались. Сначала был сделан прототип, он показал приличное ускорение. Потом была создана настройка под названием DMIP (deferred-mode image processing). Далее, в 2011 году на одном из первых заседаний комитета стандартизации OpenVX в Khronos, DMIP был упомянут и горячо поддержан комитетом с учетом популярности графов у разработчиков железа. Так стандарт OpenVX получился основанным на графовой технологии. Стандарт OpenVX по разным причинам не получил достаточной популярности, но зато сейчас графовую парадигму поддерживает и развивает технологию команда Intel Graph API. А так как Graph API входит в OpenCV, OpenVINO, Movidius SDK, то налицо прямое влияние технологий IPP на стандарты компьютерного зрения и современные API.

IPP полезные ссылки


Еще раз приведем самые главные ссылки из этой статьи.


Intel IPP от первого лица


Предоставим слово людям, в разные годы сыгравшим важную роль в судьбе Intel Performance Primitives.

Владимир Дудник, руководитель команды Intel IPP в 2009-2011 годах
По-моему, у IPP по прежнему очень сильная концепция предоставить индустрии легкий, достаточно низкоуровневый слой абстракции от различий разных аппаратных платформ. Несмотря на существенный прогресс в развитии компиляторов, по-прежнему нет возможности из описания на языке высокого уровня получить оптимальный по размеру и производительности машинный код для функций преобразования Фурье, фильтров разного рода и т.п. Это означает что у IPP по-прежнему есть ниша, где она нужна и действительно полезна для ускорения разработки кросс-платформенных высокопроизводительных программ.
Думается, есть потенциальная возможность присмотреться к потребностям разных фреймворков глубокого обучения, там много достаточно стандартных, относительно простых операций, которые могут быть ускорены SIMD инструкциями. Часть из них, как правило многомерные свертки, покрываются библиотекой MKL, но и для IPP возможность применения можно найти.

Вадим Писаревский, лидер проекта OpenCV, участник команды IPPCV в 2006-2008 годах
Если говорить про IPP в целом, то, конечно, скорость у библиотеки выдающаяся. Особенно флагманские функции типа FFT. Ядра чистый изумруд! К счастью, у меня была возможность немного поизучать исходники IPP в свое время, и местами это был просто учебник, как надо писать эффективный код.
В свое время команду IPP очень сильно заинтересовала проблема автогенерации кода, в частности, на OCaml, и впечатлили успехи проекта Spiral, который сумел автоматически сгенерировать ядра FFT и родственных преобразований на уровне или даже обходивших по скорости реализованные в IPP. Часть этих ядер потом была включена в IPP. А меня, в свою очередь, эта тема зацепила настолько, что мой текущий проект, 13-14 лет спустя, с этой темой связан.

Павел Бердников, руководитель команды IPP QA в 2011-2015 годах, руководитель команды IPP в 2017-2020 годах
Работу в IPP я начал в QA команде в Сарове в 2000г. с написания тестов, позже создав единую билдовую систему для такого сложного проекта, автоматизированный процесс построения и тестирования.
А 17 лет спустя в Нижнем Новгороде стал руководителем всей команды IPP. Пришлось знакомиться с партнёрами внутри Intel и пользователями за его пределами. Изменить способы планирования и разработки кода от старых waterfall & development к современным agile & DevOps. Пересмотреть стратегии проекта и придумать новые. Так проект больше сосредоточился на криптографии и компрессии данных, появились интересные направления работы и новые области развития.
Реалистичные планы по развитию проекта развивать что есть и смотреть в смежные области. Фантастические планы, которые были ранее в мечтах, разбились о понимание мировых трендов и нужд наших пользователей. Пришло осознание, что бесконечные ресурсы, которые всегда очень хочется получить чтобы сделать больше и глубже, не дадут ожидаемого результата. Наверное, если бы я получил полную свободу действий и ресурсы, я бы пошёл дальше в область исследований новых алгоритмов, в область университетов и работы с ними.

Валентин Кубарев, руководитель команды Image & Signal Processing проекта Intel IPP, 2020 год настоящее время
Работу в IPP я начал в качестве SW инженера в 2011 году, когда проект только переехал из Сарова в Нижний Новгород. Меня всегда интересовала работа, связанная с математикой и оптимизацией. Знакомство с IPP я начал с функциональности изменения размера изображения, после чего долгое время отвечал за поддержку, разработку и оптимизацию геометрических преобразований, таких как Resize, WarpAffine, WarpPerspective и т. д. C 2017 года активно участвовал в задачах связанных с криптографией, и, в итоге, в 2018 мы смогли открыть код IPP Crypto и выложить проект на GitHub. С конца 2018 года работал в другом проекте, в 2020 году снова вернулся в IPP, но уже как руководитель Image & Signal Processing домена. Считаю, что в данный момент данная часть проекта недооценена, поэтому активно работаю с пользователями продукта IPP и нашими партнёрами внутри компании Intel, чтобы вывести проект на новый уровень.
Подробнее..

Перевод Julia готова для прода

22.09.2020 10:23:41 | Автор: admin


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


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


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

В течение многих лет я бы с этим согласился, но после JuliaCon 2020, я думаю, мы можем с уверенностью заявить, что


Джулия готова идти в производство!


Позвольте мне теперь привести список ключевых (на мой взгляд) презентаций, сделанных во время JuliaCon 2020, которые заставили меня сделать этот вывод.


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


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


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


Еще будет интересно:


  • Доклад по характеристикам приложений, где Кристоффер Карлссон покажет как создать исполняемые файлы, которые могут быть запущены на машинах, на которых не установлена Julia.
  • Презентация Julia for scripting, в ходе которой [Фредрик Экре]() обсуждает лучшие практики использования Julia в контекстах, где вам нужно много раз выполнять короткие фрагменты кода.
  • Доклад Genie.jl, в котором Адриан Сальчану показывает зрелый, стабильный, производительный и многофункциональный фреймворк веб-разработки на Julia.

Управление зависимостями


Пара докладов Pkg.update() и What's new in Pkg показывает, что в настоящее время Julia имеет лучшие в своем классе функциональные возможности для управления зависимостями корпоративного уровня для ваших проектов. Список предоставляемых функций настолько велик, что здесь трудно перечислить их все.


Позвольте мне только упомянуть один конкретный инструмент в этой экосистеме BinaryBuilder.jl, который позволит взять программное обеспечение, написанное на компилируемых языках, таких как C, C++, Fortran, Go или Rust, и построить предварительно скомпилированные артефакты, которые могут быть легко использованы из пакетов Julia (что означает, что не нужна никакая компиляция на стороне клиента, когда вы устанавливаете пакеты, имеющие такие зависимости).


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


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



Здесь стоит добавить, что Джулия уже много лет отлично интегрируется с Python, см. JuliaPy.


Хорошим всеобъемляющим примером выполнения некоторой реальной работы в Julia, требующей интеграции, является создание многоканальной беспроводной настройки динамиков, где показано, как легко сшивать всякие штуки вместе (и, в частности, с помощью ZMQ.jl, Opus.jl, PortAudio.jl, и DSP.jl).


Еще один интересный доклад, демонстрирующий возможности интеграции, это JSServe: Websites & Dashboards в Julia, который показывает высокопроизводительный фреймворк для легкого объединения интерактивных графиков, markdown, виджетов и простого HTML/Javascript в Jupyter / Atom / Nextjournal и на веб-сайтах.


Инструментарий разработчика


В паре замечательных докладов Juno 1.0 и Using VS Code рассказывается, что текущая поддержка IDE для Джулии в VS Code первоклассна. У вас есть все для полного счастья: анализ кода (статический и динамический), отладчик, рабочие области, интеграция с ноутбуками Jupyter и удаленные возможности.


Управление рабочими процессами в машинном обучении


Я не хочу охватывать множество различных алгоритмов ML, доступных в Julia изначально, так как их просто слишком много (и если чего-то не хватает, вы можете легко интегрировать его см. раздел возможности интеграции выше).


Однако в дополнение к конкретным моделям вам нужны фреймворки, позволяющие управлять рабочими процессами ML. В этой области есть две интересные презентации: MLJ: A machine learning toolbox for Julia, и AutoMLPipeline: A ToolBox for Building ML Pipelines. По моему опыту, такие инструменты имеют решающее значение, когда вы хотите перейти с вашими ML-моделями из песочницы дата-саянтиста в реальное производственное использование.


Выводы


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


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

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


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


Готова Ли Джулия для прода? Вопросы и ответы с Богумилом Каминским


Статья профессора Каминского вызвала немного ажиотажа на Hacker News. Некоторые комментаторы высказывали сомнения в том, что Джулия может считаться готовой к производству из-за, в частности, документации, пакетов, инструментов и поддержки.


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


InfoQ: Не могли бы вы описать ваш быкграунд и вашу связь с Julia?


Bogumi Kamiski: Я профессор Варшавской школы экономики SGH, Польша, работаю в области операционных исследований и имитационного моделирования.
Я нахожусь в топ-5% участников языка Julia по количеству коммитов, внес значительный вклад в экосистему данных Julia и, в частности, один из основных мейнтейнеров DataFrames.jl.
Я занимаю второе место по ответу на тег [julia] в StackOverflow. Прежде чем перейти на полный рабочий день в академию, я более 10 лет руководил командой из нескольких сотен разработчиков и аналитиков, развертывающих проекты BI/DWH/data science для крупнейших польских корпораций и учреждений.


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


Kamiski: Здесь очень важно определить, что я понимаю под готовностью Джулии к проду.


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


В прошлом это было серьезной проблемой. Основные пакеты, как правило, часто меняли свой API; учебные пособия, созданные год назад, теперь не будут работать без получения обновлений и т. д. Это было естественное состояние, когда развивались язык и экосистема. Теперь я вижу, что эти вещи значительно изменились, особенно для ядра языка Julia, но аналогичные вещи происходят и в экосистеме пакетов.


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


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


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


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


Почему это так важно? Ну, вы можете легко "отправить" проект Julia и ожидать, что любой человек в любой среде должен быть в состоянии относительно легко запустить его. Конечно, будут угловатые моменты; но мой опыт ежедневного использования Linux и Windows 10 заключается в том, что все должно просто работать на обеих платформах.


Если вы делаете проект в Julia, вы можете ожидать, что либо есть пакет, который делает то, что вы хотите, либо вы можете легко использовать код, написанный на C или Python, и заставить его работать. В своем посте я хотел подчеркнуть, что мы находимся именно в этом состоянии. В качестве примера, основанного на чем-то, над чем я работал на этой неделе, у Джулии есть отличный пакет LightGraphs.jl для работы с графиками, но мои сотрудники используют Python и предпочитают использовать igraph. Вот пример кода с использованием графика (взято из учебника для графика в Python):


import igraph as igg = ig.Graph()g.add_vertices(3)g.add_edges([(0,1), (1,2)])g.add_edges([(2, 0)])g.add_vertices(3)g.add_edges([(2, 3), (3, 4), (4, 5), (5, 3)])g.pagerank()

Теперь вы спросите, как бы выглядел эквивалент в Julia. А вот так:


using PyCallig = pyimport("igraph")g = ig.Graph()g.add_vertices(3)g.add_edges([(0,1), (1,2)])g.add_edges([(2, 0)])g.add_vertices(3)g.add_edges([(2, 3), (3, 4), (4, 5), (5, 3)])g.pagerank()

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


Что это означает на практике? Если вы делаете проект, вы не застреваете с мыслью: "могу ли я использовать Джулию, так как через три месяца, возможно, мне понадобится что-то в проекте, чего еще нет в Джулии"? Но скорее вы знаете: "я могу относительно безопасно использовать Julia, так как в настоящее время многие пакеты общего назначения уже существуют, и даже если чего-то не хватает, я могу просто использовать это с другого языка, и это будет относительно безболезненно, независимо от того, является ли это C/Python/R/...".


Также частью производственной готовности является то, что с PackageCompiler.jl вы можете создавать "приложения, которые представляют собой набор файлов, включая исполняемый файл, который может быть отправлен и запущен на других машинах без установки Julia на этой машине." Я не рассматриваю это как существенную часть готовности для продакшена (многие скриптовые языки, считающиеся готовыми к производству, не предоставляют этой опции), но во многих сценариях это хорошая функция.


Теперь позвольте мне уточнить, что я не вижу в определении термина "готовый к производству". Я не рассматриваю Джулию как язык, который лучше всего подходит для любого вида проекта. Каждый язык программирования имеет свою нишу, и ниша Julia это высокопроизводительные вычисления / наука о данных (или как вы это там называете). Если вам нужны компактные бинарные файлы наверняка Julia не является лучшим вариантом. Если вы хотите разрабатывать приложения для Android тут тоже посоветую смотреть в другом направлении.


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


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


Kamiski: Процитирую то, что я написал в начале своего поста:


"Я потратил 20 лет на развертывание проектов, связанных с наукой о данных, в корпоративных средах (тогда это не называлось наукой о данных, но мы уже обучали нейронные сети делать прогнозы), и у меня есть много коллег, которые глубоко занимаются разработкой корпоративного программного обеспечения."


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


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


Теперь, что касается таких вещей, как документация, пакеты, инструменты и поддержка конечно, это должно и может быть улучшено. И я согласен, что более зрелые экосистемы, такие как R/Python/Java, в среднем имеют здесь лучший охват. Например, как соразработчик DataFrames.jl, я могу сказать вам, что большинство последних PR связаны с документацией. Но я бы не стал недооценивать сообщество Джулии здесь. В общем, если у вас есть какой-либо вопрос и вы разместите его на SO, Julia Discourse или Julia Slack, вы можете получить ответ обычно в течение нескольких минут, самое большее часов. Вот типичная история такого рода: люди обычно очень отзывчивы, и ошибки исправляются довольно быстро.


Если бы меня попросили назвать главный сомнительный момент касательно Джулии, то это было бы наличие достаточного количества людей, квалифицированных в этом языке в общем сообществе разработчиков. Я могу понять владельцев продуктов / менеджеров проектов и их чувство риска того, что они не смогут найти достаточно людей для работы над своими проектами, как только они возьмут на себя смелость начать работать на Julia. Однако здесь я убежден, что ситуация быстро улучшается. В прошедшем JuliaCon 2020 приняли участие более 20 000 участников. Кроме того, есть много ресурсов, уже доступных бесплатно в интернете.


InfoQ: Помимо вопроса о том, готова ли Джулия к проду или нет, или для каких областей она предназначена, каковы, на ваш взгляд, основные сильные стороны языка? Рассматриваете ли вы его как замену Python, R или любому другому языку, по крайней мере в области научных вычислений и науки о данных?


Kamiski: Я думаю, что здесь снова лучше всего процитировать последний опрос разработчиков Julia, слайды с 8 по 11. Я бы сосредоточился на трех главных вещах из слайда 8:


  • Скорость здесь ситуация относительно проста. Возьмите любой зрелый пакет, такой как TensorFlow или PyTorch, который требует производительности; они написаны в основном на C++. А Python это всего лишь тонкая оболочка вокруг ядра C++. Теперь возьмем Flux.jl или Knet.jl. Они по существу реализованы в чистом виде. Итак, суть такова: если вам нужно, чтобы ваш код работал быстро, в то же время используя преимущества языка высокого уровня, то Julia это естественный выбор. Кроме того, как объяснялось выше, если есть внешняя библиотека, которая очень быстра, и вы хотите использовать ее, обычно это относительно легко сделать.


  • Простота использования есть множество проявлений этого аспекта, но мой опыт показывает, что когда кто-то получает представление о принципах Джулии, он очень хорошо сравнивается с точки зрения синтаксиса и дизайна, например, с R/Python при выполнении вычислений. Язык был разработан вокруг поддержки этих видов задач в лучшем виде. И это не только синтаксис это также выбор того, когда вещи будут происходить явно, а когда нет (например, с трансляцией). Исходя из моего опыта, это делает код Джулии простым в обслуживании, а хорошо написанный код в значительной степени самодокументируется.


  • Код является открытым и может быть изменен этот аспект имеет два измерения. Прежде всего, большинство пакетов Julia лицензированы MIT, что часто очень приветствуется в корпоративных средах. Во-вторых поскольку большинство пакетов написано на Julia, то если вам не нравится, как что-то работает вы просто модифицируете это сами (что гораздо проще, чем в R/Python, где, скорее всего, то, что вам нужно изменить, написано, например, на C, C++, Fortran).



Люди часто спрашивают меня, вижу ли я Джулию в качестве замены R / Python. И я думаю об этом так::


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


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


  • Если у вас много устаревшего кода R/Python и вы им довольны просто придерживайтесь его и помните, что если у вас есть некоторые критически важные для производительности части, они могут быть относительно легко переписаны на Julia и интегрированы обратно с вашей исходной кодовой базой (я сделал много таких проектов).



InfoQ: Как вы видите эволюцию Джулии?


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


Если бы я назвал здесь некоторые основные изменения, то это были бы:


  • Улучшение поддержки многопоточности. Я думаю, что это действительно актуально для основных случаев использования Julia. Так-то поддержка есть, но все же здесь многое можно улучшить. Точно так же следует ожидать улучшения в обработке GPU/TPU.


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


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


  • Рост сообщества я считаю, что число людей, заинтересованных в Джулии, значительно увеличивается, о чем свидетельствует, например, количество участников JuliaCon2020. Это означает, что: а) в будущем будет легче нанимать качественных разработчиков Julia, если они понадобятся, и б) это создаст положительную обратную связь для качества экосистемы Julia, поскольку все больше людей сообщают о проблемах и участвуют в них. Опять же, как сопровождающий DataFrames.jl я наблюдаю этот сдвиг: люди, которые никогда не были в "ядре" разработки пакета, открывают вопросы / делают PR и обсуждают функциональные возможности в социальных сетях.



Если вы заинтересовались языком Julia, все доклады с JuliaCon 2020 доступны на YouTube.

Подробнее..

Как мы строили компанию в Кремниевой долине

18.09.2020 10:20:14 | Автор: admin
Вид на Сан-Франциско с восточной стороны заливаВид на Сан-Франциско с восточной стороны залива

Привет Хабр,

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

Под катом много интересных историй про Y Combinator, венчурные инвестиции, поиск команды, и другие аспекты жизни и работы в долине.

Предыстория

Я приехал в долину в 2011 году, где присоединился к компании MemSQL, которая только выпустилась из Y Combinator. В MemSQL я был первым сотрудником. Мы работали из трёхкомнатной квартиры в городе Менло-Парк, в которой и жили (в одной комнате я с супругой, в другой CEO с супругой, а CTO компании Никита Шамгунов спал на диване в зале). Время текло, MemSQL сегодня это большая enterprise компания с сотнями сотрудников, многомиллионными сделками и офисом в центре Сан-Франциско.

В 2016 году я понял, что компания меня переросла, и решил, что пора начинать что-то новое. Еще не решив, что делать дальше, я сидел в кофейне в Сан-Франциско и читал какую-то статью того года по машинному обучению. Ко мне подсел другой молодой человек и сказал я заметил, ты про машинку читаешь, давай знакомиться. Такие ситуации в Сан-Франциско обычное дело. Большинство людей в кофейнях, в ресторанах и на улице сотрудники стартапов или больших технологических компаний, поэтому вероятность так вот с кем-то познакомиться очень высокая. Через ещё две встречи с этим молодым человеком в кофейне мы решили начать строить компанию, которая строит умных помощников. Samsung только что купил VIV, Google анонсировал Google Assistant, и казалось, что будущее где-то в этом направлении.

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

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

Заявка в Y Combinator

Нам нужно было подтянуть наши навыки в строительстве компании. Строить компанию это не код писать. Это понимать, что нужно людям, проводить user studies, прототипировать, правильно решать, когда пивотиться, а когда продолжать, найти product-market-fit. Как раз в это время проходил набор в Y Combinator Winter 2017. Y Combinator это самый престижный акселератор в Кремниевой долине, через который прошли такие гиганты, как Dropbox, Reddit, Airbnb, даже MemSQL. Критерии Y Combinator и венчурных инвесторов очень схожи: им надо из большого числа компаний в Кремниевой долине выбрать небольшое количество и максимизировать шанс поймать следующего единорога. Чтобы попасть в Y Combinator, надо заполнить анкету. Анкета отсекает примерно 97% заявок, поэтому её заполнение это невероятно ответственный процесс. После анкеты проходит интервью, которое отсекает половину из оставшихся компаний.

Мы потратили неделю на заполнение анкеты, перезаполнение, чтение с друзьями, чтение ещё раз, перезаполнение опять. В итоге спустя пару недель мы получили приглашение на интервью. В 3% попали, осталось попасть в 1.5%. Интервью проходит в головном офисе YC в Маунтин-Вью (это 40 минут на машине от СФ) и длится 10 минут. Вопросы задаются примерно одни и те же и хорошо известны. Есть сайты в интернете, где засекается таймер на 10 минут и рандомно выбираются вопросы из известной методички и показываются. Мы по этим сайтам занимались каждый день часами, попросили несколько наших друзей, кто прошли через YC в прошлом, поинтервьюировать нас тоже. В общем подошли серьезнее, чем мы подходили к встречам с инвесторами за месяц до этого.

День интервью был очень интересный. Наше интервью было около 10 утра. Мы приехали заранее. Для меня день интервью представлял определенную проблему. Так как моя компания пока явно не взлетала, я диверсифицировал свои инвестиции времени, начав испытательный срок в компании OpenAI. Один из сооснователей OpenAI, Сэм Альтман, по совместительству являлся президентом Y Combinator. Если я попаду к нему на интервью и он в моей заявке увидит OpenAI, нет ни малейшего сомнения, что он спросит о моих успехах на моем испытательном сроке у моего менеджера. Если я затем не попаду в Y Combinator, то мой испытательный срок в OpenAI тоже окажется под большим вопросом.

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

Если Y Combinator принимает компанию, они звонят в тот же день. Если отвергают, то пишут email на следующий день с развернутым объяснением почему. Соответственно, если до вечера звонка не поступило, значит не повезло. А если позвонили, то, не снимая трубки, можно знать, что нас взяли. Интервью мы прошли с лёгкостью, все вопросы оказались из методички. Вышли воодушевлённые, поехали в СФ. Прошло полчаса, мы были в десяти минутах от города, как нам позвонили.

Попасть в Y Combinator это мечта почти каждого человека, который строит компанию в Кремниевой долине. Тот момент, когда позвонил телефон, входит в топ 3 самых запоминающихся моментов в моей карьере. Забегая вперед, второй из трёх случится в этот же день спустя всего несколько часов.

Девушка на том конце совсем не спешила радовать нас новостями о нашем приёме. Она сообщила нам, что им нужно провести второе интервью. Это редкое событие, но в интернете о нём тоже было написано. Что интересно, по статистике среди компаний, которые вызвали на второе интервью, принимают те же самые 50%, то есть тот факт, что нам надо возвращаться, даёт нем 0 новой информации о том, попадём мы в YC или нет.

Развернулись, приехали назад. Подошли к комнате. Сэм Альтман. Не повезло

Я написал своему менеджеру в OpenAI в слэк, что так и так, я сегодня в Y Combinator прохожу интервью, тебе наверняка напишет Сэм, не удивляйся. Прошло все ОК, моему менеджеру в OpenAI, кажется, не могло быть более фиолетово.

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

Y Combinator

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

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

Помимо этого был поднят портал, на котором мы могли в любой момент создавать встречи с разными умными ребятами, у которых опыт в самых разных областях строительства компаний: продажи, маркетинг, user studies, дизайн, UX. Мы этим пользовались достаточно много и набрались кучи опыта. Почти всегда эти ребята находились в СФ, поэтому даже не надо было далеко ехать. Часто даже не нужна была машина.

Поиск ещё одного сооснователя

Вдвоем компанию не поднять. Но у нас есть $150K, которые YC дает в начале программы. Надо найти людей. Учитывая, что мы едва сами знаем, что мы пишем, искать сотрудников пока что дело гиблое, но, может, мы найдем ещё человека, который хочет быть с нами сооснователем? В ВУЗе я занимался ACM ICPC, и многие из людей, кто в моём поколении им занимались, сейчас имели успешные карьеры в долине. Я начал писать своим старым друзьям, которые теперь жили в СФ. И долина не была бы долиной, если бы за первые пять сообщений я бы не нашёл кого-то, кто хочет строить компанию. Супруга одного из моих друзей с поездок на ICPC строила очень успешную карьеру в Facebook, но рассматривала вариант уйти и начать компанию. Мы с ней встретились. Она уже тоже была в активном поиске сооснователей и познакомила меня с её другом Ильёй Полосухиным. Илья был одним из инженеров в команде, которая строила TensorFlow. Спустя несколько встреч девушка решила остаться в Facebook, а Илья пришёл к нам в компанию третьим основателем.

Начало NEAR

После YC поднимать венчурные инвестиции немного проще. В последние дни программы Y Combinator организует Demo Day, где мы презентуем перед 100 инвесторами. YC построили систему, в которой инвесторы выражают интерес к нам прямо во время презентации, а мы к ним в конце дня, а потом там строится взвешенное паросочетание и мы с ними встречаемся. Мы подняли $400K, я и Илья в этом процессе не были очень вовлечены, мы писали код, поэтому много интересных историй рассказать не могу. Но одна есть.

В качестве маркетинга мы проводили в Сан-Франциско встречи по машинному обучению с топовыми исследователями (многие из которых работают в Google Brain, OpenAI, учатся в Стэнфорде или Беркли, и поэтому находятся географически тут же в долине) и строили локальное сообщество. На одной из таких встреч мы убедили одного из абсолютно топовых исследователей в области быть нашим advisor. Мы уже почти подписали документы, когда через неделю он понял, что его текущая компания не позволяет ему быть advisorом. Но он чувствовал, что он нас подводит, и поэтому предложил вместо того, чтобы эдвайзить, в нас просто инвестировать. Сумма в масштабе компании была небольшой, но получить топового исследователя в области не просто как эдвайзера, но как инвестора, это было очень круто.

Это уже был июнь 2017, Google Pixel вышел и был популярен. В отличие, к сожалению, от встроенного в него Google Assistant. Я брал у друзей Пиксели, зажимал home button и в 10 случаях из 10 видел настройте Google Assistant перед первым использованием. Samsung никак купленный VIV не использовал, а выпустил вместо этого Bixby с хардварной кнопкой, и в Samsung Store стали популярны приложения, которые заменяли Bixby на фонарик.

На фоне всего этого наша с Ильёй вера в будущее ассистентов погасла, и мы покинули ту компанию. Мы сразу основали новую компанию Near Inc, потеряв в процессе бейджик Y Combinator на компании, $400K и топового исследователя как инвестора.

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

Венчурные инвестиции

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

Наше первое ДА пришло от инвестора Х. Я ничего хорошего про Х не скажу, поэтому и имя упоминать не буду. Х сбивал оценку компании на каждой встрече и пытался добавить дополнительные условия, которые были невыгодны команде и основателям. Конкретный человек, с которым мы работали в X, был в начале своей карьеры как инвестора в большом фонде, и для него закрыть очень выгодную сделку это лестница в карьеру. И так как кроме него нам ДА никто не говорил, он мог требовать всё, что угодно.

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

В одно солнечное утро в Сан-Франциско я получил письмо от Никиты Шамгунова, уже тогда CEO MemSQL, Introducing Alex (NEAR) to Amplify Partners. Спустя буквально 17 минут совершенно независимо и по чистому совпадению приходит письмо от X с точно таким же заголовком. Ребята из Amplify оказались невероятно крутыми. Условия, которые нам предлагали Х, казались им драконовскими, и они были готовы инвестировать в нас на разумных условиях. Ряд инвесторов был готов инвестировать вместе с Amplify. В таких условиях мы отказались от инвестиции Х и подняли раунд с Amplify в качестве главного инвестора. Amplify тоже не был рад инвестировать в обход X, но так как первое интро пришло от Никиты, а не от Х, общий язык между всеми нашёлся, и никто ни на кого в обиде не остался. Если бы Никита в тот день прислал письмо на 18 минут позже, всё могло бы быть немного сложнее.

У нас теперь было $800K, чтобы существовать, и начался год, полный хардкорных моделек на PyTorch, общения с десятками компаний в долине, чтобы понять, где на практике можно было бы применять program synthesis, и других не очень интересных приключений. К июлю 2018 у нас был какой-то прогресс по моделькам и несколько статей на NIPS и ICLR, но не было понимания, где модели достижимого на то время уровня могли быть применены на практике.

Первое знакомство с блокчейн

Мир блокчейна это очень странный мир. Я его достаточно целенаправленно избегал долгое время, но в итоге наши пути пересеклись. В поисках применения program synthesis мы в итоге пришли к тому, что что-то на пересечении program synthesis и смежной темы formal verification может быть очень полезно для смарт-контрактов. Мы ничего не знали про блокчейн, но долина не была бы долиной, если бы среди моих старых друзей там не нашлось хотя бы нескольких, кто этой темой интересовался. Мы начали с ними общаться и поняли, что formal verification это хорошо, но в блокчейне есть проблемы более насущные. В 2018 году Ethereum уже справлялся с нагрузкой достаточно тяжело, и разработать протокол, который работал бы значительно быстрее, было очень насущной проблемой.

Мы, конечно, далеко не первые, кому такая идея пришла в голову, но быстрое изучение рынка показало, что в то время как конкуренция там есть, и высокая, выиграть его можно. Что важнее, и я, и Илья очень хорошие системные программисты. Моя карьера в MemSQL была, разумеется, гораздо ближе к разработке протоколов, чем строительству моделек на PyTorch, а Илья в Google был одним из разработчиков TensorFlow.

Я начал обсуждать эту идею со своими бывшими коллегами по MemSQL и моим сокомандником со времен ICPC, и идея строить быстрый блокчейн протокол оказалась интересна четырём из пяти людей, с кем я пообщался. За один день в августе 2018 года NEAR вырос с трёх человек до семи и до девяти в течение следующей недели, когда мы наняли head of operations и head of business development. При этом уровень людей был просто невероятным. Все инженеры были либо из ранней команды MemSQL, либо проработали по многу лет в Google и Facebook. Трое из нас имели золотые медали ICPC. Один из семи первых инженеров выиграл ICPC дважды. На тот момент дважды чемпионов в мире было шесть (сегодня количество дважды чемпионов в мире уже девять, но теперь два из них работают в NEAR, так что статистика со временем улучшилась).

Это был взрывной рост, но была проблема. Никто не работал бесплатно, и офис в центре СФ тоже далеко не дешёвый, и покрывать аренду офиса и зарплаты уровня долины девяти людям, имея то, что осталось от $800K спустя год было проблематично. NEAR осталось существовать 1.5 месяца, прежде чем в банке останется ноль.

Снова венчурные инвестиции

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

Metastable топовый фонд, и получить ДА от них значило бы закрыть раунд почти сразу. Мы уже к тому времени набрали 3-4 НЕТ, и количество фондов, с кем можно пообщаться, быстро сокращалось, как и время до того, как NEAR останется без средств к существованию. В Metastable работало несколько невероятно умных ребят, задача которых была разнести наши идеи в щепку и найти минимальные неточности в нашем дизайне. Так как нашему дизайну в то время было несколько дней, как и нашему опыту в блокчейне на тот момент, на митинге с Metastable они нас с Ильей уничтожили. Количество НЕТ в копилке увеличилось еще на один.

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

Решение нашлось. По случаю дня рождения Илья устроил шашлыки на крыше своего дома (которая, как и многие крыши в квартирных комплексах в СФ, представляла из себя ухоженный парк), куда были приглашены все сотрудники NEAR и друзья, включая Ивана Богатого друга Ильи, который работал в Metastable в тот момент, а также некоторые другие инвесторы. В отличие от презентации инвесторам в комнате для совещаний, на шашлыках вся команда из NEAR могла пообщаться в простой обстановке с пивом в руках с Иваном и другими инвесторами о нашем текущем дизайне и целях. К концу шашлыков Иван подошел к нам и сказал, что, кажется, нам есть смысл встретиться ещё раз.

Эта встреча прошла намного лучше, и мы с Ильёй смогли защитить дизайн от коварных вопросов. Metastable позвали нас встретиться с её основателем Навалом Равикантом спустя пару дней в офисе компании Angellist. В офисе было абсолютно пусто, потому что компания почти всем составом уехала на Burning Man. На этой встрече НЕТ превратилось в ДА, и NEAR больше не была в шаге от смерти. Митинг закончился, мы сели в лифт. Новости о том, что Metastable в нас инвестирует, разлетелись очень быстро. Лифт еще не доехал до первого этажа, когда на нашу почту без какого-либо участия с нашей стороны прилетело второе ДА, тоже от топового фонда. Больше НЕТ в том фандрейзе не было, и через неделю мы опять решали задачу о рюкзаке, чтобы вместить самые лучшие предложения в ограниченный раунд.

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

Скорость не самая большая проблема

В конце 2018 года мы пошли на хакатон ETH San Francisco. Это один из множества хакатонов по всему миру, посвященных Ethereum. На хакатоне у нас была большая команда, которая хотела построить первую версию моста между NEAR и Эфиром.

Я от команды отделился и решил пойти другим путём. Я нашёл глазами Влада Замфира, известного инфлюенсера в экосистеме, который писал свою версию шардинга для Ethereum, подошел к нему и сказал "Привет, Влад, я писал шардинг в MemSQL, давай участвовать в одной команде". Влад был с девушкой, и на его лице ясно читалось, что я выбрал не самое лучше время для общения. Но девушка сказала "это звучит круто, Влад, тебе нужно взять его в команду". Так я оказался в команде с Владом Замфиром, и следующие 24 часа узнавал о том, как работает его дизайн, и писал вместе с ним прототип.

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

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

Строим узнаваемость

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

Мы только начинали, а у наших конкурентов уже были большие фан-базы. А есть ли способ как-то достучаться до этих фан-баз, чтобы все оказались в плюсе? Мы сидели небольшой компанией в кофейне Red Door в Сан-Франциско утром когда в голову пришла феноменальная идея. В мире, где десятки протоколов конкурируют за то, чтобы быть the next big thing, реально у людей нет никакого источника информации об этих протоколах, кроме их собственных маркетинговых материалов. Было бы здорово, если бы кто-то достаточно умный встал с исследователями и разработчиками таких протоколов перед доской и разносил их. Такие видео хороши для всех. Для них (если их не разнесут), потому что их сообщество может видеть, что их дизайн не трава. Для нас возможность быть замеченными их сообществом, а ещё возможность узнать хорошие идеи. Почти все протоколы, включая NEAR, разрабатываются открыто, поэтому идеи и код в целом не скрываются, но найти эти идеи иногда сложно. За один час перед доской с умным человеком можно научиться очень многому.

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

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

Дальнейшая история

Команда росла, и в жизни стартапа самое важное это иметь достаточно финансов, чтобы поддерживать рост. Третий фандрейзинг тоже начался удачно не сразу, мы получили несколько НЕТ, но одно ДА опять перевернуло всё с ног на голову, и мы быстро его закрыли. Четвёртый фандрейзинг в начале этого года начался с ДА почти сразу, мы получили финансирование от Andreessen Horowitz, самого топового фонда как в принципе, так и в области блокчейнов, и имея a16z как инвестора раунд закрылся очень быстро. В последнем раунде мы подняли $21.6M.

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

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

Разработка на этом не заканчивается. Протокол имеет встроенный механизм перехода на новые версии, и впереди ещё много работы.

В заключение

Это первый пост в корпоративном блоге NEAR. В ближайшие месяцы я буду рассказывать, как работает NEAR, почему мир лучше с хорошим удобным блокчейн протоколом, чем без него, и какие интересные алгоритмы и задачи мы решали в ходе разработки: шардирование, генерация случайных чисел, алгоритмы консенсуса, мосты с другими цепями, т.н. Layer 2 протоколы и многое другое. Мы приготовили хорошую комбинацию научно-популярных и глубоких технических постов.

Небольшой список ресурсов для тех, кто хочет закопаться глубже уже сейчас:

1. Посмотреть как выглядит разработка под NEAR, и поэкспериментировать в онлайн-IDE можно здесь: https://examples.near.org.

2. Код протокола открыт, его можно поковырять лопаткой тут: https://github.com/nearprotocol/nearcore

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

4. Обширная документация для разработчиков на английском доступна здесь: https://docs.near.org.

5. Следить за всеми новостями на русском можно в уже упомянутой группе в телеграме, и в группе на ВКонтакте

Наконец, позавчера мы запустили онлайн хакатон с призовым фондом в $50K, на котором предлагается написать интересные приложения, которые используют мост между NEAR и Ethereum. Больше информации (на английском) здесь: https://near.org/rainbow/

До скорых встреч!

Подробнее..

Возможности ClickHouse для продвинутых разработчиков. Алексей Миловидов (2018г)

15.09.2020 10:12:59 | Автор: admin
![](http://personeltest.ru/aways/habrastorage.org/webt/pu/fq/64/pufq64pgen2y7nx3el0l8-znwxa.jpeg)

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

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





![](http://personeltest.ru/aways/habrastorage.org/webt/wf/zp/ze/wfzpzexig0isbnlqra24kx6t87e.jpeg)

Например, сэмплирование. И, соответственно, ключ сэмплирования. Это возможность выполнения приближенных запросов. Знает пара человек. Уже хорошо.

Рассмотрим типичную задачу для ClickHouse, именно ту задачу, для которой он предназначался изначально. Есть у вас какой-то clickstream. Например, рекламная сеть, система веб-аналитики. И, допустим, есть у вас Яндекс.Метрика.

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

![](http://personeltest.ru/aways/habrastorage.org/webt/1i/gc/pq/1igcpqx18d5zo0ep7rj_zfg2bhc.jpeg)

И вы хотите генерировать отчеты для разных клиентов. И среди клиентов есть некоторые маленькие, например, маленькие сайты типа pupkin.narod.ru; есть средние и есть действительно крупные такие, как yandex.ru. И вы хотите для таких крупных клиентов получать отчеты мгновенно. Что значит мгновенно? Хотя бы за секунду.

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

![](http://personeltest.ru/aways/habrastorage.org/webt/vr/qk/_w/vrqk_wcxhktimd--gbffzxpwsys.jpeg)

И вот, как это делается. CREATE TABLE. Дальше ORDER BY первичный ключ. Кстати, структура точно такая же, как в Яндекс.Метрике для таблиц просмотров событий. Номер счетчика, т. е. идентификатор сайта, потом дата идет, а потом некий Hash от идентификатора пользователя. Дальше партиционирование. И в самом низу ключ сэмплирования. И в качестве ключа сэмплирования hash от идентификатора пользователя.

![](http://personeltest.ru/aways/habrastorage.org/webt/j7/vc/pr/j7vcprd2vs33rger_r8w-8vascu.jpeg)

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

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

![](http://personeltest.ru/aways/habrastorage.org/webt/hi/0t/jr/hi0tjrif0-lbos8qgew1ew2aa_0.jpeg)

Давайте добавим простую секцию в запросе сэмпл 1/10. Прямо после from пишем такой синтаксис. Теперь тот же самый запрос выполняется за 0,6 секунды. Если посмотреть скорость в гигабайтах в секунду, то стало почему-то меньше, всего лишь 2,5 гигабайта в секунду. Но нас это не волнует, главное не скорость, а то, чтобы запрос выполнялся за маленькое время. А с какой скоростью в байтах он читает, обрабатывает это уже его дело. Правда, количество уникальных посетителей получилось в 10 раз меньше. И его еще надо умножить, чтобы получить приблизительный результат.

![](http://personeltest.ru/aways/habrastorage.org/webt/si/lv/db/silvdbdioopkwhikvfrsksppaje.jpeg)

Что нужно, чтобы все это работало?

Во-первых, ключ сэмплирования должен входить в первичный ключ. Это как в нашем примере. Последний компонент первичного ключа, после него ничего другое не имеет смысла.
Он должен быть равномерно распределенным в своем типе данных. Типичная ошибка, если мы в качестве ключа сэмплирования возьмем unix timestamp.
Если нарисовать график количества событий в зависимости от timestamp, то там будет вот такая штука (рисует волну, размахивая рукой в воздухе). Все, кто работал админом, DevOps, видели такой график. Это график активности по времени суток. Так нельзя, потому что неравномерно.
Надо что-нибудь захэшировать. Если захэшировать идентификатор пользователя, то все будет прекрасно.
Ключ сэмплирования должен быть легким как для чтения из таблицы, так и для вычисления.
Тоже плохой пример, когда берем url, хэшируем его и этот hash используем в качестве ключа сэмплирования. Это очень плохо, потому что, если просто выполняется запрос, то все нормально. А теперь добавляется сэмпл и запрос может работать дольше, потому что вам нужно будет этот url из таблицы прочитать. А потом еще и хэшировать какой-то сложной hash-функцией.
Возьмите в качестве ключа сэмплирования что-нибудь маленькое.
Вот это более распространенная ошибка. Надо, чтобы ключ сэмплирования не находился после мелко-гранулированной части первичного ключа.
Например, в первичном ключе у вас есть время с точностью до секунды. Допустим, у вас логи и это естественно. А потом вы еще добавляете какой-то hash. И это будет работать плохо, потому что ключ сэмплирования позволяет именно с диска читать мало данных. И почему это работает? Потому что данные на диске упорядочены по первичному ключу. И если есть какие-то диапазоны префикса первичного ключа, то из них можно читать меньшие поддиапазоны ключа сэмплирования. А если там эти диапазоны маленькие для каждой секунды, то из них уже нет возможности эффективно какой-нибудь кусочек прочитать, придется читать все.
В нашем примере из Яндекс.Метрики первичный ключ это счетчик, дата и только затем ключ сэмплирования. И для больших счетчиков есть много данных за каждую дату. И из этих данных можно выбрать меньше данных достаточно эффективно.

![](http://personeltest.ru/aways/habrastorage.org/webt/ho/b2/qw/hob2qwa8luxvjjiagurciarnwqc.jpeg)

Посмотрим, какие есть свойства, на которые можно полагаться:

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

![](http://personeltest.ru/aways/habrastorage.org/webt/vy/sk/-f/vysk-fuvtp8oapj1eznau8alnvq.jpeg)

А в качестве бонуса применять его можно разными способами:

Самый частый это выбираем 1/10 всевозможных данных.
Другой способ мы можем написать сэмпл какое-то большое число, например, 1 000 000. И в этом случае подмножество выберется динамически. Относительный коэффициент подберется динамически, чтобы выбрать не менее, чем 1 000 000, но и не сильно больше. Правда, в этом случае будут трудности с тем, чтобы узнать, какой же относительный коэффициент сэмплирования выбрался, какая это доля данных. Т. е. это 1 000 000 строк из 10 000 000 или из 1 000 000 000? И для этого есть никому не известная возможность это виртуальный столбец _sample_factor. И теперь вы можете написать: x умножить на _sample_factor, получится все, что нужно.
Еще одна интересная возможность это SAMPLE OFFSET. Это очень просто. Вы выбрали 1/10 данных, а теперь вы можете сказать: Дайте мне, пожалуйста, вторую 1/10 данных. И для этого просто напишите: SAMPLE 1/10 OFFSET 1/10. И так можно все эти данные перебрать.
И еще один бонус. Если у вас в таблице есть ключ сэмплирования, то вы можете теперь распараллелить запрос не только по шардам, но и по репликам каждого шарда. На разных репликах будет выбираться разный сэмпл данных так, чтобы покрыть все множество. И, соответственно, запрос будет выполняться быстрее. Или не будет? Будет, если ключ сэмплирования выбран так, что его легко читать, вычислять и это само сэмплирование, добавленное в запрос, не потребует существенного overhead.

![](http://personeltest.ru/aways/habrastorage.org/webt/lr/ms/7x/lrms7xwzzepfnseq0wroozfa69a.jpeg)

Теперь перейдем к следующей интересной возможности это комбинаторы агрегатных функций. Есть обычные агрегатные функции такие, как сумма, среднее, count distinct, который можно записать с помощью uniq. И этих uniq есть 4 разных варианта или даже больше.

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

![](http://personeltest.ru/aways/habrastorage.org/webt/hk/hl/o4/hkhlo4iys2lsy4ayd3t_mysrraa.jpeg)

Для чего это нужно? Типичное применение это сравнение разных сегментов. Мы это используем для сравнения сегментов в Яндекс.Метрике.

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

![](http://personeltest.ru/aways/habrastorage.org/webt/dp/l3/yn/dpl3ynpppht1429pv_velrujsh0.jpeg)

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

Рассмотрим пример. Есть у нас два массива разных. И мы сначала считаем количество разных массивов, потом количество разных элементов в объединении этих массивов.

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

Потом groupUniqArray. Функция собирает все уникальные значения в массив.

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

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

А groupUniqArrayArray это тоже самое, только для разных элементов.

![](http://personeltest.ru/aways/habrastorage.org/webt/1x/u3/rd/1xu3rdmjhh8oha4jgh3xmpjkohk.jpeg)

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

![](http://personeltest.ru/aways/habrastorage.org/webt/v4/xc/-w/v4xc-wwdxjjlv_n12p14b3yt59m.jpeg)

Комбинаторы агрегатных функций можно комбинировать друг с другом. Т. е. взяли агрегатную функцию sum, приписали к ней Array, а потом можно еще If приписать, а можно, наоборот. Т. е. sumArrayIf и SumIfArray.

В чем разница? В одном случае у нас будет два аргумента массива. Мы возьмем элементы этих массивов соответствующие, а массивы должны быть одинаковых длин. И один элемент это то, что суммировать, а другое это условие: надо ли суммировать этот элемент или не надо. Это у нас будет sumIfArray. Т. е. комбинатор Array применяется к функции sumIf и превращает оба аргумента в массивы.

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

![](http://personeltest.ru/aways/habrastorage.org/webt/no/_d/oq/no_doq92fks0cqhy3lcbuk7d3ju.jpeg)

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

![](http://personeltest.ru/aways/habrastorage.org/webt/ss/mo/ix/ssmoixgahzyuuqyy7i5ywamazyq.jpeg)

Агрегатную функцию можно вычислять не только до конца. Есть у вас агрегатная функция, например, сумма. Вычислили, получили сумму. Все просто.

Если есть агрегатная функция среднее, то вычислили и получили среднее, но в процессе вычисления мы будем накапливать некоторые состояния и это будет два числа. Это будет сумма и количество. А потом мы делим одно на другое.

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

Какое нужно состояние, чтобы посчитать count distinct? Hash table. Вчера был замечательный доклад про hashтаблицы и как раз на эту тему.

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

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

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

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

Давайте посмотрим, что получится.

![](http://personeltest.ru/aways/habrastorage.org/webt/jt/vw/jn/jtvwjnxcpmy4bqrmi9fz9sbumvy.jpeg)

Состояния вычислений агрегатных функций

Сначала вычислим среднее и uniq из двух чисел. Ничего интересного.

![](http://personeltest.ru/aways/habrastorage.org/webt/_z/ns/4o/_zns4o1uxlc8fhh3uoau32f26ei.jpeg)

А теперь вычислим состояние. Приписали комбинатор State. И он нам вернул какую-то вещь, которой пользоваться невозможно. Какие-то бинарные данные. Половина байт не выводится в терминал, потому что терминал кодировки UTF-8. А эти бинарные данные, естественно, не UTF-8.

![](http://personeltest.ru/aways/habrastorage.org/webt/2u/6g/d4/2u6gd4yhugkcepgmqm0l30rw7ly.jpeg)

А какого типа эти значения? Типа AggregateFunction с аргументами. Типы у нас могут быть параметризованными. Первый параметр это имя агрегатной функции, а остальные параметры это типы аргументов агрегатных функций.

![](http://personeltest.ru/aways/habrastorage.org/webt/6r/gl/da/6rgldach7ajiv8v8shqby6vrrn4.jpeg)

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

![](http://personeltest.ru/aways/habrastorage.org/webt/94/gt/ca/94gtcaif2xoz10rrmgp-sj1m40s.jpeg)

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

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

Но все не так просто. Иногда агрегировать данные все-таки полезно, ведь их становится меньше, и запрос работает быстрее.

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

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

![](http://personeltest.ru/aways/habrastorage.org/webt/xi/xj/zw/xixjzwwa_xszudzo15ijowh_-ue.jpeg)

Но есть некоторые ограничения. И что мы могли бы сделать лучше?

Сейчас эти состояния агрегатных функций это бинарные данные, которые не версионируются. И мы попали в ловушку, потому что мы не можем их поменять. Я уже рассказал про эту возможность, вы будете ее использовать. И было бы очень плохо, если вы обновили бы ClickHouse-сервер, и она сломалась бы. Поэтому нам придется добавить туда версионирование как можно скорее.
И надо определять больше случаев, когда состояния, казалось бы, разных агрегатных функций, на самом деле это одно и то же. Состояние агрегатной функции sum и sumIf это одно и то же, но сейчас они не совместимы.
Тут написано, что должна быть возможность создавать состояние агрегатной функции с помощью обычной функции*. Сейчас это тоже можно, если функция, например, arrayReduce. Берем массив, указываем, какая нам агрегатная функция нужна, и она передает все эти данные в агрегатную функцию, все элементы массива. И мы получаем значение агрегатной функции. А если в качестве агрегатной функции указать агрегатную функцию с комбинатором State, то мы получим состояние.

\* по состоянию на 2020 год, добавлена функция initializeAggregation.

![](http://personeltest.ru/aways/habrastorage.org/webt/cz/i_/lp/czi_lpqphxksyeajzf-4dassc84.jpeg)

Еще одна интересная возможность ClickHouse это настраиваемый режим консистентности.

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

Репликация conflict-free без конфликтов по причине того, что у нас нет update. Есть INSERT. Прекрасно коммутируются друг с другом, конфликтов друг с другом быть не может.

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

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

![](http://personeltest.ru/aways/habrastorage.org/webt/5q/xp/co/5qxpcojar9q_gii4at78m3npxik.jpeg)

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

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

Первая настройка это включить кворумную запись для INSERT. Минимальное количество реплик, на которые данные должны быть записаны перед тем, как клиент получит успешное подтверждение записи. Ставите insert_quorum = 2. И будет записано на две реплики.

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

А со стороны SELECT есть такая настройка, как select_sequential_consistency. Может быть, ее имя даже не совсем точное. Надо было ее назвать select_linearizability, но переименовывать уже поздно.

В этом случае, если вы отправите SELECT, то будет сделан запрос в метаданные в ZooKeeper. И SELECT будет разрешен только на репликах, которые консистентные, т. е. содержат все раннее подтвержденные INSERT, записанные с кворумом.

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

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

![](http://personeltest.ru/aways/habrastorage.org/webt/ae/nu/ez/aenuezuzio5way2okjrw_ehqy9u.jpeg)

Теперь рассмотрим еще одну интересную возможность. Это агрегация во внешней памяти.

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

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

![](http://personeltest.ru/aways/habrastorage.org/webt/qx/7y/9d/qx7y9d3l2z5x8z9_qzw1l6fo8wc.png)

Вот он считает. Красивый progress-bar, мне он очень нравится. И ClickHouse-клиент это тоже замечательная штука, я его очень люблю. К сожалению, он ничего не посчитал, потому что не хватило оперативной памяти. Он пишет, что для обработки этого запроса не хватило 9,31 гигабайта оперативной памяти, но это 10 млрд байт.

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

![](http://personeltest.ru/aways/habrastorage.org/webt/er/wh/jh/erwhjhzminuj37msfdohirzui3c.png)

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

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

![](http://personeltest.ru/aways/habrastorage.org/webt/zz/s5/g4/zzs5g4mae2l4x3j551dhl-ojps0.jpeg)

Почему бы просто не сказать: Давайте я увеличу max_memory_usage для себя?

![](http://personeltest.ru/aways/habrastorage.org/webt/o3/jr/8d/o3jr8dyl5ietrcgffprxg9igu90.png)

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

![](http://personeltest.ru/aways/habrastorage.org/webt/ry/hg/3m/ryhg3mu8-zcmgopf_jjkzj2st5g.jpeg)

Правильный способ это включить GROUP BY во внешней памяти. Что это значит? Это значит, что накапливаются какие-то данные в оперативке. Когда их становится много, мы их сбрасываем на диск. Потом снова накапливаются, снова сбрасываем на диск. Снова накапливаются, сбрасываем. А потом возьмем это все и будем мержить. Причем мержить будем с использованием маленького количества оперативки. Т. е. будем какие-то маленькие кусочки брать из каждого куска. И будем вот так мержить, и отдавать результат клиенту.

Есть две настройки, которые для этого предназначены:

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

Обычно распределенная обработка запроса устроена так. Мы с каждого сервера весь временный dataset скачиваем по сети на сервер-инициатор запроса. Все объединяем и для этого нужна оперативка. А тут он будет скачивать эти datasets по каким-то кусочкам, по buckets. И будет объединять их в потоковом режиме.

![](http://personeltest.ru/aways/habrastorage.org/webt/-a/xi/ly/-axily37jlv6ubrcxag7rrbpkka.png)

Давайте проверим. Выставляем max_memory_usage в 10 гигабайт. Сбрасывать будем 8 гигабайт (max_bytes_before_external_group_by = 8 000 000 000, distributed_aggregation_memory_efficient = 1). И у нас progress-bar завис на какой-то момент. А потом дальше продолжил идти. Что это значит? Это значит, что именно в этот момент времени данные сбрасывались на диск. И как ни странно, запрос обработался даже не сильно дольше, чем запрос без этих настроек.

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

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

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

![](http://personeltest.ru/aways/habrastorage.org/webt/tw/g5/-i/twg5-ibhhvfpf777nrg8jlzzapq.jpeg)

Давайте перейдем к следующей возможности. Это работа с географическими координатами.

Честно сказать, что ClickHouse это не какая-то geospatial-система, но наши пользователи очень хотят складывать туда всякие координаты.

У нас в компании есть сервис Метрика мобильных приложений. Она собирает логи. И, естественно, там есть координаты.

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

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

Вот пример функции pointInPolygon. Первый аргумент это кортеж: lat, lon, а дальше идет массив координат полигона. Этот массив должен быть константным*. Вы какую-то область этим полигоном зачеркиваете.

\* по состоянию на 2020 год, поддерживаются и неконстантные полигоны тоже.

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

Есть еще парочка функций, вместо которых я рекомендую использовать pointInPolygon, но они есть. Это pointInElliplses, которая позволяет задать несколько ellipses на координатах. Как не странно, ни на земле, ни на сфере, а просто на плоскости. И будьте осторожны, если у вас пользователи на Чукотке. Там есть этот разрыв координат. И просто вернет 0 или 1, если пользователь попал в эти ellipses.

И другая функция это greatCircleDistance. Это расстояние на сфере*. Две точки. И считаем, сколько на сфере будет.

\* по состоянию на 2020 год, присутствует также функция geoDistance, которая считает расстояние на WGS-84 эллипсоиде.

![](http://personeltest.ru/aways/habrastorage.org/webt/ya/ku/zd/yakuzdw7njj4gjycronkahy7byu.jpeg)

events.yandex.ru/lib/talks/5330

Еще одна экспериментальная интересная вещь это интеграция ClickHouse с моделями машинного обучения. Это не значит машинное обучение внутри ClickHouse. Это значит, что это машиннообученные модели, которые вы обучили где-то заранее. И теперь вы можете их загрузить в ClickHouse. Они будут доступны для выполнения.

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

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

Например, вам нужно предсказать CTR или предсказать вероятность покупки. Про эту тему я подробно рассказывать не буду. Если интересует, то вот замечательная ссылка: events.yandex.ru/lib/talks/5330. Там есть доклад про эту возможность и как ей пользоваться.

Сейчас у нас единственный метод машинного обучения доступен. Это CatBoost. Почему именно CatBoost? Потому что он лучше.

![](http://personeltest.ru/aways/habrastorage.org/webt/0l/fk/rs/0lfkrscyjmfqhiyn2vjuehytm5c.jpeg)

Что мы могли бы сделать, чтобы улучшить эту возможность?

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

\* эти методы уже добавлены в ClickHouse.

![](http://personeltest.ru/aways/habrastorage.org/webt/xk/bf/uu/xkbfuu4wy4l5nkwkm_d2g1b5msy.jpeg)

Следующая возможность тоже интересная. Это обработка данных с помощью ClickHouse без ClickHouse-сервера.

Есть у вас какая-то машина. Вы не хотите на нее ставить ClickHouse. Но у вас на ней есть какие-то логи. И вы админ.

Что вы обычно делаете? У вас обычно есть куча средств от grep, sed, awk или даже perl. И, например, вы где-то увидели, как круто в ClickHouse обрабатывать данные, можно написать запрос и не надо грепить и седить. Было бы очень заманчиво обрабатывать эти файлы с помощью ClickHouse без какой-либо загрузки, без преобразования. И такая возможность есть. Это утилита [clickhouse-local](http://personeltest.ru/aways/clickhouse.tech/docs/ru/operations/utilities/clickhouse-local/).

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

Указываете формат, в котором ваши данные лежат. Например, очень удобно, если у вас логи в JSON, то вы указываете формат JSONEachRow. Указываете запрос и прямо в stdin передаете ваши данные. И, пожалуйста, ваши данные обработаны. Конечно, это будет не так быстро, как если вы сначала все-таки загрузите данные в ClickHouse. Потому что эти данные надо будет распарсить и все, что нужно, с ними сделать. Но работать будет быстрее, чем awk, perl, sed. В некоторых случаях даже будет быстрее, чем grep, т. е. зависит от параметров.

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

![](http://personeltest.ru/aways/habrastorage.org/webt/tw/k1/io/twk1ioozjoqqvch1kfhcadkhs98.jpeg)

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

![](http://personeltest.ru/aways/habrastorage.org/webt/-g/ps/ts/-gpstsxuqecwzfe9oasvvs9hzaw.jpeg)

Что сейчас эту возможность ограничивает? Что надо сделать, чтобы стало удобнее?

Мы сейчас весьма строги к форматам Date и DateTime. Date у нас, например, в формате `ISO 8601`, а в DateTime не поддерживается, чтобы можно было указать плюс-минус смещение или суффикс, или дробные секунды. И, естественно, было бы очень удобно, чтобы такая возможность была*.

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

\* а уже всё есть, смотрите настройку `date_time_input_format`.

Еще очень удобно было бы, если мы добавили бы такие форматы, которые более свойственны для Hadoop инфраструктуры. Чтобы вы могли запустить ClickHouse-local в качестве MapReduce jobs прямо в Hadoop.

Было бы очень хорошо, если бы мы добавили Parquet. И сейчас эта возможность появилась в виде pull request. Наверное, скоро помержим*.

\* ура, помержили теперь возможность есть!

Еще интересный вариант, если бы добавили такой формат данных, который я про себя называю trash SQL. Представьте, что вы могли бы данные разделить каким-нибудь regexpом на столбцы, а потом подать на вход clickhouse-local. Конечно, это можно сначала сделать с помощью Awk, но иногда было бы удобно, чтобы такая возможность была прямо внутри ClickHouse*.

\* и это тоже добавили смотрите формат `Regexp`.

![](http://personeltest.ru/aways/habrastorage.org/webt/pp/7x/sz/pp7xsz7eosu7kpt4b4wtp5-6hmu.jpeg)

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

В чем суть? Конфигурация кластера в ClickHouse довольно жесткая. Т. е. вы должны указать, сколько у вас шардов, сколько реплик на каждом шарде. И все прекрасно будет работать, пока вам не надо будет это перешардировать.

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

Но проблема в том, что это работает в один поток. Будет передавать данные через один сервер по сети. И если у вас данных достаточно, вы запустите, но оно поработает, например, недельку, а потом сетевая ошибка и вам придется смотреть, какие данные скопировались, какие данные не скопировались. Или разделять это как-то вручную по партициям. Это неудобно.

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

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

Если какая-то партиция частично скопирована на другой сервер, то попытка скопировать будет повторяться. Какие-то частично скопированные данные удаляются и копируются заново.

![](http://personeltest.ru/aways/habrastorage.org/webt/qs/rp/fh/qsrpfhg3xsjuyt-s3-cywsl3o_u.jpeg)

И самое главное эта возможность проверена в production.

Мы недавно завершили операцию межкластерного копирования в Яндекс.Метрике. У нас был один кластер на 538 серверов. И мы взяли другой кластер на 240 серверов. Меньше, потому что серверы более современные и туда больше помещается. Мы изменили в наших таблицах все.

Поменяли схему шардирования. Было шардирование по сайтам, сделали шардирование по пользователям. Изменили алгоритм сжатия. На старом кластере было lz4, на новом кластере zstd и запустили это все. И оно начало копировать. И у нас были проблемы, потом еще были проблемы и еще были проблемы. Мы дорабатывали ClickHouse-copier. Потому что это серьезная задача и с первого раза, конечно, ничего не работает. И где-то через месяц все скопировалось.

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

Все, спасибо!

![](http://personeltest.ru/aways/habrastorage.org/webt/iy/7j/no/iy7jnoq_3bmfytpnco6y4p-w3pm.jpeg)

Вопросы

*Спасибо за доклад! Вопрос по поводу копира. Интересная возможность. Он в real time поддерживает дописывание данных, которые прилетели на старый кластер?*

Нет. Копирует партиции, которые не изменяются.

*Т. е., соответственно, когда вы копировали, вы уже читали с дистрибьютора между старым и новым кластером? Или читали только с нового кластера? Как это происходило?*

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

*Т. е. как раз тестировать ClickHouse?*

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

*Здравствуйте! Раз вы уже смотрите на Avro, на Parquet, на замену MapReduce и machine learning, то есть какие-то наработки для того, чтобы запускать ClickHouse как какие-то jobs под управлением YARN, под управлением Mesos? Т. е. чтобы запускались так же, как Spark, шедулилось на кластер, запускалось рядом с данными, где лежат уже на конкретных нодах с хорошей date locality и вот это все было рядышком, все быстро обрабатывалось?*

Да, такие планы есть, но с ними есть одна очень серьезная проблема, которая именно для вас очень серьезная. Дело в том, что в Яндексе не используется Hadoop и есть своя реализация MapReduce под называнием YT. И люди сделали так, чтобы ClickHouse запускался внутри YT, сам обрабатывал данные в его формате, передавал их друг с другом для распределенных запросов. Сделает ли это кто-нибудь для распространенных систем, это еще вопрос.

*Понятно. Т. е. внутри Яндекса такие наработки по поводу применения на YT и запуск под конкретный ресурс-менеджер на кластере уже есть? Т. е. то, что уже не конкретно на каждой ноде установлен ClickHouse, а это уже как tool для обработки CSV уже распространяется в виде job?*

У нас внутри компании эта задача находится в стадии эксперимента на последней стадии разработки*. И там сейчас сделано так, что какая-то часть кластера выделается. И на этой части кластера запускаются не ClickHouse-сервер, а какой-то другой бинарник, который содержит и ClickHouse, и часть YT. И оно все это читает и обрабатывает.

\* уже в продакшене.

*Спасибо за доклад! ClickHouse-local существует только в виде бинарника или есть еще какие-то модули для каких-нибудь языков или его куда-нибудь встраивать можно, например?*

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

*Спасибо! И второй вопрос есть. groupUniqArrayArray он не order делает, он их в рандомном порядке берет?*

Да, в недетерминированном порядке, в зависимости оттого, в каком порядке данные обрабатываются на разных шардах и разных потоках.

*Привет! Спасибо за доклад! У меня была практическая проблема с ClickHouse. Она немножко надуманная. Данные до этого лежали в Vertica. Люди там пытались писать запрос, но это все ужасно тормозило. Что я сделал? Я их выгрузил в CSV. Из CSV загрузил в ClickHouse. В ClickHouse заджойнил уже в другую таблицу, в которой все было в денормализованном виде. Но уже по ней запросы шли быстро. Проблемы была только в том, что если ты хочешь это все заджойнить, то тебе нужно очень много памяти, чтобы весь запрос в память влез. Я ничего лучшего не придумал, кроме, как еще памяти докинуть на этот момент. Но можно ли это как-то было решить другим путем?*

Есть ли возможность в ClickHouse выполнить JOIN так, чтобы необязательно правая таблица или результат правого подзапроса помещался в оперативку? Пока нет*. Пока в ClickHouse реализован hash JOIN, т. е. правый результат, правая часть должна помещаться в эту hash-таблицу в оперативке. Но у нас есть план это изменить. Эти планы довольно серьезные. Правда, их серьезность в том числе связана еще и с тем, что это будет не так-то просто сделать. Для этого придется серьезно менять конвейер выполнения запросов, чтобы реализовать merge JOIN, чтобы он работал нормально, и в том числе в распределенном случае.

\* уже да, смотрите настройку `join_algorithm`. И конвейер, кстати, тоже переписали.

*Спасибо! Если или, когда появятся апдейты, то будет ли еще master-master репликация, если да, то, как? Если нет, то что надо делать, чтобы она работала?*

По этому вопросу можно сказать ура, потому что сейчас апдейты находятся в pull request. Мы их планируем домержить в мастер на этой неделе. Я сейчас здесь стою, а другие люди в офисе мержат. Master-master репликация продолжит работать. Во-первых, все операции в ClickHouse линеаризуются с помощью ZooKeeper. Там есть полный порядок этих операций. Если вы будете выполнять апдейты конкурентно на разных репликах, то, соответственно, в каком-то порядке они дойдут и в каком-то порядке выполнятся.

*Здравствуйте! Спасибо за доклад! Поможет ли ClickHouse-copier решить задачу, когда необходимо вывести из шарда реплику и на новую реплику этого шарда скопировать данные, например, для проведения работ?*

Для этой задачи ClickHouse-copier не нужен, потому что это очень простая задача и типичная operation-вещь. Вы просто создаете новую реплику. У вас, допустим, было две реплики, вы создаете новую. И теперь три реплики. Старую теперь можно удалять. Или одна реплика была, вы создаете новую. И она наливает сама данные. Самое главное, если у вас сервис с репликой с концами исчез куда-то, т. е. его больше нет, то надо удалить метаданные этой исчезнувшей реплики из ZooKeeper. Там есть особенность накапливаются логи репликации и поэтому будут проблемы*.

\* уже исправлено, теперь не накапливаются.
Подробнее..

Немного Сythonа

04.09.2020 00:21:28 | Автор: admin


Дошли руки до Cythona, спасибо самоизоляции. Проблема прозаична как ускориться на python с минимальными потерями в синтаксисе. Один из подходов использование Сython (сместь С и python).
Не давала покоя публикация с громким названием отсюда habr.com/ru/company/ruvds/blog/462487
Но из содержания публикации мало что можно вынести, так как формулы и результирующая таблица неверны. Попробуем дополнить картину, начатую авторами поста и расставим точки над и.

*Тесты проводились на odroid xu4, ubuntu mate, python 2.7.17.
Cython ставится просто (pip install cython).

Будем мучить все те же числа Фибоначчи.
Создадим файлы для тестов прироста производительности.
Для языка python (test.py):
def test(n):   a, b = 0.0, 1.0   for i in range(n):      a, b = a + b, a   print (a)


Для языка cython(test2.pyx):
def test2(int n):   cdef int i   cdef double a=0.0, b=1.0   for i in range(n):      a, b = a + b, a   print (a)


Файл cython требует предварительной сборки.
Для него создадим setup.py c содержимым:
from distutils.core import setupfrom Cython.Build import cythonizesetup(ext_modules=cythonize('test2.pyx'))


И соберем:
python setup.py build_ext --inplace


Теперь возьмем файл из упомянутого поста с тестами и немного его поправим, добавив возможность вводить собственное число на старте (tests.py):
import testimport test2import timenumber = input('enter number: ')start = time.time()test.test(number)end =  time.time()py_time = end - startprint("Python time = {}".format(py_time))start = time.time()test2.test(number)end =  time.time()cy_time = end - startprint("Cython time = {}".format(cy_time))print("Speedup = {}".format(py_time / cy_time))


Посмотрим, что получилось:
python tests.py


Результаты:

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

*Можно обойтись без сборки test2.pyx с использованием setup.py, для этого необходимо просто в файл tests.py добавить строки:
import pyximportpyximport.install()

Теперь test2.pyx будет собираться на лету при каждом запуске tests.py, а файлов в папке будет меньше.
Подробнее..

Собираем данные AlphaVantage с Faust. Часть 1. Подготовка и введение

20.09.2020 14:22:27 | Автор: admin

http://personeltest.ru/aways/habrastorage.org/webt/wo/6b/ui/wo6buieqgfwzr4y5tczce4js0rc.png


Как я дошёл до жизни такой?


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


Скажу так, проект очень интересный и вполне успешно работает в других приложениях нашей команды, да и сам автор говорит о том, что смог выкатить в прод, заюзав асинхронный пул. Но, к сожалению, мне это не очень подошло, так как обнаружилась проблема с групповым запуском задач (см. group). На момент написания статьи issue уже закрыта, однако, работа велась на протяжении месяца. В любом случае, автору удачи и всех благ, так как рабочие штуки на либе уже есть в общем, дело во мне и для меня оказался инструмент сыроват. Вдобавок, в некоторых задачах было по 2-3 http-запроса к разным сервисам, таким образом даже при оптимизации задач мы создаём 4 тысячи tcp соединений, примерно каждые 2 часа не очень Хотелось бы создавать сессию на один тип задач при запуске воркеров. Чуть подробнее о большом кол-ве запросов через aiohttp тут.


В связи с этим, я начал искать альтернативы и нашёл! Создателями celery, а конкретно, как я понял Ask Solem, была создана Faust, изначально для проекта robinhood. Faust написана под впечатлением от Kafka Streams и работает с Kafka в качестве брокера, также для хранения результатов от работы агентов используется rocksdb, а самое главное это то, что библиотека асинхронна.


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


Что будем делать?


Итак, в небольшой серии статей я покажу, как собирать данные в фоновых задачах с помощью Faust. Источником для нашего пример-проекта будет, как следует из названия, alphavantage.co. Я продемонстрирую, как писать агентов (sink, топики, партиции), как делать регулярное (cron) выполнение, удобнейшие cli-комманды faust (обёртка над click), простой кластеринг, а в конце прикрутим datadog (работающий из коробки) и попытаемся, что-нибудь увидеть. Для хранения собранных данных будем использовать mongodb и motor для подключения.


P.S. Судя по уверенности, с которой написан пункт про мониторинг, думаю, что читатель в конце последней статьи всё-таки будет выглядеть, как-то так:


http://personeltest.ru/aways/habrastorage.org/webt/e5/v1/pl/e5v1plkcyvxyoawde4motgq7vpm.png


Требования к проекту


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


  1. Выгружать ценные бумаги и overview по ним (в т.ч. прибыли и убытки, баланс, cash flow за последний год) регулярно
  2. Выгружать исторические данные (для каждого торгового года находить экстремумы цены закрытия торгов) регулярно
  3. Выгружать последние торговые данные регулярно
  4. Выгружать настроенный список индикаторов для каждой ценной бумаги регулярно

Как полагается, выбираем имя проекту с потолка: horton


Готовим инфраструктуру


Заголовок конечно сильный, однако, всё что нужно сделать это написать небольшой конфиг для docker-compose с kafka (и zookeeper в одном контейнере), kafdrop (если нам захочется посмотреть сообщения в топиках), mongodb. Получаем [docker-compose.yml](https://github.com/Egnod/horton/blob/562fa5ec14df952cd74760acf76e141707d2ef58/docker-compose.yml) следующего вида:


version: '3'services:  db:    container_name: horton-mongodb-local    image: mongo:4.2-bionic    command: mongod --port 20017    restart: always    ports:      - 20017:20017    environment:      - MONGO_INITDB_DATABASE=horton      - MONGO_INITDB_ROOT_USERNAME=admin      - MONGO_INITDB_ROOT_PASSWORD=admin_password  kafka-service:    container_name: horton-kafka-local    image: obsidiandynamics/kafka    restart: always    ports:      - "2181:2181"      - "9092:9092"    environment:      KAFKA_LISTENERS: "INTERNAL://:29092,EXTERNAL://:9092"      KAFKA_ADVERTISED_LISTENERS: "INTERNAL://kafka-service:29092,EXTERNAL://localhost:9092"      KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: "INTERNAL:PLAINTEXT,EXTERNAL:PLAINTEXT"      KAFKA_INTER_BROKER_LISTENER_NAME: "INTERNAL"      KAFKA_ZOOKEEPER_SESSION_TIMEOUT: "6000"      KAFKA_RESTART_ATTEMPTS: "10"      KAFKA_RESTART_DELAY: "5"      ZOOKEEPER_AUTOPURGE_PURGE_INTERVAL: "0"  kafdrop:    container_name: horton-kafdrop-local    image: 'obsidiandynamics/kafdrop:latest'    restart: always    ports:      - '9000:9000'    environment:      KAFKA_BROKERCONNECT: kafka-service:29092    depends_on:      - kafka-service

Тут вообще ничего сложного. Для kafka объявили два listener'а: одного (internal) для использования внутри композной сети, а второго (external) для запросов из вне, поэтому пробросили его наружу. 2181 порт zookeeper'а. По остальному, я думаю, ясно.


Готовим скелет проекта


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


horton docker-compose.yml horton     agents.py *     alphavantage.py *     app.py *     config.py     database      connect.py      cruds       base.py       __init__.py       security.py *      __init__.py     __init__.py     records.py *     tasks.py *

*Всё что я отметил мы пока не трогаем, а просто создаём пустые файлы.**


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


Начнём с зависимостей и мета о проекте pyproject.toml


Далее, запускаем установку зависимостей и создание virtualenv (либо, можете сами создать папку venv и активировать окружение):


pip3 install poetry (если ещё не установлено)poetry install

Теперь создадим config.yml креды и куда стучаться. Сразу туда можно разместить и данные для alphavantage. Ну и переходим к config.py извлекаем данные для приложения из нашего конфига. Да, каюсь, заюзал свою либу sitri.


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


Что будет дальше?


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


Итак, а в этой самой следующей части мы:


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

Код проекта


Код этой части

Подробнее..

Фоновые задачи на Faust, Часть I Введение

20.09.2020 16:05:00 | Автор: admin

http://personeltest.ru/aways/habrastorage.org/webt/wo/6b/ui/wo6buieqgfwzr4y5tczce4js0rc.png


Как я дошёл до жизни такой?


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


Скажу так, проект очень интересный и вполне успешно работает в других приложениях нашей команды, да и сам автор говорит о том, что смог выкатить в прод, заюзав асинхронный пул. Но, к сожалению, мне это не очень подошло, так как обнаружилась проблема с групповым запуском задач (см. group). На момент написания статьи issue уже закрыта, однако, работа велась на протяжении месяца. В любом случае, автору удачи и всех благ, так как рабочие штуки на либе уже есть в общем, дело во мне и для меня оказался инструмент сыроват. Вдобавок, в некоторых задачах было по 2-3 http-запроса к разным сервисам, таким образом даже при оптимизации задач мы создаём 4 тысячи tcp соединений, примерно каждые 2 часа не очень Хотелось бы создавать сессию на один тип задач при запуске воркеров. Чуть подробнее о большом кол-ве запросов через aiohttp тут.


В связи с этим, я начал искать альтернативы и нашёл! Создателями celery, а конкретно, как я понял Ask Solem, была создана Faust, изначально для проекта robinhood. Faust написана под впечатлением от Kafka Streams и работает с Kafka в качестве брокера, также для хранения результатов от работы агентов используется rocksdb, а самое главное это то, что библиотека асинхронна.


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


Что будем делать?


Итак, в небольшой серии статей я покажу, как собирать данные в фоновых задачах с помощью Faust. Источником для нашего пример-проекта будет, как следует из названия, alphavantage.co. Я продемонстрирую, как писать агентов (sink, топики, партиции), как делать регулярное (cron) выполнение, удобнейшие cli-комманды faust (обёртка над click), простой кластеринг, а в конце прикрутим datadog (работающий из коробки) и попытаемся, что-нибудь увидеть. Для хранения собранных данных будем использовать mongodb и motor для подключения.


P.S. Судя по уверенности, с которой написан пункт про мониторинг, думаю, что читатель в конце последней статьи всё-таки будет выглядеть, как-то так:


http://personeltest.ru/aways/habrastorage.org/webt/e5/v1/pl/e5v1plkcyvxyoawde4motgq7vpm.png


Требования к проекту


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


  1. Выгружать ценные бумаги и overview по ним (в т.ч. прибыли и убытки, баланс, cash flow за последний год) регулярно
  2. Выгружать исторические данные (для каждого торгового года находить экстремумы цены закрытия торгов) регулярно
  3. Выгружать последние торговые данные регулярно
  4. Выгружать настроенный список индикаторов для каждой ценной бумаги регулярно

Как полагается, выбираем имя проекту с потолка: horton


Готовим инфраструктуру


Заголовок конечно сильный, однако, всё что нужно сделать это написать небольшой конфиг для docker-compose с kafka (и zookeeper в одном контейнере), kafdrop (если нам захочется посмотреть сообщения в топиках), mongodb. Получаем [docker-compose.yml](https://github.com/Egnod/horton/blob/562fa5ec14df952cd74760acf76e141707d2ef58/docker-compose.yml) следующего вида:


version: '3'services:  db:    container_name: horton-mongodb-local    image: mongo:4.2-bionic    command: mongod --port 20017    restart: always    ports:      - 20017:20017    environment:      - MONGO_INITDB_DATABASE=horton      - MONGO_INITDB_ROOT_USERNAME=admin      - MONGO_INITDB_ROOT_PASSWORD=admin_password  kafka-service:    container_name: horton-kafka-local    image: obsidiandynamics/kafka    restart: always    ports:      - "2181:2181"      - "9092:9092"    environment:      KAFKA_LISTENERS: "INTERNAL://:29092,EXTERNAL://:9092"      KAFKA_ADVERTISED_LISTENERS: "INTERNAL://kafka-service:29092,EXTERNAL://localhost:9092"      KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: "INTERNAL:PLAINTEXT,EXTERNAL:PLAINTEXT"      KAFKA_INTER_BROKER_LISTENER_NAME: "INTERNAL"      KAFKA_ZOOKEEPER_SESSION_TIMEOUT: "6000"      KAFKA_RESTART_ATTEMPTS: "10"      KAFKA_RESTART_DELAY: "5"      ZOOKEEPER_AUTOPURGE_PURGE_INTERVAL: "0"  kafdrop:    container_name: horton-kafdrop-local    image: 'obsidiandynamics/kafdrop:latest'    restart: always    ports:      - '9000:9000'    environment:      KAFKA_BROKERCONNECT: kafka-service:29092    depends_on:      - kafka-service

Тут вообще ничего сложного. Для kafka объявили два listener'а: одного (internal) для использования внутри композной сети, а второго (external) для запросов из вне, поэтому пробросили его наружу. 2181 порт zookeeper'а. По остальному, я думаю, ясно.


Готовим скелет проекта


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


horton docker-compose.yml horton     agents.py *     alphavantage.py *     app.py *     config.py     database      connect.py      cruds       base.py       __init__.py       security.py *      __init__.py     __init__.py     records.py *     tasks.py *

*Всё что я отметил мы пока не трогаем, а просто создаём пустые файлы.**


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


Начнём с зависимостей и мета о проекте pyproject.toml


Далее, запускаем установку зависимостей и создание virtualenv (либо, можете сами создать папку venv и активировать окружение):


pip3 install poetry (если ещё не установлено)poetry install

Теперь создадим config.yml креды и куда стучаться. Сразу туда можно разместить и данные для alphavantage. Ну и переходим к config.py извлекаем данные для приложения из нашего конфига. Да, каюсь, заюзал свою либу sitri.


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


Что будет дальше?


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


Итак, а в этой самой следующей части мы:


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

Код проекта


Код этой части

Подробнее..

Фоновые задачи на Faust, Часть II Агенты и Команды

23.09.2020 04:06:02 | Автор: admin

Оглавление

  1. Часть I: Введение

  2. Часть II: Агенты и Команды

Что мы тут делаем?

Итак-итак, вторая часть. Как и писалось ранее, в ней мы сделаем следующее:

  1. Напишем небольшой клиентик для alphavantage на aiohttp с запросами на нужные нам эндпоинты.

  2. Сделаем агента, который будет собирать данные о ценных бумагах и мета информацию по ним.

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

Подготовка

Клиент AlphaVantage

Для начала, напишем небольшой aiohttp клиентик для запросов на alphavantage.

alphavantage.py

Spoiler
import urllib.parse as urlparsefrom io import StringIOfrom typing import Any, Dict, List, Unionimport aiohttpimport pandas as pdimport stringcasefrom loguru import loggerfrom horton.config import API_ENDPOINTclass AlphaVantageClient:    def __init__(        self,        session: aiohttp.ClientSession,        api_key: str,        api_endpoint: str = API_ENDPOINT,    ):        self._query_params = {"datatype": "json", "apikey": api_key}        self._api_endpoint = api_endpoint        self._session = session    @logger.catch    def _format_fields(self, data: Dict[str, Any]) -> Dict[str, Any]:        formatted_data = {}        for field, item in data.items():            formatted_data[stringcase.snakecase(field)] = item        return formatted_data    @logger.catch    async def _construct_query(        self, function: str, to_json: bool = True, **kwargs    ) -> Union[Dict[str, Any], str]:        path = "query/"        async with self._session.get(            urlparse.urljoin(self._api_endpoint, path),            params={"function": function, **kwargs, **self._query_params},        ) as response:            data = (await response.json()) if to_json else (await response.text())            if to_json:                data = self._format_fields(data)        return data    @logger.catch    async def get_securities(self, state: str = "active") -> List[Dict[str, str]]:        data = await self._construct_query("LISTING_STATUS", state=state, to_json=False)        data = pd.read_csv(StringIO(data))        securities = data.to_dict("records")        for index, security in enumerate(securities):            security = self._format_fields(security)            security["_type"] = "physical"            securities[index] = security        return securities    @logger.catch    async def get_security_overview(self, symbol: str) -> Dict[str, str]:        return await self._construct_query("OVERVIEW", symbol=symbol)    @logger.catch    async def get_historical_data(self, symbol: str) -> Dict[str, Any]:        return await self._construct_query(            "TIME_SERIES_DAILY_ADJUSTED", symbol=symbol, outputsize="full"        )    @logger.catch    async def get_last_price_data(self, symbol: str) -> Dict[str, Any]:        return await self._construct_query("GLOBAL_QUOTE", symbol=symbol)    @logger.catch    async def get_indicator_data(        self, symbol: str, indicator: str, **indicator_options    ) -> Dict[str, Any]:        return await self._construct_query(            indicator, symbol=symbol, **indicator_options        )

Собственно по нему всё ясно:

  1. API AlphaVantage достаточно просто и красиво спроектирована, поэтому все запросы я решил проводить через метод construct_query где в свою очередь идёт http вызов.

  2. Все поля я привожу к snake_case для удобства.

  3. Ну и декорация logger.catch для красивого и информативного вывода трейсбека.

P.S. Незабываем локально добавить токен alphavantage в config.yml, либо экспортировать переменную среды HORTON_SERVICE_APIKEY. Получаем токен тут.

CRUD-класс

У нас будет коллекция securities для хранения мета информации о ценных бумагах.

database/security.py

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

get_app()

Добавим функцию создания объекта приложения в app.py

Spoiler
import faustfrom horton.config import KAFKA_BROKERSdef get_app():    return faust.App("horton", broker=KAFKA_BROKERS)

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

Основная часть

Агент сбора и сохранения списка ценных бумаг

app = get_app()collect_securities_topic = app.topic("collect_securities", internal=True)@app.agent(collect_securities_topic)async def collect_securities(stream: StreamT[None]) -> AsyncIterable[bool]:pass

Так, сначала получаем объект faust-приложения - это достаточно просто. Далее, мы явно объявляем топик для нашего агента... Тут стоит упомянуть, что это такое, что за параметр internal и как это можно устроить по-другому.

  1. Топики в kafka, если мы хотим узнать точное определение, то лучше прочитать офф. доку, либо можно прочитать конспект на хабре на русском, где так же всё достаточно точно отражено :)

  2. Параметр internal, достаточно хорошо описанный в доке faust, позволяет нам настраивать топик прямо в коде, естественно, имеются ввиду параметры, предусмотренные разработчиками faust, например: retention, retention policy (по-умолчанию delete, но можно установить и compact), кол-во партиций на топик (partitions, чтобы сделать, например, меньшее чем глобальное значение приложения faust).

  3. Вообще, агент может создавать сам управляемый топик с глобальными значениями, однако, я люблю объявлять всё явно. К тому же, некоторые параметры (например, кол-во партиций или retention policy) топика в объявлении агента настроить нельзя.

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

app = get_app()@app.agent()async def collect_securities(stream: StreamT[None]) -> AsyncIterable[bool]:pass

Ну а теперь, опишем, что будет делать наш агент :)

app = get_app()collect_securities_topic = app.topic("collect_securities", internal=True)@app.agent(collect_securities_topic)async def collect_securities(stream: StreamT[None]) -> AsyncIterable[bool]:    async with aiohttp.ClientSession() as session:        async for _ in stream:            logger.info("Start collect securities")            client = AlphaVantageClient(session, API_KEY)            securities = await client.get_securities()            for security in securities:                await SecurityCRUD.update_one(                    {"symbol": security["symbol"], "exchange": security["exchange"]}, security, upsert=True                )            yield True

Итак, в начале агента мы открываем aiohttp сессию для запросов через наш клиент. Таким образом, при запуске воркера, когда будет запущен наш агент, сразу же будет открыта сессия - одна, на всё время работы воркера (или несколько, если изменить параметр concurrency у агента с дефолтной единички).

Далее, мы идём по стриму (сообщение мы помещаем в _, так как нам, в данном агенте, безразлично содержание) сообщений из нашего топика, если они есть при текущем сдвиге (offset), иначе, наш цикл будет ожидать их поступления. Ну а внутри нашего цикла, мы логируем поступление сообщения, получаем список активных (get_securities возвращает по-умолчания только active, см. код клиента) ценных бумаг и сохраняем его в базу, проверяя при этом, есть ли бумага с таким тикером и биржей в БД, если есть, то она (бумага) просто обновится.

Запустим наше творение!

> docker-compose up -d... Запуск контейнеров ...> faust -A horton.agents worker --without-web -l info

P.S. Возможности веб-компонента faust я рассматривать в статьях не буду, поэтому выставляем соответствующий флаг.

В нашей команде запуска мы указали faust'у, где искать объект приложения и что делать с ним (запустить воркер) с уровнем вывода логов info. Получаем следующий вывод:

Spoiler
aS v1.10.4 id           horton                                             transport    [URL('kafka://localhost:9092')]                    store        memory:                                            log          -stderr- (info)                                    pid          1271262                                            hostname     host-name                                          platform     CPython 3.8.2 (Linux x86_64)                       drivers                                                           transport  aiokafka=1.1.6                                       web        aiohttp=3.6.2                                      datadir      /path/to/project/horton-data                       appdir       /path/to/project/horton-data/v1                   ... логи, логи, логи ...Topic Partition Set topic                       partitions  collect_securities          {0-7}       horton-__assignor-__leader  {0}         

Оно живое!!!

Посмотрим на partition set. Как мы видим, был создан топик с именем, которое мы обозначили в коде, кол-во партиций дефолтное (8, взятое из topic_partitions - параметра объекта приложения), так как у нашего топика мы индивидуальное значение (через partitions) не указывали. Запущенному агенту в воркере отведены все 8 партициций, так как он единственный, но об этом будет подробнее в части про кластеринг.

Что же, теперь можем зайти в другое окно терминала и отправить пустое сообщение в наш топик:

> faust -A horton.agents send @collect_securities{"topic": "collect_securities", "partition": 6, "topic_partition": ["collect_securities", 6], "offset": 0, "timestamp": ..., "timestamp_type": 0}

P.S. с помощью @ мы показываем, что посылаем сообщение в топик с именем "collect_securities".

В данном случае, сообщение ушло в 6 партицию - это можно проверить, зайдя в kafdrop на localhost:9000

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

2020-09-23 00:26:37.304 | INFO     | horton.agents:collect_securities:40 - Start collect securities

Так же, можем заглянуть в mongo (с помощью Robo3T или Studio3T) и увидеть, что ценные бумаги в базе:

Я не миллиардер, а потому, довольствуемся первым вариантом просмотра.

Счастье и радость - первый агент готов :)

Агент готов, да здравствует новый агент!

Да, господа, нами пройдена только 1/3 пути, уготованного этой статьёй, но не унывайте, так как сейчас будет уже легче.

Итак, теперь нам нужен агент, который собирает мета информацию и складывает её в документ коллекции:

collect_security_overview_topic = app.topic("collect_security_overview", internal=True)@app.agent(collect_security_overview_topic)async def collect_security_overview(    stream: StreamT[?],) -> AsyncIterable[bool]:    async with aiohttp.ClientSession() as session:        async for event in stream:            ...

Так как этот агент будет обрабатывать информацию о конкретной security, нам нужно в сообщении указать тикер (symbol) этой бумаги. Для этого в faust существуют Records - классы, декларирующие схему сообщения в топике агента.

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

import faustclass CollectSecurityOverview(faust.Record):    symbol: str    exchange: str

Как вы уже могли догадаться, faust для описания схемы сообщения использует аннотацию типов в python, поэтому и минимальная версия, поддерживаемая библиотекой - 3.6.

Вернёмся к агенту, установим типы и допишем его:

collect_security_overview_topic = app.topic(    "collect_security_overview", internal=True, value_type=CollectSecurityOverview)@app.agent(collect_security_overview_topic)async def collect_security_overview(    stream: StreamT[CollectSecurityOverview],) -> AsyncIterable[bool]:    async with aiohttp.ClientSession() as session:        async for event in stream:            logger.info(                "Start collect security [{symbol}] overview", symbol=event.symbol            )            client = AlphaVantageClient(session, API_KEY)            security_overview = await client.get_security_overview(event.symbol)            await SecurityCRUD.update_one({"symbol": event.symbol, "exchange": event.exchange}, security_overview)            yield True

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

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

....for security in securities:    await SecurityCRUD.update_one({            "symbol": security["symbol"],            "exchange": security["exchange"]        },        security,        upsert = True,    )    await collect_security_overview.cast(        CollectSecurityOverview(symbol = security["symbol"], exchange = security["exchange"])    )....

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

  1. cast - не блокирует, так как не ожидает результата. Нельзя послать результат в другой топик сообщением.

  2. send - не блокирует, так как не ожидает результата. Можно указать агента в топик которого уйдёт результат.

  3. ask - ожидает результата. Можно указать агента в топик которого уйдёт результат.

Итак, на этом с агентами на сегодня всё!

Команда мечты

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

После объявленных агентов в agents.py добавим функцию с декоратором app.command, вызывающую метод cast у collect_securitites:

@app.command()async def start_collect_securities():    """Collect securities and overview."""    await collect_securities.cast()

Таким образом, если мы вызовем список команд, в нём будет и наша новая команда:

> faust -A horton.agents --help....Commands:  agents                    List agents.  clean-versions            Delete old version directories.  completion                Output shell completion to be evaluated by the...  livecheck                 Manage LiveCheck instances.  model                     Show model detail.  models                    List all available models as a tabulated list.  reset                     Delete local table state.  send                      Send message to agent/topic.  start-collect-securities  Collect securities and overview.  tables                    List available tables.  worker                    Start worker instance for given app.

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

> faust -A horton.agents start-collect-securities

Что будет дальше?

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

На сегодня всё! Спасибо за прочтение :)

Код этой части

P.S. Под прошлой частью меня спросили про faust и confluent kafka (какие есть у confluent фичи). Кажется, что confluent во многом функциональнее, но дело в том, что faust не имеет полноценной поддержки клиента для confluent - это следует из описания ограничений клиентов в доке.

Подробнее..

PostgreSQL 13 happy pagination WITH TIES

23.09.2020 12:10:47 | Автор: admin
На прошедшей неделе вышло сразу две статьи (от Hubert 'depesz' Lubaczewski и автора самого патча Alvaro Herrera), посвященные реализованной в грядущей версии PostgreSQL 13 поддержке опции WITH TIES из стандарта SQL:2008:
OFFSET start { ROW | ROWS }
FETCH { FIRST | NEXT } [ count ] { ROW | ROWS } { ONLY | WITH TIES }
Что это, и как оно избавляет от проблем с реализацией пейджинга, о которых я рассказывал в статье PostgreSQL Antipatterns: навигация по реестру?



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

CREATE TABLE events(  id    serial      PRIMARY KEY, ts    timestamp, data    json);INSERT INTO events(ts)SELECT  now() - ((random() * 1e8) || ' sec')::intervalFROM  generate_series(1, 1e6);

то для организации хронологического пейджинга по ней (по ts DESC) эффективнее всего использовать вот такой индекс:

CREATE INDEX ON events(ts DESC);

и вот такую модель запроса:

SELECT  ...WHERE  ts < $1 AND  ts >= coalesce((    SELECT      ts    FROM      events    WHERE      ts < $1    ORDER BY      ts DESC    LIMIT 1 OFFSET 25  ), '-infinity')ORDER BY  ts DESC;

Старый-добрый подзапрос


Давайте посмотрим на план такого запроса, если мы хотим получить очередной сегмент от начала этого года:

EXPLAIN (ANALYZE, BUFFERS)SELECT  *FROM  eventsWHERE  ts < '2020-01-01'::timestamp AND  ts >= coalesce((    SELECT      ts    FROM      events    WHERE      ts < '2020-01-01'::timestamp    ORDER BY      ts DESC    LIMIT 1 OFFSET 25  ), '-infinity')ORDER BY  ts DESC;


[посмотреть на explain.tensor.ru]

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



Пробуем WITH TIES на зуб


Но ведь ровно для этого и нужен функционал WITH TIES чтобы отобрать сразу все записи с одинаковым значением граничного ключа!

EXPLAIN (ANALYZE, BUFFERS)SELECT  *FROM  eventsWHERE  ts < '2020-01-01'::timestampORDER BY  ts DESCFETCH FIRST 26 ROWS WITH TIES;


[посмотреть на explain.tensor.ru]

Запрос выглядит гораздо проще, почти в 2 раза быстрее, и всего лишь за один Index Scan отличный результат!

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



Ну что же, ждем официального релиза PostgreSQL 13, который запланирован на завтра.
Подробнее..

Анонс Nvidia Ampere Как Хуанг всех приятно удивил

06.09.2020 12:06:19 | Автор: admin
Никто не станет отрицать 2020 год толком не успел начаться, как сразу же вошел в историю. Сложная обстановка в мире, где политические амбиции пересекаются с экономическими антирекордами, сильно усложнила привычный расклад вещей. Но несмотря на все невзгоды, текущий год не потерял статуса самого захватывающего года новинок в сфере компьютерного железа. Новое поколение консолей, новые видеокарты и процессоры ближайшие месяцы будут жаркими и интересными для каждого энтузиаста нашей сферы. И буквально в первый же день осени, 1 сентября, в 19:00 по московскому времени неподражаемый Дженсен Хуанг на своей уютной кухне представил всему миру Ampere долгожданное семейство новых игровых видеокарт nVidia.

image

Слухи об Ampere ходили еще с начала года, но по большей части не сулили фанатам зеленых ничего хорошего. Многолетнее партнерство NVidia с TSMC, лидером рынка наиболее совершенных кремниевых пластин, оказалось под угрозой после череды ударных успехов AMD. Красный гигант занял на будущее рекордный объем будущих 7-нм пластин, оставив Дженсену Хуангу лишь толику необходимых объемов. Переговоры между компаниями длились долго, но соглашения достичь не вышло. Оставив на балансе старого партнера производство чипов для карт серии Quadro, nVidia обратилась к единственной доступной альтернативе корейскому Samsung.

В итоге в основу линейки Ampere легли чипы, произведенные по 8-нм техпроцессу Samsung не слишком энергоэффективные и уступающие изделиям TSMC, но несравнимо дешевые, а потому крайне выгодные nVidia. Впрочем, о производственных проблемах корейских фабрик знали далеко за пределами рынка, и поэтому Big Navi в очередной раз казалась куда интереснее.

Перейдем к самому мероприятию. Заметим, что 21 день. 21 год можно было воспринять как смелую попытку смены парадигмы в названиях Дженсен много издевался над любителями смаковать утечки во времена Turing, но RTX 2180 все же не случилось. Трансляцию открывал красивый ролик с ключевыми достижениями графики, отчет которых начинался с GeForce 256 и заканчивался последними картами семейства Turing. Дженсен Хуанг с улыбкой и явным удовольствием рассказывал и о трассировке лучей, и о тензорных ядрах на основе продвинутых AI-алгоритмов, и даже о том, что традиционная растеризация вот-вот достигнет потолка, после которого на сцену выйдут другие технологии. Очевидно, речь идет именно про RTX переходя к сути, Дженсен Хуанг представил ключевые преимущества Ampere.

image

RTX 2.0 В 2 раза больше производительности


Ключевой фишкой поколения Turing стало появление технологии RTX трассировки лучей в реальном времени. Кинематографичные презентации Nvidia на некоторые время пленили всех красотами отражений, но уже совсем скоро стало понятно, что мощности RT-ядер недостаточно для комфортной игры с новыми плюшками. На примере недавней демонстрации Cyberpunk 2077, где нас тоже ожидают RTX-чудеса, поколение Turing снова будет не у дел 2080Ti не обеспечивает в будущей новинке даже 60 кадров в секунду в разрешении Full HD, что уж говорить о 4К. Остается только посочувствовать тем, кто успел недавно разориться на карты уходящего поколения.

Ampere же совсем другая история. Следующее поколение RTX обещает решить проблему низкой производительности с трассировкой лучей, из-за чего старые проблемы канут в лету. Больше никаких 40 кадров в Control с RTX ON а всё благодаря серьезному росту всех компонентов системы (тензорных ядер, SM-блоков и RT-ядер). Дженсен Хуанг обещает геймерам прирост по меньшей мере в 80%, но в некоторых играх можно без труда увидеть и двухкратный рост производительности в сравнении с RTX в исполнении Turing.

На практике двухкратный рост производительности RTX 3080 на фоне RTX 2080 продемонстрировали специалисты Digital Foundry, очень независимый hands-on которых вышел практически сразу после презентации Хуанга.

image
image
image
image

И на первый взгляд это серьезное достижение, но серьезное ли? Во-первых, презентация скрыла от зрителей самое важное число FPS, ограничившись процентами. Во-вторых, скоро на рынок выйдут консоли, в которых будет собственный RTX, и их красные лучи могут оказаться не сильно хуже зеленых лучей Ampere. Само собой, такую катастрофу, как Turing, nVidia просто не могла себе позволить перед релизом новых платформ Sony и Microsoft. Снова не завидуем тем, кто купил 2080Ti в надежде на светлое будущее.

RTX I/O Догнать и перегнать фишки PlayStation 5


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

Для достойного ответа кудесникам из Sony NVidia презентовала технологию RTX I/O своеобразный подход к обработке данных, заточенный под будущие релизы. Дженсен справедливо отметил, что современные ААА-хиты (да и другие знаковые проекты) с каждым годом становятся всё объемнее, и загружать те же 200 Гб текстур с жесткого диска уже просто невозможно. Даже современные SSD испытывают трудности с такими массивами данных, и именно здесь на помощь накопителям приходит хитрая технология nVidia.

image

Во время презентации Дженсен Хуанг продемонстрировал преимущества RTX I/O одной незамысловатой схемой согласно ей, данные с накопителя кэшируются напрямую в видеопамять карты Ampere, минуя процессор и подсистему памяти. При этом главным недостатком традиционного подхода Хуанг назвал отнюдь не узкую шину, а высокую нагрузку на CPU. Так, NVME-накопители последнего поколения способны загрузить целых 24 потока, что не пройдет бесследно в тяжелых игровых сценариях. По сути, в PS5 будет реализована похожая система, но nVidia предлагает её преимущества в более упрощенном формате.

Программные новинки NVidia Reflex, Broadcast, Omniverse Machinima



Вместе с красивыми графиками Дженсен Хуанг рассказал и о новых программных технологиях, призванных сделать покупку Ampere еще выгоднее. К примеру, киберспортсмены оценят преимущества nVidia Reflex, сводящей на нет большую часть задержек компонентов ПК на уровне драйвера обычно это значение составляет 30 мс, но с помощью Reflex его можно сократить более чем наполовину. Для уверенного доминирования над противником Хуанг рекомендовал использовать технологию в паре с новеньким монитором. Такие, с частотой обновления в 360 Гц должны выйти на рынок уже в ближайшие месяцы.

image

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

Не забыли NVidia и о разработчиках, для них был представлен уникальный в своем роде инструментарий для создания эффектных роликов и презентаций, а также работы с ассетами. Он получил название Omniverse Machinima в честь тех самых легендарных машиним из светлого прошлого. В рамках анонса примером работы Omniverse послужила Mount & Blade 2: Bannerlord, на базе ассетов которого всего за несколько дней был создан кинематографичный ролик с полностью анимированным персонажем. С помощью Omniverse станет возможным моделирование мимики и анимации рта героя по голосу актера озвучки, построение общих анимаций моделей и многое другое. Этот интересный набор инструментов станет доступным для общего тестирования уже в октябре. Возможно, на горизонте маячит новый золотой век машиним.

Остается лишь один вопрос где же новая графическая оболочка драйвера для обладателей видеокарт nVidia? Почему Radeon Adrenalin всё еще является более совершенным и продвинутым, а Дженсен не спешит шагнуть в XXI век? К сожалению, ответа на эти вопросы мы не знаем до сих пор. А теперь поговорим об изменениях.

Позиционирование RTX 3000. Упрощенные названия, низкие цены


Поколение Ampere стало первым за долгие годы, в рамках которого компания NVidia отошла от традиции выпуска флагмана с индексом Ti. У такого неожиданного решения было несколько причин. Самой весомой из них стала серьезная путаница в модельном ряде, из-за чего бесконечные Ti, SUPER и модели с разной конфигурацией памяти то и дело портили настроение людям, пришедшим в магазин за видеокартой. Уложить в памяти то, какая карта быстрее и почему, было сложно и поэтому nVidia протянула руку помощи, создав максимально простую, интуитивно понятную линейку RTX 3000.

Флагманскую роль на этот раз исполнила классическая вторая карта линейки RTX 3080. Имея 10 Гб сверхбыстрой памяти GDDR6X и целых 8704 CUDA-ядра (что вдвое больше 2080Ti), новинка готова свернуть горы и заявленная двукратная производительность 2080 делает серьезную заявку на лидерство. Но зрителей анонса шокировало даже не это, а цена всего $699!

image

И нет, это не ошибка сам Дженсен признал, что Turing не смог склонить к апгрейду владельцев блестящих видеокарт семейства Pascal, но Ampere способен приятно удивить, причем сделать это недорого. И речь далеко не только о RTX 3080.
Та же RTX 3070, младшая модель из стартовой линейки, всего за $499 предлагает производительность выше 2080Ti! Да, у неё на борту всего 8 Гб обычной GDDR6 видеопамяти, но это не слишком умаляет очевидных достоинств. Вся мощь флагмана прошлого поколения за 40% от цены?

image

Но и это еще не всё. Отказавшись от бренда Titan, nVidia представила видеокарту для тех, кому важно не только играть, но и заниматься контентом на новом уровне. RTX 3090, которую даже Дженсен с трудом вытащил из печки, несет на борту 24 Гб памяти GDDR6X, и предлагает забыть о любых компромиссах.

image

На презентации nVidia для намека на мощность новинки даже показывали каких-то людей, играющих в разрешении 8К с 60 FPS. И эти люди были серьезно впечатлены! Вопросов нет, 3090 для серьезных людей. И владельцев 8К-телевизоров.
И всё же презентация состояла не только из светлого и прекрасного. Стоило снять зеленые очки и отвести взгляд от портрета Дженсена Хуанга на столе, как вопросы возникли сами собой. И сейчас мы разберем всё, что осталось за кадром анонса Ampere.

О чем умолчали на презентации Ampere? Подводные камни предзаказа


Любой анонс всегда стоит воспринимать со здравой долей скептицизма. Вспомните презентацию Turing, где слова Дженсена It just works и несколько красивых демо заставили людей до хрипоты доказывать друг другу, что за RTX будущее. И если в этом они не совсем ошиблись, то с покупкой карт поколения Turing многие допустили ошибку. Игр с поддержкой RTX в заявленном количестве так и не вышло, а те, что все-таки получили поддержку перспективной технологии, работали из рук вон плохо владельцы флагманских видеокарт продолжали играть с отключенными RTX, надеясь на будущее. И вот оно наступило и после анонса Ampere некогда счастливые владельцы 2080 SUPER или 2080Ti в спешке сбывают свои карты на Ebay по 400 долларов релиз всё ближе, и скоро былые флагманы превратятся в тыкву, обесценившись в разы.

Но что же графики? Дженсен Хуанг продажник от бога, мастер презентаций и душевный мужик, которому хочется верить всем сердцем. Но увы, в этот раз красивые значения не показывали нам самого главного, а именно значений FPS (которые были продемонстрированы позже на других каналах и только в определённых сценария и играх). Будь то множители (как в случае с RT-производительностью) или проценты (как в случае с демонстрацией от Digital Foundry) всё это никак не демонстрирует нам чистую производительность, и просто вводит в заблуждение. Не говоря уже о том, что многие цифры были получены с использованием DLSS 2.0 прекрасной технологии апскейлинга, которая творит чудеса с производительностью и картинкой но лишь там, где поддерживается. Ярким примером (показанным на презентации) был недавний ПК-релиз Death Stranding Кодзима прекрасно реализовал технологию в своем проекте, но опять же далеко не каждый разработчик на это способен, и общую картину это никак не показывает. Насколько же 3080 бодрее той же 2080 без RTX и DLSS? Ответ на этот вопрос знает лишь сам Дженсен Хуанг но вам не скажет, а то вдруг еще не предзакажете 3080.

Энергопотребление новинок тоже упоминалось только вскользь. Довольно жуткие цифры в 90% производительности на ватт могли создать у зрителей впечатление, что перед нами монстры, да еще и не слишком прожорливые, но это не так. Умеренной из трех карт является лишь RTX 3070 (с TDP в 220 Вт) её игрушечную систему охлаждения можно сразу заметить на фоне двух старших моделей. А там экспериментальная система, инновационный дизайн, внушительные габариты, и TDP, значения которых могут уходить за пределы 400 Вт (в разогнанных партнерских моделях). Такие цифры серьезно угнетают, но говорят они лишь об одном из двух или техпроцесс Samsung всё еще очень сырой, или же nVidia выжала из своих чипов всё, что могла, опасаясь то ли консолей, то ли таинственной Big Navi. В любом случае, если ваш интерес сосредоточен на 3080 или 3090, позаботьтесь и о покупке дорогого, хорошо зарекомендовавшего себя блока питания мощностью минимум на 650 Вт. KSAC не покупать!
Наконец, остается вопрос доступности. Новые видеокарты могут сильно задержаться на всех рынках за пределами США. Случай 2080Ti, которая добиралась до некоторых счастливых предзаказавших целых три месяца после релиза, намекает на то, что на этот раз картина будет как минимум схожей, а некоторые ретейлеры, почувствовав жажду наживы, будут предлагать новинки по таким ценам, что офигеет даже сам Дженсен Хуанг. Увы, современная ситуация в мире только способствует всякого рода перегибам.

К тому же никто не может сказать, какими окажутся партнерские наценки на карты семейства Ampere. Если самые дешевые 2080Ti можно было купить чуть дороже 1000 долларов, то лучшие модели продавались уже за $1200 и даже больше. Тысяча баксов за 3080? Не исключено, поэтому мы рекомендуем вам переждать релизный шторм, и купить видеокарту чуть позже по более приятной цене. Продажи Ampere стартуют совсем скоро. RTX 3080 за $800 появится в магазинах уже 17 сентября, RTX 3090 за $1500 ограниченным тиражом начнет продаваться 24 сентября, а RTX 3070 за вкусные $500 попадет на прилавки только в октябре.

image

Выводы. Ampere круто!


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

Наш видеоролик по Анонсу nVidia Ampere:
image

Автор статьи Александр Лис.
Подробнее..

Полку ARM прибыло представлен первый 64-битный процессор ARM Cortex-R82

06.09.2020 20:21:10 | Автор: admin

ARM-процессоры занимают новые для себя ниши. Это уже давно не только маломощные чипы с минимальным энергопотреблением. Недавно анонсировано третье поколение ARM-процессоров Thunder-X, о чем мы уже рассказывали. Теперь представлен процессор, который превосходит все предыдущие решения в серии R.

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



Для того, чтобы процессор мог выполнять все требуемые операции, его архитектуру несколько модернизировали и обновили. Так, Cortex-R характеризуется улучшенной аппаратной обработкой прерыванией, включая детерминированную. Были усовершенствованы аппаратные инструкции деления, защита памяти (MPU), коррекция ошибок на всех уровнях (включая как системные шины, так и кеш L1). Есть и еще одна особенность мгновенное горячее резервирование на тот случай, если выходит из строя одно из ядер.


Чип будет работать в системах с оптимизированным ПО, которое экономно использует аппаратные ресурсы. Это не пользовательские приложения. Поэтому разработчики сделали представителей серии R производительными, но без рекордных показателей, как у серии А. Напомним, что чипы серии А используются как в смартфонах, так и в серверах.

Новые процессоры ARM серии R планируется использовать, в частности, в compute-on-storage drives. Это умные накопители, которые способны выполнять сложные задачи в автономном режиме, не нагружая ими хост-системы. Соответственно, производительность хост-систем при выполнении тех же задач будет выше.

Разработчики утверждают: серия Cortex-R может брать на себя такие задачи, как транскодирование видео на лету, ускорение работы с базами данных или анализ информации в режиме реального времени. R82, по мнению разработчиков, в два раза превосходит R8 по производительности. В типовых же нагрузках при работе с нейросетями новые процессоры превосходят коллег в 14 раз.


Благодаря MMU процессор имеет возможность работать с виртуальной памятью без ограничения физическим объемом DRAM. Кроме того, R82 опционально поддерживает и выполнение специфических SIMD-инструкций (ARM NEON), обеспечивая параллелизм на уровне данных.

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

Подробнее..

Объем или частота, сколько нужно оперативной памяти в 2020 году?

18.09.2020 10:20:14 | Автор: admin


На что влияет объем и частота оперативной памяти, какая конфигурация выгоднее для игр, для приложений и одновременной работы и того и другого самый распространенный вопрос пользователей. Сколько же памяти нужно для различных задач. Чаще всего мы прибегаем к покупке стандартного набора из пары модулей DIMM частотой от 2666 МГц и выше. Устанавливают их в свой ПК и в 2-3 случаях из 10 немного их разгоняют до 3200-3800 МГц. Энтузиасты сразу выбирают комплекты с частотами более 4000 МГц. Для платформы AM4 разумный предел разгона находится в диапазоне между 3600-4000 МГц. LGA1151 разгоняется проще и лучше, позволяя достичь частоты памяти свыше 4000 МГц.
Для нашего теста мы будем использовать 2 диаметрально противоположных по свойствам комплекта памяти HyperX.

HyperX Predator DDR4 HX430C15PB3K4/64




Комплект состоит из 4 модулей по 16 ГБ каждый. В сумме 64 ГБ памяти на частоте 3000 МГц с таймингами 15-17-17, и напряжении 1.35В. Набор весьма привлекательный по цене. Купить его можно за 22-23 тысячи рублей. Частота не высокая и легко достижимая для любой платформы и процессора. В профилях для разгона содержится 2 XMP и один стандартный JEDEC:
JEDEC: DDR4-2400 CL17-17-17 @1.2В
XMP Profile #1: DDR4-3000 CL15-17-17 @1.35В
XMP Profile #2: DDR4-2666 CL15-17-17 @1.35В



HyperX Predator DDR4 HX446C19PB3K2/16




Комплект состоит из 2 модулей по 8 ГБ каждый. В сумме 16 ГБ памяти на частоте 4600 МГц с таймингами 19-26-26, и напряжении 1.5В. Для работы такого комплекта на заявленной частоте вам понадобится хорошая материнская плата, процессор Intel и удача. Дело в том, что память спокойно разгоняется до 4 ГГц без видимых осложнений с подбором правильных напряжений. Дальнейший разгон будет сдерживаться возможностями BIOS, разводки DIMM, способностями контроллера памяти в процессоре! Не все процессоры могут держать частоту памяти выше 44,2ГГц даже с поднятием напряжений Vccio и Vsa. Цены начинаются от 31-32 тысяч рублей.
В профилях для разгона содержится 2 XMP и один стандартный JEDEC:
JEDEC: DDR4-2400 CL17-17-17 @1.2В
XMP Profile #1: DDR4-4600 CL19-26-26 @1.5В
XMP Profile #2: DDR4-4000 CL19-21-21 @1.35В

Для работы этой памяти на заявленной частоте нужны адаптированные под высокую частоту комплектующие. Серия процессоров Coffe Lake в теории работает с разгоном памяти до 4,3 ГГц с некоторыми нюансами. А именно, ручной подбор вторичных таймингов для материнской платы. Потому что не все платы держат разгон памяти выше 4,2 ГГц. Но не стоит отчаиваться, при невозможности разогнать память до частоты XMP 2.0, откатитесь на 2-3 шага вниз и снизьте тайминги памяти.



4,2 ГГц с оптимизированными первичными/вторичными и третичными таймингами в сумме дадут большую пропускную способность и меньше суммарную задержку. На это способны только хорошие комплекты памяти, позволяющие с понижением частоты снижать время отклика с таймингами. Для тестов использовался XMP Profile #2: с ручными настройками DDR4-4200 CL19-21-21 @1.35В, а более низкие тайминги в итоге оказали больше влияния на результаты.



Впрочем, именно этот комплект памяти был ограничен способностями платы ASUS Hero XI, и финальной частотой стало число 4300 МГц с заводскими таймингами. На платформе Z390/B550/X570 частоту в 4,6 ГГц можно достичь без особых затруднений.



Используя подготовленные производителем для разгона материнские платы, например, серия ASUS Apex или модифицированная Gene, данный комплект не только достигает заявленных характеристик, но и может выдавать 4600 МГц на гораздо более агрессивных таймингах.

Тестовый стенд



Материнская плата: ASUS ROG Maximus XI Hero (Intel Z390, LGA 1151 v2);
Процессор: Intel Core i9-9900К (Фиксированная частота 4500 МГц, HT вкл.);
Система охлаждения: система водяного охлаждения:
Alphacool NexXxoS Monsta 360;
Scythe Minebea Silent IC 2000 об/мин x3;
EK-XRES 140 Revo D5 PWM;
EK-Supremacy EVO;
Шланги 15/19;
Термоинтерфейс: Arctic Cooling МХ-2;
Видеокарты: Nvidia GTX 2080Ti, GTX 1660 Super;
Блок питания: ASUS Thor 1200 Ватт.

Программное обеспечение:

Операционная система: Microsoft Windows 10 x64 (2004);
Драйверы видеокарты, чипсета: последние на момент тестирования.

Объем используемой памяти в играх




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



Как видите, за редким исключением все помещаются в стандартные 16 ГБ и беспокоиться о недостатке памяти не нужно. Возможно, что-то поменяется, если мы попробуем провести аналогичный эксперимент, но в системе с 16 ГБ памяти, а не 64 ГБ? И снова мы увидим похожие цифры. А ответ в таких случаях кроется в разработчиках, прекрасно понимающих среднюю конфигурацию игроков. Но учтите, что тест проводился в идеально чистых условиях, без лишних приложений. В реальной жизни пользователи привыкли все ярлыки держать на рабочем столе, а число запущенных приложений редко бывает меньше 5-10. Плюс открытые вкладки в браузере и вот уже 16 Гб быстро исчерпались. Поэтому пока 16 Гб бывает достаточно, но запас свободной памяти с каждым годом будет уменьшаться. Покупая новую систему, стоит уже смотреть в сторону 32 Гб скоро это станет нормой (с). Значит ли это, что памяти никоим образом не влияет на игровую производительность?

Игры, разрешения и частота памяти




Для понимания происходящего обратимся к сухим фактам. Более половины игроков все еще используют FullHD разрешение мониторов. Большая часть сидит на 4-ядерных процессорах. Усредненный объем оперативной памяти находится в промежутке между 8 и 16 ГБ. Если вы хотите ощутить влияние памяти на количество кадров в играх, то придется постараться.

Есть прямая зависимость: производительность процессора/видеокарты от разрешения/качества в игре. Ограничимся самым популярным FullHD, в нем влияние видеокарты и процессора распределяется в равной степени. Чем мощнее видеокарту вы используете, тем сильнее проявляется зависимость от связки процессор/память.



o Фиксированный процессор, меняем видеокарту синяя линия;
o Фиксированная видеокарта, разгоняем память, меняем процессор темно-зеленая линия;
o Фиксированная видеокарта, меняем процессор зеленая линия;
o Фиксированный процессор, разгоняем память черная линия.

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

Сколько кадров мы получим с высокочастотной памятью?


Нужно сразу пояснить, что максимальный и средний фремрейт из-за повышения частоты памяти меняются линейно и медленно. Наилучшая динамика наблюдается в регистре минимальных кадров в секунду. Там, при переходе со стандартных 2133-2400 МГц с распущенными таймингами на частоты 3600-4200 МГц и агрессивными таймингами повышается 1% мин. FPS. 1% мин. FPS это один процент минимальных кадров и считается их среднее число. А насколько сильно он подрастает, зависит от игры. В более требовательных к графике играх ожидайте прибавки до 2-4%, в старых до 15%. Естественно, максимальная польза от высокочастотной, настроенной памяти (тайминги первичные и вторичные) будет видна в низких разрешениях (FullHD) и с наиболее производительными процессорами и видеокартами.

Покупать ли дорогую память в ПК средней мощности?




Отбросим конфигурации с 6-12 ядерными процессорами, видеокартами Nvidia 20х0/Super, Radeon 5700/X и рассмотрим массовый сегмент игроков. Отбрасываем мы их потому, что большой объем видеопамяти видеокарт редко расходуется полностью. Поэтому в играх на топовых сборках ПК редко проявляется нехватка памяти. С другой стороны, именно для игр полезно устанавливать высокочастотную память, чтобы добавить совсем нелишние FPS. К массовому сегменту принято относить конфигурации с 4-ядерными процессорами и видеокартами уровня 1660 Super-GTX 2060 или Radeon 5600 (XT). Для них востребованным остается используемое разрешение FullHD. Переход от 2133-2400 МГц памяти на 3600-4200 МГц всегда сопровождается увеличением производительности в играх. Но она не столь выражена, как на более мощных конфигурациях. И снова мы возвращаемся к выбору игр для примера. Microsoft Flight Simulator (2020), Battlefield 1 (V) практически никак не отреагировали на память, в более легкие в плане графики отозвались 2-14% ростом минимального и среднего значения FPS. Не стоит забывать о принципах работы видеобуфера при нехватке Vmem. ОС создаст файл подкачки и выделит виртуальное пространство на HDDсо скоростью работы HDD. Рассматривать этот объем как полноценная замена памяти не стоит, ведь скорость обмена данными с ним очень низкая в сравнении с полноценной оперативной памятью. В результате для массовых конфигураций не стоит устанавливать DIMM совокупной емкостью менее 16 Гб, сейчас! А если вы параллельно с играми любите оставлять открытыми другие приложения, в том числе работающие в фоновом режиме и загружающие комплектующие, то пора смотреть в сторону 32 Гб.

Для печатных машинок, заточенных для серфинга и просмотра видео/фото и 8 Гб бывает недостаточно. Постепенно сайты толстеют и потребляют все больше памяти и ресурсов. Офисы и другие прикладные программы также не стоят на месте, задействуют и видеокарту, и память. Относительно распространенный Lightroom с легкостью съест 8-12 Гб на средней папке с фотографиями.

Программы и память все неоднозначно!


Лучшее применение большого объема памяти это сервера. БД, бухгалтерские сервисы и т.п. потребляют уйму памяти, но на рынке присутствует масса комплектов для простых пользователей ПК. Минимальный объем актуальный на данный момент 8 ГБ. Акцент постепенно сместился в сторону 16 ГБ и сейчас рекомендовать меньше просто опасно. Но что же с программами, неужели они, как и игры легко укладываются в типичные 16 ГБ?

Браузеры и интернет растут быстрее всех! Всем нам известна вечная проблема раздувания в объемах интернет страниц с кучей рекламы и интерактивностью. Даже десяток окон в Google Chrome легко затормозит среднюю офисную машинку до состояния Spectrumа. Не будем жаловаться на тенденции развития интернета и сайтов, а оценим, сколько влезет страниц в 16 ГБ систему



Первая сотня страниц уместилась в 8 Гб и последующие 200 никак не превысили 12 ГБ включая работающие сервисы Windows 10. По мере закрывания страниц высвобождалась и память. Переключение между закладками происходило плавно и быстро. Так что 16 ГБ памяти хватает с избытком.



Соответственно в конфигурации с 64 Гб памяти ровно такое же поведение системы быстрый отклик и гигабайты пустого пространства в памяти.



В популярном редакторе видео наличие 16 ГБ памяти сначала устраивает, но позже при работе операционная система начинает создавать файл подкачки и отзывчивость программы снижается: дольше применяются изменение в предпросмотре, интерфейс становится задумчивым. А представьте себе, что файл подкачки лежит не на быстром SSD, а на стандартном магнитном HDD! Постоянные подтормаживания растягиваются на неопределенное время!



В системе с 64 Гб памяти всегда остается запас, но Windows продолжает генерировать файл подкачки. Впрочем, сам Premiere чувствует себя прекрасно, как и его пользователь. А что на выходе? Работать комфортнее с 64 Гб памяти, но при рендере видео система с быстрой памятью и объемом в 16 ГБ сделала это быстрее на несколько минут. Вместо 1:05 мин мы получили готовое видео через 57 минут.

Проведем тест на выживаемость в Photoshop, открыв максимальное количество фотографий NEF, каждая размером 72-76 Мб, полученная с фотоаппарата Nikon D800. Сам по себе тест абсурден, т.к. не отражает реальную необходимость пользователей, но интересен своим результатом.



За 4 минуты 40 секунд открылось 150 фотографий и Photoshop их обработал. Нет ни зависаний, ни ошибок. А теперь по аналогии переходим к 16 Гб системе



Через 4 минуты и 12 секунд программа автоматически закрылась, так как произошло следующее: как только израсходовалась пустая память в дело вступила система подкачки. Свап-файл постепенно рос, потом Windows попыталась сжать его и в результате закрылся Photoshop на 70 фотографии.



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



Спустя несколько минут сокращаем объем задач и запускаем игру. Фактически, все программы остаются в фоне и архивируются в свап-файл, не производя никаких вычислений внутри них. В играх все будет отлично с числом кадров. Стоит нам задействовать автоматический скрипт для пакетной обработки фото параллельно с игрой, и время выполнения задачи увеличивается в разы! 15 фото + игра на 16 Гб 140-145 секунд. 15 фото + игра на 64 Гб 90 секунд. А с ростом объема работ на заднем плане разница только будет увеличиваться.





В программах тестирования памяти разница между 3 ГГц и 4 ГГц не столь существенна и укладывается в несколько процентов. Тоже самое происходит и в 3DMark в тесте Time Spy (тест процессора, как наиболее зависимый результат от памяти).





Выводы




Что же важнее, объем или частота? Для игр частота, при условии установленных не менее 16 Гб памяти в систему. А также не менее важно настроить у памяти и первичные и вторичные тайминги. В прикладных программах, а не синтетических бенчмарках, высокая частота памяти тоже оказывает свое влияние на результат. Сокращается время рендеринга, архивации и т.п., в любом программном обеспечении, где проявляется высокая зависимость от пропускной способности памяти. А для чего тогда нужен объем? Прежде всего, для комфортной работы с большими задачами. Это касается дизайнеров, монтажеров, редакторов. Особенно комфортно себя чувствуешь при работе с 4К видеоконтентом. Система практически не создает свап-файл для подкачки и редактор молниеносно отзывается на применение эффектов и фильтров. Наш тест с открытием более 100 NEF файлов средствами Photoshop скорее искусственный. Никто в здравом уме не будет открывать такое количество файлов в редакторе, но если возникнет необходимость, то 64 ГБ позволят это сделать. Поэтому, осмысленный подход к требуемому объему оперативной памяти залог успеха.

Нетребовательные пользователи с минимальным числом вкладок, простые документы, просмотр фото/видео без редактуры пока достаточно 8 Гб любой памяти на любой частоте.
Только игры с минимальным количеством приложений в фоне 16 Гб памяти с максимальной частотой и ручной настройкой вторичных таймингов (для систем с процессорами Intel). AMD платформы Zen +(2): одноранговая память до 4000 МГц, двухранговая память 3466 3600 МГц.
Игры и ограниченное число ресурсоемких приложений 32 Гб и это постепенно становится стандартом, ровно как 16 Гб последние несколько лет. При возможности берите память с запасом по частоте. Даже если сейчас ваша материнская плата или процессор не смогут на 100% использовать всю частоту памяти, при смене процессора, платы или платформы потом не потребуется искать более дорогие модули.
Фото/видео редакторы, CAD ПО, другие специализированные программы обрабатывающие массивные данные предъявляют повышенные требования с памяти. Особенно несладко приходится с видеоконтентом, так как 4К файлы стали распространены. А значит 64 Гб им не просто нужно, а действительно необходимо для работы.

Для получения дополнительной информации о продуктах HyperX обращайтесь на официальный сайт компании.
Подробнее..

RTX 3080 Мечта, которой нет в наличии

20.09.2020 14:22:27 | Автор: admin
Выход поколения Ampere ждали все. Предыдущий Turing оказался слишком дорогим и совершенно посредственным, а подоспевшие позже карты с припиской Super только больше запутали публику. Все с нетерпением ожидали анонса поколения, которое станет поводом для долгожданного апгрейда с блестящей линейки Pascal. И Ampere при всех слухах, тизерах и ожиданиях обещал стать именно тем, на что так рассчитывали фанаты мечтой, которая вот-вот станет реальностью.

image

Для начала напомним технические фишки Ampere. Согласно официальной презентации, которая состоялась 1 сентября, главными особенностями нового поколения стали сильно возросшие цифры CUDA-ядер (более чем в 2 раза превышающие значения Turing) и новое поколение RT-ядер, с помощью которого NVidia обещала сократить падение производительности при использовании трассировки лучей. Также особый акцент был сделан на применение технологии мультисемплинга DLSS 2.0, дебют которой в Control и Death Stranding стал настоящим потрясением для большинства игроков. Неудивительно, что именно эта технология выступает одним из ключевых преимуществ решений NVidia перед видеокартами AMD.

image

При этом у новинок есть и технические недостатки. Производством чипов Ampere на 8-нм техпроцессе занимается компания Samsung, технические решения которой значительно уступают конкурентным предложениям TSMC, 7-нм пластины которой значительно плотнее в плане бюджета транзисторов на квадратный мм, и предлагают лучшие характеристики производительности на ватт. Использование компромиссного варианта корейской компании привело к тому, что размер чипов оказался довольно велик, а энергопотребление, несмотря на смену техпроцесса, в среднем на 40% превышает значения аналогичного сегмента видеокарт поколения Turing (произведенного на базе 12-нм техпроцесса TSMC). Именно возросшие аппетиты стали причиной появления того необычного высокотехнологичного кулера референсных моделей, призванного сохранить тишину и обеспечить эффективное отведение более чем 300 Вт тепла. Напомним, что RTX 3080 в базовом исполнении без разгона потребляет 320 Вт, а при повышении частот это значение может возрасти до 360 Вт или даже выше. Для юзера это означает лишь одно простеньким блоком питания от предыдущей сборки уже не обойтись.

image

Теперь к презентации. В рамках анонса Дженсен Хуанг сделал серьезные заявления, сообщив о двукратном преимуществе новой RTX 3080 над своей предшественницей RTX 2080 (не путать с Super). Смазанные графики без упоминания кадров в секунду, а также постоянное подчеркивание 4К, трассировки лучей и технологии DLSS во многом заставили пользователей насторожиться а что если сырая производительность новинки далека от ожиданий? Многочисленные утечки и ожидания пророчили Ampere трехкратный, а то и четырехкратный прирост производительности с трассировкой лучей (а некоторые даже предполагали, что необычная компоновка кулера NVidia связана с присутствием на задней стороне платы отдельного RT-чипа, отвечающего за обработку таких инструкций). В этом плане официальная презентация оказалась куда скромнее, 80% прироста в сравнении с 2080, но даже такой шаг нельзя назвать разочарованием в рамках всего одного поколения.

Наконец, вишенкой на торте стала цена всего $800 за новую флагманскую видеокарту (как назвал её сам Дженсен) выглядело чуть ли не подарком на фоне $1000 цены 2080Ti, но многие люди в индустрии сразу подчеркнули, что первые поставки будут очень скромными, поэтому спешить с выводами не нужно. Кто-то не послушал, и ринулся в панике продавать свои карты за бесценок, уповая на удачу. На деле же всё оказалось куда хуже, но об этом чуть позже.
16 сентября обзоры RTX 3080 появились в сети. Впечатления от реальных сравнений 3080 vs 2080 оказались неоднозначными с одной стороны, преимущество над картой поколения Turing практически никогда не превышало 50-70%, с другой благодаря расширенному до 10 Гб буферу видеопамяти новинка получила возможность блеснуть более существенным приростом в 4К. Помните официальную презентацию Nvidia, где в Doom Eternal новинка отрывалась от предшественницы аж на 60 фпс? Выяснилось, что выбор этой игры был не случаен при использовании пресета графики Ultra Nightmare в 4К-разрешении игра потребляет ровно 10 Гб видеопамяти, из-за чего RTX 2080 с 8 Гб на борту испытывает острую нехватку буфера, позволяя RTX 3080 без труда показать колоссальный прирост в более чем 100%. К несчастью для будущих владельцев, эта игра оказалась единственным исключением.

image

Неизбежное сравнение с 2080Ti показало, что карта всего на 25-30% быстрее прошлого флагмана, что стало еще одним поводом для жесткой критики. Сырой 50% прирост на фоне прошлого лидера позволил бы задать совершенно новую планку производительности, недостижимую для AMD, однако теперь загадочные карты на базе RDNA2 получили возможность навязать серьезную конкуренцию серии Ampere.

Но куда большей неожиданностью стали результаты тестирования RTX 3080 в играх с поддержкой RTX и DLSS 2.0. Оказалось, что Ampere практически не получила прироста эффективности в работе алгоритмов трассировки лучей по сравнению с первым поколением RT-ядер в Turing новинки в среднем были быстрее в работе со сложными эффектами и освещением всего на 20%, а в ряде случаев цифры были даже ниже этих значений. Во многом разница в общем fps здесь достигается не благодаря продвинутым RT-ядрам нового поколения, а за счет банального повышения общей сырой производительности видеокарты это логично, но где же обещанные на презентации 80%? Даже в сравнение с 2080 разница чаще всего лежала всё в той же плоскости 50-70%. Digital Foundry, как Вы могли?

Многие возразят, сказав Да черт с ним, посмотрите на цену! Мы платим куда меньше, а получаем на 30% больше, чем раньше!. Но так ли это? Здесь всё куда сложнее, чем может показаться на первый взгляд. После старта продаж 17 сентября все RTX 3080 были разобраны за считанные минуты, и уже в скором времени на Ebay стали появляться очень выгодные предложения по покупке предзаказа, ставки на которые иногда превышали реальную стоимость новинки в десятки раз.

image
Источник

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

В России RTX 3080 тоже появилась и стоила по 85 тысяч рублей (при рекомендованной цене в 63 490 рублей). Встречались и более доступные модели, но и они предусматривали наценку в 15 тысяч рублей или выше. Кто-то уже советовал присмотреться ко всем известному немецкому магазину в желании купить заветную карту, но не обольщайтесь несмотря на вкусные цены, их, как и везде, до сих пор нет в наличии.

Что можно сказать об этом релизе? Дженсен Хуанг, как и всегда, заставил тысячи людей ахнуть и бежать в магазины, занимая очередь и перепродавая предзаказы за бешеные деньги. RTX 3080 оказалась далеко не такой производительной, как все ожидали, трассировка лучей всё еще превращает 100 фпс в 50, а очень приятная цена также далека от реальности, как второй чип на обратной стороне платы. К счастью, когда-нибудь всё образуется, и мы надеемся, что еще к Черной Пятнице магазины пополнят свои складские запасы и предложат оголодавшим ПК-боярам долгожданные новинки поколения Ampere. Ну а если нет всегда будет возможность купить Big Navi.

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

Охота за убегающей памятью в Go на этапе разработки

22.09.2020 16:11:47 | Автор: admin

Проблемы

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

  1. Снижение производительности из за расходов на выделение памяти

  2. Снижение производительности из-за расходов на сборку мусора

  3. Появление ошибкиOut of Memory , если скорость появления мусора превышает скорость его уборки

Указанные проблемы могут решаться несколькими способами:

  1. Увеличением объема вычислительных ресурсов (память, процессор)

  2. Тонкой настройкой механизма сборщика мусора

  3. Минимизацией числа побегов в кучу

В данной статье я рассмотрю только третий путь.

С чистого листа

Если все еще впереди, но уже поставлена цель добиться производительности, близкой к максимально возможной, то нужно знать в лицо главных замедлителей в плане работы с памятью. Встречаем основные конструкции, число которых следует минимизировать: make , new , map ,go . Есть и более скрытые способы учинить побег, их я рассмотрю уже в процессе "охоты", а пока - основные способы профилактики.

Вместо постоянного выделения памяти через make и new следует максимально переиспользовать уже ранее выделенное. Одним из способов добиться такого переиспользования является sync.Pool(), на habr этот способ был рассмотрен здесь. Чтобы поменьше быть КО замечу, что использовать элементы типа []byte ,как это делается в статье по сылке, не стоит - при каждом возврате будет дополнительно выделяется 32 байта памяти (для go1.14.6 windows/amd64). Мелочь, но неприятно; если стремиться к совершенству, лучше переиспользовать интерфейсы или указатели, а еще лучше использовать butebuferpool от @valyala.

С map история такая. Интенсивное использование map ведет к интенсивному выделению памяти, но это не единственная проблема. Если приложению нужен огромный кэш, и этот кэш реализован через map, то можем получить то, из-за чего Discord перешел на Rust. Т.е. на постоянное, в рамках уборки мусора, сканирование гигантского скопления указателей будут тратится ресурсы, и по каким-то метрикам система выйдет за рамки требований. Для решения этой проблемы великий @valyala сделал fastcache, там же можно найти и ссылки на альтернативные решения, и, опять же у него, наряду с другими советами по повышению производительности, можно найти достаточно детальный разбор, как использовать slices вместо maps.

С оператором go просто - все обработку нужно развести по фиксированному набору горутин и каналов. Запускать горутину на каждый запрос можно, но относительно дорого и плохо предсказуемо по расходу памяти.

Имеет смысл сделать такое замечание, и я его сделаю - предотвращение массовых "побегов" имеет свою цену, в частности, упомянутый fastcache далеко не "идиоматичен". Нам, например, идеально подходит кэш []byte->[]byteно, не факт, что это так для всех. Возможно, дешевле будет усилить аппаратную часть, а то и вообще ничего не делать - все зависит от требований к системе, те самые "rps", "95th percentile latency" и т.д. Возможно, и даже скорее всего, все будет работать и в "коробочном" варианте, да еще и с запасом. Так что будет вполне разумным сделать прототип "горячих путей" обработки и погонять на скорость. Т.е. заняться той самой "охотой".

Охота

Пойдем опять "на поклон" к Александру Валялкину и выполним:

git clone https://github.com/valyala/fasthttp

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

go test -bench=PServerGet10Req -benchmem -memprofile netmem.out

и

go test -bench=kServerGet10Req -benchmem  -memprofile fastmem.out

Первая команда запустит тесты для стандартного http.Server, вторая - для fasthttp.Server. По выводу мы заметим, что fasthttp примерно в десять раз быстрее и все операции проходят в zero-allocation режиме. Но это не все, теперь у нас есть профили netmem.out и fastmem.out. Смотреть их можно по разному, для быстрой оценки ситуации я предпочитаю такой способ:

echo top | go tool pprof netmem.out

Что дает разбивку потребления памяти по 10 самым "прожорливым" функциям:

Showing top 10 nodes out of 53      flat  flat%   sum%        cum   cum%  698.15MB 21.85% 21.85%   710.15MB 22.22%  net/textproto.(*Reader).ReadMIMEHeader  466.13MB 14.59% 36.43%   466.13MB 14.59%  net/http.Header.Clone  423.07MB 13.24% 49.67%  1738.32MB 54.39%  net/http.(*conn).readRequest  384.12MB 12.02% 61.69%   384.12MB 12.02%  net/textproto.MIMEHeader.Set  299.07MB  9.36% 71.05%  1186.24MB 37.12%  net/http.readRequest  137.02MB  4.29% 75.33%   137.02MB  4.29%  bufio.NewReaderSize  134.02MB  4.19% 79.53%   134.02MB  4.19%  net/url.parse  122.45MB  3.83% 83.36%   122.45MB  3.83%  bufio.NewWriterSize (inline)   99.51MB  3.11% 86.47%   133.01MB  4.16%  context.WithCancel   87.11MB  2.73% 89.20%    87.11MB  2.73%  github.com/andybalholm/brotli.(*h5).Initialize

Можно получить подробную схему убеганий в графическом виде через:

go tool pprof -svg -output netmem.svg netmem.out

После выполнения команды в netmem.svg будет картинка типа такой (фрагмент):

Есть и более крутой способ:

go tool pprof -http=:8088 netmem.out

Здесь, по идее, должен запуститься браузер, и этот браузер с какой-то вероятностью покажет текст: Could not execute dot; may need to install graphviz.Те, кто работает на Unix-подобных системах и так знают, что делать, пользователям же Windows могу посоветовать поставить chocolatey а затем, с правами администратора, вызвать cinst graphviz. После этого можно начать по всякому крутить профиль. Моя любимая крутилка вызывается через VIEW/Source:

Здесь, кроме очевидных убеганий через make, мы также видим большие потери на преобразование []byteв string. Операции со строками весьма затратны и, если "идем на рекорд", их следует избегать и работать исключительно с []byte. Еще одним способом "убежать", с которым встречался, является возврат адреса локальной переменной, т.е. return &localVar . Есть и другие варианты, но углубляться не буду - ваш личный профиль их покажет.

Несмотря на сокрушительное превосходство fasthttp в этом тесте, именно эту библиотеку я не рекомендовал бы использовать. Или рекомендовал бы с осторожностью - с fasthttp у вас не будет поддержки HTTP/2.0, поддержка websockets отполирована не с такой тщательностью, как сам fasthttp (на момент, когда я эту тему изучал), ну и, главное, на реальной нагрузке может и не получиться десятикратного выигрыша. У нас в одном тесте на железе типа c5.4xlarge получалось 250.000 RPS для fasthttp.Server против 190.000 RPS для http.Server . Выигрышь есть, но вам точно надо больше, чем 190.000 RPS? Тут очень многое зависит от профиля нагрузки, от того, что с этой нагрузкой делается дальше, ну и от требований к системе, само собой.

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

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

Результаты чтения "все поля большого объекта":

Avro         23394 ns/op    11257 B/opDyno_Untyped  6437 ns/op      808 B/opDyno_Typed    3776 ns/op        0 B/opFlat          1132 ns/op        0 B/opJson         87331 ns/op    14145 B/op

Результаты чтения "несколько полей большого объекта":

Avro         19311 ns/op    11257 B/opDyno_Typed    62.2 ns/op        0 B/opFlat          19.8 ns/op        0 B/opJson         83824 ns/op    11073 B/op 

Последний сценарий является для нас основным, ради которого все и затевалось, и здесь ускорение, по сравнению с тем же linkedin/goavro - весьма и весьма существенное.

Опять скажу - все зависит от конкретных данных и способов их обработки. Например, весь выигрышь на (де)сериализации можно потерять при сохранении, ибо avro часто дает "пакует" данные более компактно, чем flatbuffer.

Заключение

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

Ссылки

Подробнее..

Категории

Последние комментарии

© 2006-2020, personeltest.ru