...

суббота, 3 августа 2019 г.

Что такое музыкальное программирование — кто и почему им занимается, устраивая настоящие live-сессии

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


Фото Pixino / PD

Краткая история компьютерной музыки


Первым компьютером, который использовали для создания музыки, был CSIRAC. Его спроектировала группа австралийских инженеров в конце 40-х годов. Тогда математик Джеф Хилл (Geoff Hill) разработал специальную алгоритмическую программу для синтеза музыкальных произведений. В 1951 году CSIRAC успешно исполнил популярный «Марш полковника Боги» («Colonel Bogey March»). Но на этом его музыкальные достижения закончились.

Поэтому первым «компьютерным музыкантом» считают Макса Мэтьюса (Max Mathews). Он начал воспроизводить музыку с помощью мейнфрейма IBM. Пример такой композиции:


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

Кто программирует музыку


В качестве примера можно привести инженера Эндрю Соренсена (Andrew Sorensen). Он доктор компьютерных наук и обладатель степени бакалавра в сфере джаза. Эндрю пишет музыку с 2005 года. Примеры треков можно найти на его Vimeo-канале. Вот одна из его работ (музыка начинается со второй минуты). Такие музыканты часто устраивают live-сессии и пишут треки перед живой аудиторией. Например, этим занимается Эллисон Уокер (Allison Walker) — саунд-дизайнер в игрострое из Мельбурна. По её словам, на таких выступлениях аудитория лучше понимает, насколько сложную работу проделывает композитор цифровой музыки.

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


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

В этом направлении работает композитор Дэвид Коп (David Cope) из Университета Калифорнии. Он разработал и запатентовал алгоритм, генерирующий музыкальные треки на основе существующих. Например, на видео выше представлена композиция, основанная на творчестве Баха.

На чем пишут


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

Примером может быть ORCA — это эзотерический ЯП для создания процедурных секвенсоров, в котором каждая буква алфавита представляет собой отдельную операцию. Один из резидентов Hacker News отметил, что написание мелодии с помощью операторов ORCA напоминает сборку пазла. Исходники и подробную инструкцию можно найти в репозитории на GitHub.

Вот пример мелодии, сгенерированной на ORCA:


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

В 2014 году Эндрю выступил на конференции для разработчиков OSCON. Там он продемонстрировал возможности своего языка и написал мелодию с нуля. Запись есть на YouTube.

Также стоит выделить язык ChucK, который разработали инженеры из Принстонского университета еще в 2003 году. Он поддерживает параллельное выполнение нескольких потоков и дает возможность изменять программу прямо во время её исполнения. Подробная документация с примерами есть на сайте проекта.

Считать ли программную музыку искусством


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

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



Дополнительное чтение в нашем «Мире Hi-Fi»:

Каков юридический статус систем ИИ, создающих музыку
Рождение и смерть альбома: как менялись музыкальные форматы за последние 100 лет
Как IT-компания боролась за право продавать музыку
От критиков к алгоритмам: как демократия и технократия пришли в музыкальную индустрию
Исследование: музыка вредит творческому мышлению — обсуждаем альтернативные мнения
На IT-фестивале представили первый «гендерно-нейтральный» голосовой помощник


Let's block ads! (Why?)

Статический анализ улучшит кодовую базу сложных C++ проектов

Старые большие проекты

Постепенно и незаметно складывается ситуация, когда сложность серьёзных C++ проектов становится запредельной. К сожалению, теперь C++ программист не может полагаться только на свои силы.
Во-первых, кода стало так много, что уже невозможна ситуация, когда в проекте есть хотя бы парочка программистов, которые знают проект целиком. Например, ядро Linux 1.0.0 содержало около 176 тысяч строк кода. Это много, но была возможность поставить рядом кофе-машину и за пару недель более или менее просмотреть весь код и понять общие принципы его работы. Если же брать ядро Linux 5.0.0, то размер кодовой базы составляет уже около 26 миллионов строк кода. Код ядра вырос почти в 150 раз. Можно только выбрать несколько частей проекта и принимать участие в их развитии. Невозможно сесть и разобраться, как именно это всё работает, какие есть взаимосвязи между различными участками кода.

Во-вторых, язык C++ продолжает быстро развиваться. С одной стороны, это хорошо, так как появляются конструкции, которые позволяют писать более компактный и безопасный код. С другой стороны, в силу обратной совместимости, старые большие проекты становятся разнородными. В них переплетаются старые и новые подходы к написанию кода. Напрашивается аналогия с кольцами на срезе дерева. Из-за этого с каждым годом погружаться в проекты, написанные на C++, становится всё сложнее и сложнее. Нужно одновременно разбираться в коде, как написанном ещё в стиле C с классами, так и в современных подходах (лямбдах, семантике перемещения и т.д.). Чтобы всесторонне изучить С++ требуется слишком много времени. Но поскольку развивать проекты все равно требуется, люди начинают писать код на C++, так и не изучив до конца всех его нюансов. Это приводит к дополнительным дефектам, но нерационально из-за этого остановиться и ждать, когда все разработчики станут идеально знать C++.

Ситуация безнадёжна? Нет. На помощь приходит новый класс инструментов: статические анализаторы кода. Многие бывалые программисты в этот момент скривили губы, как будто им подсунули лимон :). Мол, знаем мы эти ваши линтеры… Сообщений много, толку мало… Да и какой же это новый класс инструментов?! Мы линтеры ещё 20 лет назад запускали.

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

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

Однако намного важнее, что современные статические анализаторы обладают обширными знаниями по паттернам ошибок. Причем анализаторы знают больше, чем даже профессиональные разработчики. Слишком сложно стало учитывать и помнить все нюансы при написании кода. Например, если специально про это где-то не прочитать, то вы никогда не догадаетесь, что вызовы функции memset для затирания приватных данных иногда исчезают, так как с точки зрения компилятора вызов функции memset избыточен. А между тем, это серьезный дефект безопасности CWE-14, который обнаруживается буквально везде. Или кто, например, знает, что опасного в подобном наполнении контейнера?

std::vector<std::unique_ptr<MyType>> v;
v.emplace_back(new MyType(123));

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

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

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

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

Кто-то может сказать, что нет смысла в специальных инструментах, так как подобные статические проверки учатся делать и компилятор. Да, это так. Однако, статические анализаторы естественно тоже не стоят на месте и как специализированные инструменты превосходят компиляторы. Например, каждый раз проверяя LLVM мы находим там ошибки с помощью PVS-Studio.

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

Если хотите поделиться этой статьей с англоязычной аудиторией, то прошу использовать эту ссылку: Andrey Karpov. Why Static Analysis Can Improve a Complex C++ Codebase

Let's block ads! (Why?)

[Из песочницы] Со склада в Дагестане — в программисты: как я стал iOS-разработчиком с нуля

История о том, как простой парень из депрессивного региона России пытается заявить о себе — и о том, что стереотипы говно.


Привет. Меня зовут Даниял Деветов, я iOS-разработчик в компании FINCH. Пока я еще junior, но крепкий, я бы даже сказал «почти middle».

Родом я из города Махачкала, столицы солнечного и очень любимого мной Дагестана. Да, того самого Дагестана, откуда, по мнению многих наших соотечественников, родом только спортсмены и ребята, которые приезжают в Москву «порядок наводить». Это, конечно, мешает карьерному развитию — стереотипы такие стереотипы! Хотя, на самом деле, Дагестан вырастил много успешных людей в IT — вроде Тагира Магомедова из Uber и Камиля Насруллаева из Mail.ru.

Мой путь в IT начался в 18 лет. Сейчас мне почти 20, и у меня есть бэкграунд в 1,5 года беспрерывного продакшн опыта. С того момента, как я стал программистом, очень часто поступают вопросы от земляков: «Вау, как круто, сидеть весь день за компом и зарабатывать кучу бабла — как бы и мне так сделать?». Там, откуда я родом, люди часто не верят, что можно пробиться в такую престижную профессию и работать в теплом офисе за хорошие деньги.

В этой статье я расскажу, как от оператора 1С на пивном складе я дорос до iOS-программиста в крупной московской компании. Надеюсь, что мой пример вдохновит кого-то еще из депрессивных регионов развиваться и не сдаваться!

Место, где ничему не учат


Мой карьерный путь начался с того, что я СОИЗВОЛИЛ прийти на пары в свой колледж. На самом деле, учиться я особо не планировал, и уж тем более получать высшее образование. Еще в 13 лет я подумал: «нафига мне универ, до которого еще учиться и учиться? Надо быстрее бабки зарабатывать!». Так что даже особо не думал, куда отдавать документы после окончания 9 классов.

Я поступил в бизнес-колледж при Государственном Дагестанском Университете Народного Хозяйства. Откуда там появился факультет программирования — не знаю. Вообще, колледж славился тем, что туда мог попасть любой. На поток в 700 человек набиралось 1200, и на первом курсе отсеивали человек 300 самых «отбитых» студентов.


Снаружи колледж выглядит как-то так

На факультет программирования обычно поступали те, кто после окончания школы не знал, чего хочет от этой жизни. При этом родители говорили им, что нужно поступить в вуз и получить диплом, хорошую профессию и так далее. При поступлении они видят программирование, думают «Это сейчас модно, ну и я вроде умею работать с компом» — и подают документы. Поэтому, если в потоке на факультете 1000 человек, то в лучшем случае около 50 вообще понимают, что же такое программирование. И при удачном стечении обстоятельств 10-15 из них могут куда-то пробиться. Представьте, каковы были мои шансы!

Самое обучение выглядит так. На первых двух курсах ничего профильного нет. На третьем курсе начинается программирование — приходит препод и начинает читать лекции про Pascal ABC. Во втором семестре третьего курса появляется препод, который рассказывает, что такое C#. На четвертом курсе материал повторяется.

Мне повезло — один крутой преподаватель, Гасан Гусейнович, заметил, что я быстро схватываю материал. Спасибо моему старшему брату — еще в 12 лет он показал мне, что такое программирование. Я ходил к нему по вечерам, когда он приходил с учебы, чтобы познавать азы Delphi и Pascal. Тот же брат подарил мне комплектующие для компьютера — я собрал его самостоятельно и стал изучать на нем Delphi 7.

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

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

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


На курсах собирались группы по 8-10 человек. В программе была разработка на iOS и Android — нужно было выбрать направление. Я доверил выбор своему другу и одногруппнику, которого тоже сподвиг пойти на курсы. Я рассчитывал, что если нас не возьмут на работу, то мы сможем придумать что-то вдвоем. И для блага будущего проекта стоит выбрать разные направления.

В итоге, он стал учиться Android-разработке, а меня отправил на iOS. Смешно, что я пошел на курсы iOS-разработки без мака, без айфона. Я до курсов ни разу и не пользовался собственным айфоном — у меня был Xiaomi mi5. И первый «яблочный» телефон у меня появился только спустя полгода программирования, когда удалось наскрести на него денег.

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

И тут меня поджидала еще одна проблема — доступ к компьютеру. Я работал на Mac mini, который находился в университете, при котором работал наш колледж. У DIIT был отдельный кабинет, где проходили занятия по iOS и Android-разработке: в понедельник и среду — iOS, во вторник и четверг — Android. Получается, для самостоятельного изучения оставались дни с пятницу по воскресенье и время после занятий. С работой это было трудно совместить, я уж молчу про учебу в колледже.


Класс в колледже, где проходила стажировка от ребят из DIIT

А я, конечно, работал — в фирме «Калибр», это местный пивной дистрибьютор. Устроился я туда между вторым и третьим курсами колледжа. Я выполнял обязанности оператора 1С — по сути, мне нужно было помогать логисту с подготовкой документов: накладными, «закрывашками». Всего я проработал там 1,5 года.

Зарплатная вилка менялась нечасто. Сначала мне платили 13 тысяч, но спустя 5 месяцев я сказал, что устал, и мне нужна прибавка. Подняли до 14 тысяч (и на том спасибо!). Потом был период, когда нас сильно завалили — я приходил на работу в 18 вечера и уходил в 1-2 ночи. Тогда мне еще раз подняли зарплату, уже до 15 тысяч.

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

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

Челлендж: купить макбук студенту в Дагестане


Мне не нравилось, что я так сильно зависел от университетских компов. Нужно было выкручиваться и покупать мак. Рисковый шаг для всех начинающих прогеров, а в моем случае — рассрочка на 2 года и ноутбук, который стоит как ~10 моих зарплат на тот момент. Для многих 100 тысяч российских рублей покажутся не такой уж большой суммой, но для меня, работающего с 16 лет за зарплату в 13-15 тысяч рублей, это были ОЧЕНЬ большие деньги.

Я понимал, что мне нужно учиться с 8:30 до 15:00 и работать с 18:00 до 24:00 (а иногда и до утра). В промежутке — в основном, в дороге — я должен был что-то изучать. Так что пришлось разориться на собственный макбук. Только так можно было попасть на стажировку.

Невероятно, но факт — мне это все-таки удалось. По итогу курса я все же попал на работу в DIIT Center — единственную дагестанскую IT-компанию с мобильной разработкой.

Это был мой билет в светлое будущее — было важно его не просрать.

Работа в DIIT и первые факапы


Обычно на стажировку в DIIT приходят люди, которые вообще НИЧЕГО не знают. Ну, может быть, только циклы. Стандартная стажировка длится от месяца до трех — ну, или до тех пор, пока кандидат не будет готов к настоящим продакшн задачам. Мне для начала дали несколько задач, в основном про архитектуру (MVP), кнопочки и Label.

Первое время я не понимал язык людей, с которыми работал. Дикая смесь русского, английского и различных терминов — в это сложно въехать людям, которые еще «не в теме». Спасибо товарищам, которые поддержали в этот момент! Отдельный респект Марату Магомедову за то, что поверил в меня, Арсену Абдурашидову и Магомеду Магомедову — за прием новичка в свой клуб «анонимных алкоголиков», Ризвану Гаджикеримову и Расулу Татаеву — за обучение и помощь по всем вопросам.

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


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

Я сильно воодушевился, потому что к проекту подключили бест оф зе бест сотрудников — back-end, android, PM — и МЕНЯ. Мы работали три месяца, а потом из-за закрытия проекта вернулись в свои команды. Начался простой.

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

Ставка джуна была 250 рублей в час. Я за месяц получил ровно 6250 рублей — в два раза меньше, чем в пивной компании! И это та самая престижная и перспективная профессия программиста, о которой все говорят?!

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

Я остался работать в DIIT. Спустя 10 месяцев, из-за ухода некоторых ребят, меня назначили тимлидом iOS-направления. Да, тимлид-джун! Звучит смешно, но по меркам Дагестана я был каким-никаким миддлом — а компании в тот момент очень нужен был человек с опытом, чтобы обучать новичков.


Иногда мы проводили общие лекции/презентации для всех сотрудников компании. На фото лекция про iOS-разработку

В Дагестане компании-разработчику очень сложно найти высокооплачиваемые проекты и платить сотрудникам «московские» зарплаты. Честно говоря, это невозможно, наверное, как и в любом другом регионе нашей необъятной страны. Поэтому все смышленые разработчики в DIIT, набирая какой-то опыт, уходили из компании и ехали покорять столицу — иногда и другие города, но 90% ехали в Москву — зарабатывать бабки.

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

Переезд в Москву: непонятно, далеко и холодно


Когда увольнялся последний оставшийся iOS-разработчик, кроме меня, я тоже подумал, что пора двигаться дальше. Я несколько дней писал своим друзьям, которые уже уехали в Москву, спрашивал, как обстановка, как они обустроились на новом месте. Я сильно переживал и метался, а они были готовы купить билет в Махачкалу, чтоб отвесить юному товарищу пару хороших ЧАПАЛАХОВ (пощечина — прим.ред.). В конце концов, они привели меня в чувство, и я начал поиск работы в столице.

За месяц поиска работы я отправил чуть больше 40 откликов на разные вакансии. Когда мне не отвечали, я очень переживал, думал — может, хотя бы в тестировщики податься? Привет всем HR-ам, которые видели мое резюме и не отвечали. Я предложил свою кандидатуру 40 компаниям, и знаете что? Мне ответили только 4. До собеседования дошло только в двух местах.

На заметку всем, кто пойдет моим путем — не ждите, что вам будут хорошо отвечать на ваши отклики. Рассылайте как можно больше резюме, рассчитывайте на конверсию не больше 10%. И не сдавайтесь — ваша компания рано или поздно вас найдет!

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

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

Собеседование в FINCH было нервным — я ответил только на 60% вопросов. К концу собеседования я уже совсем упал духом, думал остаться в Махачкале, сидеть на жопе ровно и не прыгать выше головы — и тут мне говорят «Даниял, думаю, ты нам подходишь, сколько ты хочешь получать?».

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

Это прекрасное чувство, когда твои навыки оценивают по достоинству — для меня на тот момент это было просто ОГРОМНАЯ сумма. Для справки, за год в Дагестане я заработал две такие зарплаты. Бедные регионы богатой страны!

Спустя три недели я уже был в Москве. До этого я выезжал из Дагестана один раз — в Астрахань, но так как это было в детстве, я ничего не помню. То же самое с самолетом — летел в первый раз. Так как дело было в январе, я безумно мерз, хотя разница в температуре была всего 5-10 градусов.

В аэропорту я понял, что ехать до моего нового дома по расстоянию столько же, сколько у нас ехать из одного города в другой — 100 км. Это дорога из Шереметьево до Первомайской. Махачкала тоже большой город-миллионник, но его можно полностью обойти за 12 часов пешком — поэтому московские расстояния меня порядком впечатлили.

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

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

Пацан к успеху пришел


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

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

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

Таких как я называют «маменькиными сынками» — меня сильно держат родственные узы. Мне было трудно приехать в Москву — ведь я всю жизнь жил с семьей, а не один. А тут ты приезжаешь в чужой город, и тебе нужно все делать самому — работать, заниматься домом, а родственники, которые могли бы поддержать, далеко. До мая я думал — может, вернуться домой? Но в итоге решил, что опыт, который я получу здесь, я больше нигде в России не получу. Я должен оставаться в Москве.

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

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

Вместо вывода


Всех, кто хочет чего-то добиться, призываю — не надо сидеть и ждать манны небесной, не надо сомневаться, не надо думать, что может пойти не так. Берите и делайте! Я просто пошел и сделал все, что от меня требовалось, ничего сверхъестественного — учился, работал, выполнял все задания. И теперь я зарабатываю хорошие деньги, работаю в ТОПОВОЙ компании и воплощаю в жизнь свои цели и мечты.


Фото в фирменном мерче компании

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

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

Место для респектов: спасибо Александру Антонову за то, что я все-таки попал в FINCH, Антону Логинову за то, что терпит и обучает — да и в принципе всей команде, в которой я работаю сейчас. Радует, что на рынке IT есть компании, не подверженные стереотипам — и надеюсь, таких будет становиться все больше!

Let's block ads! (Why?)

[Из песочницы] Шифрование конфигурационных файлов

Предыстория


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

Key Container


В каждой ОС Windows есть наборы сгенерированных ключей. Ключ генерируется либо на учетную запись, либо на машину. Ключи сгенерированные на машину можно посмотреть по этому пути C:\ProgramData\Microsoft\Crypto\RSA\MachineKeys. Сюда и отправиться ключ который мы создадим далее.

Создание ключа


Запускаем cmd от администратора и переключаемся в директорию с aspnet_regiis, у меня это C:\Windows\Microsoft.NET\Framework64\v4.0.30319

Выполняем команду

aspnet_regiis -pc "TestKey" -exp

exp — добавляется чтобы можно было экспортировать ключ в дальнейшем
TestKey — название нашего Key Container

Экспорт ключа


Команда
aspnet_regiis -px "TestKey" С:\TestKey.xml -pri

TestKey — название Key Container
С:\TestKey.xml — путь куда будет экспортирован файл
pri — добавить в экспорт приватный ключ

Импорт ключа


Команда
aspnet_regiis -pi "TestKey" С:\TestKey.xml

TestKey — название Key Container
С:\TestKey.xml — путь откуда будет экспортирован файл

Настройка прав


Чтобы ваше приложение или IIS могли работать с key container нужно настроить им права.

Делается это командой

aspnet_regiis -pa  "TestKey" "NT AUTHORITY\NETWORK SERVICE"

TestKey — название Key Container
NT AUTHORITY\NETWORK SERVICE — кому будет выдан доступ до ключа

По умолчанию в IIS стоит ApplicationPoolIdentity для пула.

В документации Microsoft (см. ссылка 2) ApplicationPoolIdentity описан как:

  • ApplicationPoolIdentity: When a new application pool is created, IIS creates a virtual account that has the name of the new application pool and that runs the application pool worker process under this account. This is also a least-privileged account.

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

Добавление секции в config

<configProtectedData defaultProvider="RsaProtectedConfigurationProvider">
<providers>
<add name="CustomRsaProtectedConfigurationProvider" 
type="System.Configuration.RsaProtectedConfigurationProvider,System.Configuration, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" 
description="Uses RsaCryptoServiceProvider to encrypt and decrypt" 
keyContainerName="TestKey" 
cspProviderName="" 
useMachineContainer="true" 
useOAEP="false"/>
</providers>
</configProtectedData>


Также key container и RsaProtectedConfigurationProvider определены глобально в файлах

C:\Windows\Microsoft.NET\Framework\v4.0.30319\Config\machine.config, C:\Windows\Microsoft.NET\Framework64\v4.0.30319\Config\machine.config

<configProtectedData defaultProvider="RsaProtectedConfigurationProvider">
    <providers>
        <add name="RsaProtectedConfigurationProvider" type="System.Configuration.RsaProtectedConfigurationProvider,System.Configuration, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" description="Uses RsaCryptoServiceProvider to encrypt and decrypt" keyContainerName="NetFrameworkConfigurationKey" cspProviderName="" useMachineContainer="true" useOAEP="false"/>
 
        <add name="DataProtectionConfigurationProvider" type="System.Configuration.DpapiProtectedConfigurationProvider,System.Configuration, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" description="Uses CryptProtectData and CryptUnProtectData Windows APIs to encrypt and decrypt" useMachineProtection="true" keyEntropy=""/>
    </providers>
</configProtectedData>


Шифрование


Само шифрование можно сделать тремя способами

Шифрование через командную строку

aspnet_regiis.exe -pef connectionStrings С:\Site  -prov "CustomRsaProtectedConfigurationProvider"

С:\Site — путь до файла с конфигом.

CustomRsaProtectedConfigurationProvider — наш провайдер указанный в конфиге с названием key container.

Шифрование через написанное приложение

private static string provider = "CustomRsaProtectedConfigurationProvider";
 
public static void ProtectConfiguration()
{
    Configuration config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
 
    ConfigurationSection connStrings = config.ConnectionStrings;
 
    if (connStrings != null && !connStrings.SectionInformation.IsProtected && !connStrings.ElementInformation.IsLocked)
    {
        connStrings.SectionInformation.ProtectSection(provider);
 
        connStrings.SectionInformation.ForceSave = true;
        config.Save(ConfigurationSaveMode.Full);
    }
}
 
public static void UnProtectConfiguration(string path)
{
    Configuration config = ConfigurationManager.OpenExeConfiguration(path);
 
    ConfigurationSection connStrings = config.ConnectionStrings;
 
    if (connStrings != null && connStrings.SectionInformation.IsProtected && !connStrings.ElementInformation.IsLocked)
    {
        connStrings.SectionInformation.UnprotectSection();
    }
}


Велосипед


Когда есть трансформация файлов и нужно зашифровать секции отдельно от всего конфига, то подойдет только самописный вариант. Мы создаем экземпляр класса RsaProtectedConfigurationProvider, берем узел из xml и шифруем его отдельно, затем заменяем в исходном xml узел на наш зашифрованный и сохраняем результат.
public void Protect(string filePath, string sectionName = null)
{
    XmlDocument xmlDocument = new XmlDocument { PreserveWhitespace = true };
    xmlDocument.Load(filePath);

    if (xmlDocument.DocumentElement == null)
    {
        throw new InvalidXmlException($"Invalid Xml document");
    }

    sectionName = !string.IsNullOrEmpty(sectionName) ? sectionName : xmlDocument.DocumentElement.Name;

    var xmlElement = xmlDocument.GetElementsByTagName(sectionName)[0] as XmlElement;

    var config = new NameValueCollection
                     {
                         { "keyContainerName", _settings.KeyContainerName },
                         { "useMachineContainer",  _settings.UseMachineContainer ? "true" : "false" }
                     };
    var rsaProvider = new RsaProtectedConfigurationProvider();
    rsaProvider.Initialize(_encryptionProviderSettings.ProviderName, config);
    var encryptedData = rsaProvider.Encrypt(xmlElement);

    encryptedData = xmlDocument.ImportNode(encryptedData, true);

    var createdXmlElement = xmlDocument.CreateElement(sectionName);
    var xmlAttribute = xmlDocument.CreateAttribute("configProtectionProvider");
    xmlAttribute.Value = _encryptionProviderSettings.ProviderName;
    createdXmlElement.Attributes.Append(xmlAttribute);
    createdXmlElement.AppendChild(encryptedData);

    if (createdXmlElement.ParentNode == null
        || createdXmlElement.ParentNode.NodeType == XmlNodeType.Document
        || xmlDocument.DocumentElement == null)
    {
        XmlDocument docNew = new XmlDocument
                                 {
                                     InnerXml = createdXmlElement.OuterXml
                                 };
        docNew.Save(filePath);
    }
    else
    {
        xmlDocument.DocumentElement.ReplaceChild(createdXmlElement, xmlElement);
        xmlDocument.Save(filePath);
    }
}


Ссылки


1. docs.microsoft.com/en-us/previous-versions/53tyfkaw
2. support.microsoft.com/en-za/help/4466942/understanding-identities-in-iis

Let's block ads! (Why?)

[Из песочницы] Ограничения машинного обучения

Привет, Хабр! Представляю вашему вниманию перевод статьи “The Limitations of Machine Learning“ автора Matthew Stewart.

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


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

В наше время гипербола о машинном обучении и искусственном интеллекте повсеместна. Возможно, это правильно, учитывая, что потенциал для этой области огромен. За последние несколько лет число консалтинговых агентств по ИИ возросло, и, согласно отчету Indeed, количество рабочих мест, связанных с ИИ, увеличилось на 100% в период с 2015 по 2018 годы.

По состоянию на декабрь 2018 года Forbes обнаружил, что 47% бизнеса имеют по крайней мере одну возможность использования ИИ в своем бизнес-процессе, a в отчете Deloitte говорится, что уровень проникновения корпоративного программного обеспечения со встроенным ИИ и облачных сервисов разработки ИИ, достигнет примерно 87 и 83 процентов соответственно. Эти цифры впечатляют — если вы планируете сменить карьеру в ближайшее время, ИИ кажется неплохой областью.

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

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

Ограничение 1 – Этика


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

Хотя эта идея может показаться смешной, но помните, когда вы в последний раз ездили в отпуск и следовали инструкциям GPS, а не своим собственным суждениям о карте — ставите ли вы под сомнение оценку GPS? Люди буквально въезжали в озера, потому что слепо следовали инструкциям своего GPS.

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

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

Кто виноват, если моя самоуправляемая машина кого-то убьет на дороге?

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

Ограничение 2 — Детерминированные проблемы


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

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

Однако все становится немного интереснее, когда дело доходит до компьютерного моделирования.

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

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

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

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

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

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

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

Ограничение 3 – Данные


Это самое очевидное ограничение. Если вы плохо «кормите» модель, то это даст только плохие результаты. Это может проявляться двумя причинами: нехватка данных и нехватка достоверных данных. Если у вас таких проблем нет, то вы смело можете изучать обработку больших массивов данных на Телеграм-канале «Big Data Books», где публикуются различные книги и ресурсы по Big Data.

Недостаток данных


Многие алгоритмы машинного обучения требуют больших объемов данных, прежде чем они начнут давать полезные результаты. Хорошим примером этого является нейронная сеть. Нейронные сети – это data-eating машины, которые требуют большого количества обучающих данных. Чем больше архитектура, тем больше данных требуется для получения жизнеспособных результатов. Повторное использование данных — плохая идея, всегда предпочтительнее иметь больше данных.
Если вы можете получить данные, то используйте их.

Недостаток хороших данных


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

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

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

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

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

Ограничение 4 — Неправильное применение


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

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

Для стохастических (случайных) систем все немного менее очевидно. Кризис машинного обучения для случайных систем проявляется двумя способами:

  • P-hacking
  • Объем анализа

p-hacking


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

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

Объем анализа


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

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

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

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

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

Ограничение 5 – Интерпретируемость


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

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

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

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

Вывод


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

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

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

Let's block ads! (Why?)

Первый прототип: Unikernels как этап в эволюции Linux

В начале июля группа инженеров из Red Hat и Бостонского университета выпустила whitepaper, в котором предложила сменить монолитное ядро Linux на архитектуру unikernels. Мы решили разобраться в материале и обсудить реакцию ИТ-комьюнити на это предложение.


Фото — Eamonn Maguire — Unsplash

Unikernels как альтернатива


Известно, что Linux использует монолитное ядро. Оно управляет процессами, сетевыми функциями, периферией и доступом к файловой системе. Однако как пишут авторы статьи из Red Hat и Бостонского университета (стр.1), такая структура имеет свои недостатки. В частности, высокопроизводительные приложения вынуждены использовать фреймворки вроде DPDK и SPDK, чтобы получить беспрепятственный доступ к устройствам ввода/вывода в обход ядра.

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

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

Ресурсы такой системы расходуются эффективнее. Также unikernels обладают более высокой производительностью, по сравнению с монолитной архитектурой ядра. Причина — упрощение IO-путей, так как все данные и файлы размещаются в едином адресном пространстве. Также пропадает необходимость переключать контекст между пользовательским пространством и пространством ядра.

Команда инженеров из Бостонского университета и Red Hat разработала прототип Linux на базе unikernels. Операционная система получила название Unikernel Linux (UKL).

Что сделали инженеры


Как заявляют разработчики (стр.3), они изменили всего одиннадцать и добавили двадцать новых строк кода в Linux kernel v5.0.5 и glibc. «Классическое» ядро сохранило работоспособность — пользователь может выбрать способ сборки (UKL или нет).

Авторы создали небольшую UKL-библиотеку, в которой разместили специальные «заглушки», которые маскируют неиспользуемые системные вызовы. Также они модифицировали линкер ядра, чтобы определять новый тип сегментов, например TLS (thread local storage) из бинарников ELF. Еще был модифицирован процесс сборки, который теперь объединяет код приложения, glibc и UKL-библиотеку в один бинарный файл.

Инженеры работают над исправлением ряда недостатков. Например, они планируют переместить TLS-память из пространства ядра и отказаться от vmalloc при управлении распределением памяти, чтобы упростить систему.

Мнения


Разработчики Red Hat говорят, что UKL может стать полноценной альтернативой для запуска процессов, работающих с аппаратным обеспечением напрямую (в обход ядра). Авторы оригинальной статьи заявляют (стр.2), что сервис кэширования memcached под unikernels работает на 200% быстрее, чем под Linux.
В целом об инициативе авторов оригинальной статьи положительно отозвалось и ИТ-сообщество. Резиденты Hacker News отметили, что архитектура unikernels значительно повысит безопасность программной среды. В случае взлома приложения хакер получит доступ лишь к его бинарнику.


Фото — Jack Young — Unsplash

Один из резидентов Hacker News даже предложил радикальное решение — переписать ядро Linux под unikernels с нуля на Rust. По его словам, язык решит проблему с большим количеством багов, связанных с безопасностью памяти. Другой пользователь назвал идею хорошей, однако предложил подождать несколько лет, пока разработчики языка разберутся с нестабильностью библиотек. Хотя один энтузиаст уже пишет свою операционную систему на Rust. Исходники можно найти на GitHub.

Другие реализации


UKL — не единственная реализация операционной системы на базе unikernels. Например, похожее решение разрабатывает группа инженеров из Политехнического университета Виргинии, компании Qualcomm и Рейнско-Вестфальского технического университета Ахена в Германии. Их легковесное ядро называется HermiTux. Оно позволяет быстро запускать приложения поверх гипервизора — по словам авторов, время загрузки не превышает 0,1 сек. Потребление памяти в тестовом окружении составляет 9 Мбайт, что в десять раз меньше, чем у классического ядра Linux.

Также имеет смысл отметить ОС MirageOS, разработанную на OCaml. Ядро может запускаться поверх гипервизоров Xen, KVM, BHyve и VMM (OpenBSD), а также на мобильных платформах. Система поддерживает несколько десятков библиотек на языке OCaml для выполнения сетевых операций (DNS, SSH, OpenFlow, HTTP, XMPP), работы с хранилищами и параллельной обработки данных. Можно сказать, что MirageOS — это один из первых успешных unikernels-проектов. Интересно, что его сайт с блогом также реализован как unikernel.

Эти операционные системы уже используются в продакшн-средах многими организациями — например, Кембриджским университетом, IBM, Ericsson и Docker. Есть вероятность, что скоро к этим ОС присоединится новая — Unikernel Linux.



О чем мы пишем в корпоративном блоге:

Let's block ads! (Why?)

Американские смартфонофермеры в 2019 году ощутили значительный спад доходов из-за новых механизмов борьбы с ними

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

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

Смартфоноферма — как это работает

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

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

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

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

Никто в реальности и не смотрел эти трейлеры, но в Netflix и не должны были знать об этом. Цель состояла в том, чтобы пассивно управлять этими смартфонами 24/7, каждый из которых собирал долю пенни за каждую рекламу, которую они «просмотрели».

Так в США появилась новая субкультура — заядлые и не очень смартфонофермеры.

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

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

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

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

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

Пример домашней смартфонофермы:

Сколько можно заработать с помощью смартфонофермы?

Например, в 2017 году Джозеф Д'Алесандро зарабатывал почти 2000 долларов в месяц.

«Вы действительно не можете сравнить это с работой», — сказал Д'Алесандро из-за того, как мало ему нужно было общаться со своими гаджетами.

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

— более продвинутые по 700-800 долларов в месяц;

— менее удачливые по 7 долларов в день, то есть более 200 долларов в месяц.

На что тратят свои баллы из приложений смартфонофермеры?

Смартфонофермеры — это молодежь, которая тратит свой заработок на такие покупки, как приставки Nintendo Switch, различные дроны и пиццу. Ведь нужно еще суметь перевести баллы и подарочные карты Airbnb и Amazon в реальные деньги, обычно путем продажи их через PayPal.

Еще один пример домашней смартфонофермы:

Конечно, прибыль от смартфонофермы может разнится из-за ее объемов.

Например, смартфоноферма из 20 гаджетов может приносить от 50 до 100 долларов в месяц. Для инвалидов или молодых родителей даже такой доход очень ощутим — можно покупать компьютерные аксессуары или продукты питания, детские товары

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

Как стать смартфонофермером?

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

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

Какие сложности есть у смартфонофермеров?

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

Ранее Perk был популярен у смартфонофермеров, но теперь новый фаворит — приложение под названием CashMagnet.

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

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

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

Пример работы смартфонофермы с приложением Perk TV:

Использование специальных программ для смартфонофермы.

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

Смартфонофермеры незаконно используют:

— приложение под названием FRep или Finger Replayer, чтобы зациклить движение виртуального пальца по экрану телефона и обмануть приложения;

— приложение Automagic, которое позволяет пользователям автоматизировать серию команд или действий на телефоне Android.

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

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

Теперь многие рекламные приложения запрещают использовать слишком много устройств с одного IP-адреса.

Упадок смартфонофермерства

В 2019 году борьба рекламодателей с механизмами смартфонофермерства только усилилась.

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

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

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

Let's block ads! (Why?)

Dat — что это за протокол, и кто его использует

Говорим о принципах работы этого P2P-протокола и проектах, построенных на его основе.


/ Unsplash / Alina Grubnyak

Что такое Dat


Dat (Decentralized Archive Transport) — это открытый протокол для обмена данными и сообщениями в рамках распределенной сети (peer-to-peer). Он имеет встроенные функции контроля версий и позволяет следить за изменениями в сводах данных. Разработчиком Dat выступил инженер Макс Огден (Max Ogden). Он представил протокол в 2013 году в рамках проекта Code for America.

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

Вокруг Dat сформировалось крупное сообщество (7 тыс. звезд на GitHub). Продвижением протокола и построенных на его основе приложений занимается некоммерческая организация Dat Foundation. Её поддерживают Mozilla, открытый фонд Code for Science & Society и разработчик P2P-сетей Wireline.

Как он работает


Для загрузки файла в сети Dat необходимо указать его URL. Вот пример:
dat://778f8d955175c92e4ced5e4f5563f69bfec0c86cc6f670352c457943666fe639/dat_intro.gif

Чтобы узнать адресную ссылку, Dat-клиенты используют multicast DNS. Пиры транслируют свой запрос в локальную сеть в надежде, что кто-нибудь из участников «услышит» и поделится информацией. Также клиенты могут обращаться к серверу в интернете. Основной сервер расположился по адресу discovery1.datprotocol.com. Если он недоступен, то можно обратиться к его зеркалу — discovery2.datprotocol.com.
О других протоколах и стандартах в нашем блоге на Хабре:

Когда пир узнает IP-адрес и номер порта другого пира, они устанавливают TCP-соединение. Все передаваемые данные шифруются — для этого используется система поточного шифрования XSalsa20. Алгоритм применяет хеш-функцию с двадцатью циклами. Операции преобразования напоминают те, что задействованы в AES.

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

Сейчас члены Dat Foundation улучшают протокол, чтобы он мог работать с большими объемами данных. В частности, планируют переработать файловую систему (называется Hyperdrive), чтобы она справлялась с миллионами файлов, и внедрить новые механизмы поиска пиров (Hyperswarm).

Кто использует


Примером может быть открытый проект ScienceFair. Это — десктопное приложение для просмотра и поиска научной литературы. На этой платформе ученые и исследователи могут работать с личными заметками, журналами или выжимками из них. Для отображения контента из научной литературы ScienceFair использует ридер Lens — он отвечает за рендер формата JATS XML.

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

Один из свежих примеров — P2P-браузер Beaker, который разработан в партнерстве с командой, развивающей Dat. Его цель — дать пользователям возможность размещать веб-сайты «прямо в браузере». Авторы Beaker запустили облачный сервис Hashbase, поддерживающий постоянный доступ к Dat-сайтам, чьи локальные копии недоступны.

Проект полностью открыт, и его исходники можно найти на GitHub. Если вы хотите оценить возможности браузера самостоятельно, то для его запуска на Linux понадобится установить libtool, m4 и autoconf:

sudo apt-get install libtool m4 make g++ autoconf # debian/ubuntu
sudo dnf install libtool m4 make gcc-c++ libXScrnSaver # fedora


После достаточно запустить:
git clone https://github.com/beakerbrowser/beaker.git
cd beaker
npm install
npm run rebuild # see https://github.com/electron/electron/issues/5851
npm start


Больше примеров приложений можно найти на сайте проекта.

Аналог


Разумеется, Dat не единственный P2P-протокол, который последнее время активно разрабатывается. В качестве альтернативы можно назвать проект IPFS (InterPlanetary File System). Это — одноранговая распределенная файловая система. Отличие от других децентрализованных сетей в том, что она работает с блоками. Они могут содержать как часть файла, так и ссылки на другие блоки. Из этих блоков формируется обобщенный древовидный направленный граф, формирующий каталог или файл. IPFS работает с распределенными хеш-таблицами и поддерживает децентрализованный обмен блоками. IPFS не имеет точек отказа, а узлы не обязаны доверять друг другу. Доступ к файловой системе можно получить с помощью FUSE или HTTP.

Решение уже использует хостинг Neocities и маркетплейс OpenBazaar. Разработчики протоколов, подобных IPFS и Dat, надеются, что их проекты дадут интернет-пользователям больше контроля над своими данными.

О чем мы пишем в корпоративном блоге VAS Experts:

Let's block ads! (Why?)

RESTinio — это асинхронный HTTP-сервер. Простой пример из практики: отдача большого объема данных в ответ

Недавно мне довелось поработать над приложением, которое должно было контролировать скорость своих исходящих подключений. Например, подключаясь к одному URL приложение должно было ограничить себя, скажем, 200KiB/sec. А подключаясь к другому URL — всего 30KiB/sec.

Самым интересным моментом здесь оказалось тестирование этих самых ограничений. Мне потребовался HTTP-сервер, который бы отдавал трафик с какой-то заданной скоростью, например, 512KiB/sec. Тогда бы я мог видеть, действительно ли приложение выдерживает скорость 200KiB/sec или же оно срывается на более высокие скорости.

Но где взять такой HTTP-сервер?

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

О том, насколько это было просто и хотелось бы рассказать в статье. Заодно узнать в комментариях, действительно ли это просто или же я сам себя обманываю. В принципе, данную статью можно рассматривать как продолжение предыдущей статьи про RESTinio под названием "RESTinio — это асинхронный HTTP-сервер. Асинхронный". Посему, если кому-то интересно прочитать о реальном, пусть и не очень серьезном применении RESTinio, то милости прошу под кат.

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


Но все несколько сложнее

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

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

Звучит разумно, но ведь операции ввода-вывода скрыты в потрохах RESTinio… Как тут узнать, можно ли записывать следующую порцию данных или нет?

Из данной ситуации можно выйти, если использовать after-write нотификаторы, которые есть в RESTinio. Например, мы можем написать так:

void request_handler(restinio::request_handle_t req) {
   req->create_response() // Начинаем формировать ответ.
      ... // Наполняем ответ содержимым.
      .done([](const auto & ec) {
          ... // Вот этот код будет вызван когда запись ответа закончится.
      });
}

Лямбда, переданная в метод done() будет вызвана когда RESTinio завершит запись исходящих данных. Соответственно, если сокет какое-то время был не готов к записи, то лямбда будет вызвана не сразу, а после того, как сокет придет в должное состояние и примет все исходящие данные.

За счет использования after-write нотификаторов логика работы тестового сервера будет такой:


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

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


И еще немного сложного: chunked_output

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

Поэтому в описываемом тестовом сервере используется т.н. chunked_output. Т.е. при создании ответа я указываю RESTinio, что ответ будет формироваться частями. После чего просто периодически вызываю методы append_chunk для добавления к ответу очередной части и flush для записи накопленных частей в сокет.

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


Функция request_processor и её подручные

Функция request_processor вызывается для обработки нужных мне HTTP GET запросов. Ей в качестве аргументов передаются:


  • Asio-шный io_context, на котором ведется вся работа (он потребуется, например, для взведения таймеров);
  • размер одной части ответа. Т.е. если мне нужно отдавать исходящий поток с темпом в 512KiB/sec, то в качестве этого параметра будет передано значение 512KiB;
  • количество частей в ответе. На случай, если поток должен иметь какую-то ограниченную длину. Например, если нужно отдавать поток с темпом 512KiB/sec в течении 5 минут, то в качестве этого параметра будет передано значение 300 (60 блоков в минуту в течении 5 минут);
  • ну и сам входящий запрос для обработки.

Внутри request_processor создается объект с информацией о запросе и параметрах его обработки, после чего эта самая обработка и начинается:

void request_processor(
        asio_ns::io_context & ctx,
        std::size_t chunk_size,
        std::size_t count,
        restinio::request_handle_t req) {
    auto data = std::make_shared<response_data>(
            ctx,
            chunk_size,
            req->create_response<output_t>(),
            count);

    data->response_
        .append_header(restinio::http_field::server, "RESTinio")
        .append_header_date_field()
        .append_header(
                restinio::http_field::content_type,
                "text/plain; charset=utf-8")
        .flush();

    send_next_portion(data);
}

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

struct response_data {
    asio_ns::io_context & io_ctx_;
    std::size_t chunk_size_;
    response_t response_;
    std::size_t counter_;

    response_data(
        asio_ns::io_context & io_ctx,
        std::size_t chunk_size,
        response_t response,
        std::size_t counter)
        : io_ctx_{io_ctx}
        , chunk_size_{chunk_size}
        , response_{std::move(response)}
        , counter_{counter}
    {}
};

Тут нужно заметить, что одна из причин появления структуры response_data состоит в том, что объект типа restinio::response_builder_t<restinio::chunked_output_t> (а именно этот тип спрятан за коротким псевдонимом response_t) является moveable-, но не copyable-типом (по аналогии с std::unique_ptr). Поэтому этот объект нельзя просто так захватить в лямбда-функции, которая затем оборачивается в std::function. Но если объект-response поместить в динамически созданный экземпляр response_data, то умный указатель на экземпляр reponse_data уже можно без проблем захватывать в лямбда-функции с последующим сохранением этой лямбды в std::function.


Функция send_next_portion

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

void send_next_portion(response_data_shptr data) {
    data->response_.append_chunk(make_buffer(data->chunk_size_));

    if(1u == data->counter_) {
        data->response_.flush();
        data->response_.done();
    }
    else {
        data->counter_ -= 1u;
        data->response_.flush(make_done_handler(data));
    }
}

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


Функция make_done_handler

Функция make_done_handler отвечает за создание лямбды, которая будет передана в RESTinio в качестве after-write нотификатора. Этот нотификатор должен проверить, завершилась ли запись очередной части ответа успешно. Если да, то нужно разобраться, следует ли следующую часть отослать сразу же (т.е. были "тормоза" в сокете и темп отсылки выдерживать не получается), либо же после некоторой паузы. Если нужна пауза, то она обеспечивается через взведение таймера.

В общем-то, несложные действия, но в коде получается лямбда внутри лямбды, что может смутить людей, не привыкших к "современному" С++. Которому не так уж и мало лет чтобы называться современным ;)

auto make_done_handler(response_data_shptr data) {
    const auto next_timepoint = steady_clock::now() + 1s;
    return [=](const auto & ec) {
        if(!ec) {
            const auto now = steady_clock::now();
            if(now < next_timepoint) {
                auto timer = std::make_shared<asio_ns::steady_timer>(data->io_ctx_);
                timer->expires_after(next_timepoint - now);
                timer->async_wait([timer, data](const auto & ec) {
                        if(!ec)
                            send_next_portion(data);
                    });
            }
            else
                data->io_ctx_.post([data] { send_next_portion(data); });
        }
    };
}

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


Подключение express-like роутера

Показанные выше request_processor, send_next_portion и make_done_handler в общем-то и составляли самую первую версию моего тестового сервера, написанного буквально за 15 или 20 минут.

Но через пару дней использования этого тестового сервера оказалось, что в нем есть серьезный недостаток: он всегда отдает ответный поток с одинаковой скоростью. Скомпилировал со скоростью 512KiB/sec — отдает всем 512KiB/sec. Перекомпилировал со скоростью 20KiB/sec — будет отдавать всем 20KiB/sec и никак иначе. Что было неудобно, т.к. стало нужно иметь возможность получать ответы разной "толщины".

Тогда и появилась идея: а что, если скорость отдачи будет запрашиваться прямо в URL? Например, сделали запрос на localhost:8080/ и получили ответ с заранее заданной скоростью. А если сделали запрос на localhost:8080/128K, то стали получать ответ со скоростью 128KiB/sec.

Потом мысль пошла еще дальше: в URL также можно задавать и количество отдельных частей в ответе. Т.е. запрос localhost:8080/128K/3000 приведет к выдаче потока из 3000 частей со скоростью 128KiB/sec.

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

auto make_router(asio_ns::io_context & ctx) {
    auto router = std::make_unique<router_t>();

    router->http_get("/", [&ctx](auto req, auto) {
            request_processor(ctx, 100u*1024u, 10000u, std::move(req));
            return restinio::request_accepted();
        });

    router->http_get(
                R"(/:value(\d+):multiplier([MmKkBb]?))",
                [&ctx](auto req, auto params) {

            const auto chunk_size = extract_chunk_size(params);

            if(0u != chunk_size) {
                request_processor(ctx, chunk_size, 10000u, std::move(req));
                return restinio::request_accepted();
            }
            else
                return restinio::request_rejected();
        });

    router->http_get(
                R"(/:value(\d+):multiplier([MmKkBb]?)/:count(\d+))",
                [&ctx](auto req, auto params) {

            const auto chunk_size = extract_chunk_size(params);
            const auto count = restinio::cast_to<std::size_t>(params["count"]);

            if(0u != chunk_size && 0u != count) {
                request_processor(ctx, chunk_size, count, std::move(req));
                return restinio::request_accepted();
            }
            else
                return restinio::request_rejected();
        });

    return router;
}

Здесь формируются обработчики HTTP GET запросов для URL трех типов:


  • вида http://localhost/;
  • вида http://localhost/<speed>[<U>]/;
  • вида http://localhost/<speed>[<U>]/<count>/

Где speed — это число, определяющее скорость, а U — это опциональный мультипликатор, который указывает, в каких единицах задана скорость. Так 128 или 128b означает скорость в 128 байт в секунду. А 128k — 128 килобайт в секунду.

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

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

std::size_t extract_chunk_size(const restinio::router::route_params_t & params) {
    const auto multiplier = [](const auto sv) noexcept -> std::size_t {
        if(sv.empty() || "B" == sv || "b" == sv) return 1u;
        else if("K" == sv || "k" == sv) return 1024u;
        else return 1024u*1024u;
    };

    return restinio::cast_to<std::size_t>(params["value"]) *
            multiplier(params["multiplier"]);
}

Здесь C++ная лямбда используется для эмуляции локальных функций из других языков программирования.


Функция main

Осталось посмотреть, как все это запускается в функции main:

using router_t = restinio::router::express_router_t<>;
...
int main() {
    struct traits_t : public restinio::default_single_thread_traits_t {
        using logger_t = restinio::single_threaded_ostream_logger_t;
        using request_handler_t = router_t;
    };

    asio_ns::io_context io_ctx;

    restinio::run(
        io_ctx,
        restinio::on_this_thread<traits_t>()
            .port(8080)
            .address("localhost")
            .write_http_response_timelimit(60s)
            .request_handler(make_router(io_ctx)));

    return 0;
}

Что здесь происходит:


  1. Поскольку мне нужен не обычный штатный роутер запросов (который вообще ничего делать сам не может и перекладывает всю работу на плечи программиста), то я определяю новые свойства для своего HTTP-сервера. Для этого беру штатные свойства однопоточного HTTP-сервера (тип restinio::default_single_thread_traits_t) и указываю, что в качестве обработчика запросов будет использоваться экземпляр express-like роутера. Заодно, чтобы контролировать, что происходит внутри, указываю, чтобы HTTP-сервер использовал настоящий логгер (по умолчанию используется null_logger_t который вообще ничего не логирует).
  2. Поскольку мне нужно взводить таймеры внутри after-write нотификаторов, то мне нужен экземпляр io_context, с которым я смог бы работать. Поэтому я его создаю сам. Это дает мне возможность передать ссылку на мой io_context в функцию make_router.
  3. Остается только запустить HTTP-сервер в однопоточном варианте на ранее созданном мной io_context-е. Функция restinio::run вернет управление только когда HTTP-сервер завершит свою работу.

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

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

В общем, если кто-то еще не пробовал RESTinio, то я приглашаю попробовать. Сам проект живет на BitBucket, есть зеркало на GitHub. Задать вопрос или высказать свои предложения можно в Google-группе или прямо здесь, в комментариях.

Let's block ads! (Why?)