Автоматическая генерация сообщений к коммитам

Привет! Меня зовут Александра Елисеева, я студентка Computer Science Center. В рамках практики в осеннем семестре 2020 года я участвовала в проекте BERT for Source Code под руководством Тимофея Брыксина и Ярослава Соколова из JetBrains Research. Я исследовала решение задачи автоматической генерации сообщений к коммитам с помощью языковой модели BERT. Что получилось, а над чем еще предстоит поработать, расскажу в этом посте.

О проекте

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

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

Недавние работы (1, 2, 3) показали, что если обучить модель BERT на большом датасете программного кода, то она и в этой области неплохо справляется с несколькими задачами (среди них, например, локализация и устранение неправильно использованных переменных и генерация комментариев к методам).

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

О задаче

Почему мы выбрали эту задачу?

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

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

  • в существующих работах (4, 5, 6) данные собираются из открытых источников и требуют серьёзной фильтрации, поэтому примеров для обучения остаётся немного. Здесь может пригодиться способность BERT дообучаться на небольших датасетах;
  • state-of-the-art результат на момент работы над проектом был у модели архитектуры Transformer, которая достаточно специфичным способом предобучена на небольшом датасете (6). Нам интересно было сравнить его с моделью на основе BERT, которая предобучена по-другому, но на гораздо большем количестве данных.

За семестр мне нужно было сделать следующее:

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

Данные

Существует несколько открытых датасетов для этой задачи, я выбрала наиболее отфильтрованный из них (5).

Датасет собирался из топ-1000 открытых GitHub репозиториев на языке Java. После фильтрации из исходных миллионов примеров осталось около 30 тысяч.

Сами примеры представляют собой пары из вывода команды git diff и соответствующего короткого сообщения на английском языке. Выглядит это как-то так:

И изменения, и сообщения в датасете короткие — не более 100 и 30 токенов соответственно.

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

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

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

BERT для sequence-to-sequence задач

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

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

  • модель-энкодер на основе входной последовательности строит векторное представление,
  • модель-декодер на основе векторного представления генерирует выходную последовательность.

Модель BERT основана на энкодере из архитектуры Transformer и сама по себе для такой задачи не подходит. Возможно несколько вариантов, чтобы получить полноценную sequence-to-sequence модель, самый простой -— использовать с ней какой-либо декодер. Такой подход с декодером из архитектуры Transformer хорошо себя показал, например, для задачи нейронного машинного перевода (7).

Пайплайн

Для проведения экспериментов необходим был код для обучения и оценки качества подобной sequence-to-sequence модели.

Для работы с моделью BERT я использовала библиотеку HuggingFace’s Transformers, а для реализации в целом — фреймворк PyTorch.

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

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

Эксперименты

В ходе экспериментов нам хотелось понять, позволяет ли использование предобученной на программном коде модели BERT улучшить state-of-the-art результат в этой области.

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

  1. Я предполагала, что с этим вариантом удастся быстро получить какой-нибудь базовый результат. GRU не так часто используют с архитектурой Transformer, поэтому было не до конца понятно, чего ожидать.
    В итоге какого-либо разумного качества получить не удалось даже после подбора нескольких влияющих на процесс обучения гиперпараметров.
  2. Я попробовала второй вариант, используя для этого предобученную на английском языке GPT-2 (8) — модель на основе декодера из архитектуры Transformer, часто применяемую для задач генерации, а также её более маленькую версию — distilGPT-2 (9).

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

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

Подводя итоги

В целом предположение о пользе применения CodeBERT к данной задаче не подтвердилось, во всех случаях обучаемая с нуля модель Transformer показала качество выше. Лучшим методом в этой области остаётся модель CoreGen6: это тоже Transformer, но дополнительно предобученный с использованием предложенной авторами целевой функции.

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

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

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

Источники

  1. Feng, Zhangyin, et al. «Codebert: A pre-trained model for programming and natural languages.» 2020
  2. Buratti, Luca, et al. «Exploring Software Naturalness through Neural Language Models.» 2020
  3. Kanade, Aditya, et al. «Learning and Evaluating Contextual Embedding of Source Code.» 2020
  4. Jiang, Siyuan, Ameer Armaly, and Collin McMillan. «Automatically generating commit messages from diffs using neural machine translation.» 2017
  5. Liu, Zhongxin, et al. «Neural-machine-translation-based commit message generation: how far are we?.» 2018
  6. Nie, Lun Yiu, et al. «CoreGen: Contextualized Code Representation Learning for Commit Message Generation.» 2021
  7. Rothe, Sascha, Shashi Narayan, and Aliaksei Severyn. «Leveraging pre-trained checkpoints for sequence generation tasks.» 2020
  8. Radford, Alec, et al. «Language models are unsupervised multitask learners.» 2019
  9. Sanh, Victor, et al. «DistilBERT, a distilled version of BERT: smaller, faster, cheaper and lighter.» 2019
  10. Yin, Pengcheng, et al. «Learning to represent edits.» 2018
  11. Kim, Seohyun, et al. «Code prediction by feeding trees to transformers.» 2021

ссылка на оригинал статьи https://habr.com/ru/company/JetBrains-education/blog/560298/

Конец вечного противостояния snake_keys VS camelKeys: наводим порядок в стилях написания переменных

Привет, Хабр! Меня зовут Владимир, работаю в Ozon, занимаюсь фронтендом.   

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

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

Бекенд отдает и принимает данные в виде: 

{  		user_name: "user1",  		main_title: "Title",  } 

Фронтенд:

{  		userName: "user1",  		mainTitle: "Title",  } 

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

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

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

Шаг 1. Преобразование строки 

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

# Преобразование snake_keys строки в camelKeys: 

const snakeToCamel = str => {      return str.replace(/([_][a-z])/g, letter => {          return letter                  .toUpperCase()                  .replace('_', '')      })  } 

# Преобразование camelKeys строки в snake_keys: 

const camelToSnake = str => {      return str.replace(/[A-Z]/g, letter => {          return '_' + letter.toLowerCase()      })  } 

Шаг 2. Работа с объектами 

# Возьмем пример с начала статьи   {  		user_name: "user1",  		main_title: "Title",  } 

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

const simpleKeysTransform = value => {      return Object.entries(value).reduce((acc, [key, value]) => {          const newKey = snakeToCamel(key)                   return {...acc, [newKey]: value}      }, {})  } 

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

const keysTransform1 = (value, isInitialSnake = true) => {      const chooseStyle = isInitialSnake ? snakeToCamel : camelToSnake       return Object.entries(value).reduce((acc, [key, value]) => {          const newKey = chooseStyle(key)           return {...acc, [newKey]: value}      }, {})  } 

Шаг 3. Что делать с вложенными объектами 

# Например   {    user_info: {      first_name: "User",      last_name: "Userin”    }  } 

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

const keysTransform2 = (input, isInitialSnake = true) => {      const chooseStyle = isInitialSnake ? snakeToCamel : camelToSnake       const recursiveTransform = value => {           if (value && typeof value === 'object') {              return Object.entries(value).reduce((acc, [key, value]) => {                  const newKey = chooseStyle(key)                  const newValue = recursiveTransform(value)                   return {...acc, [newKey]: newValue}              }, {})          }           return value      }       return recursiveTransform(input)  } 

Шаг 4. Что делать с массивами 

# Например   {    users: [      {        first_name: "user1",        phone_number: 8996923      },      {        first_name: "user2",        phone_number: 12312312      }    ]    }  

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

const keysTransform = (input, isInitialSnake = true) => {       const chooseStyle = isInitialSnake ? snakeToCamel : camelToSnake       const recursiveTransform = value => {          if (Array.isArray(value)) {              return value.map(recursiveTransform)          }           if (value && typeof value === 'object') {              return Object.entries(value).reduce((acc, [key, value]) => {                  const newKey = chooseStyle(key)                  const newValue = recursiveTransform(value)                   return {...acc, [newKey]: newValue}              }, {})          }           return value      }       return recursiveTransform(input)  } 

Перемирие 

Давайте посмотрим, что получилось: мы реализовали алгоритм преобразования ключей объектов из snake_keys в camelKeys и наоборот. Чуть-чуть меньше раздора между фронтендом и бэкендом – неплохо же! 

Существуют и другие стили написания составных слов (PascalKeys, kebab-keys, UPPER_SNAKE_KEYS). При надобности, вы уже сами сможете с ними справиться. 

ссылка на оригинал статьи https://habr.com/ru/company/ozontech/blog/560294/

Как айтишнику издать свою книгу. Часть первая: куда податься

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

Книга, опыт выпуска которой и послужил источником данного материала, в статье ранее https://habr.com/ru/post/512460/ набрала тысячи скачиваний и десятки благодарных отзывов. 

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

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

А теперь к делу. 

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

Часть 1. Куда податься с книгой

Есть два пути: площадки самиздата и традиционные книжные издательства.

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

Плюсы и минусы первого варианта — самиздата

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

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

+ название книги, дизайн обложки — на ваш вкус. Никаких сюрпризов от издательства;

+ никакой редактуры, искажающей ваш авторский текст;

+ услуги печати по требованию (print-on-demand) на основных площадках самиздата: при размещении через них в магазинах (Ozon, My-shop, Москва, Буквоед и др.) книга печатается при поступлении заказа. А значит можно продавать и печатные версии, нет перебоев с отсутствием тиражей, нет затрат автора на их подготовку и отправку в магазины или покупателям;

денежные затраты: оплата художников/дизайнеров, верстальщиков, корректоров, печать тиражей за свой счет, а затем и реклама; 

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

сложность попадания в сетевые «серьезные» книжные офлайн-магазины;

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

Плюсы и минусы издательств*

*(в случае наиболее традиционного сотрудничества на бесплатной основе по договору исключительной лицензии):

+ все делается издательством за их счет;

+ профессионализм и опыт. Знают, как сделать лучше и продать (крутые обложки, цепляющие названия,…);

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

+ масштаб — больше печатные тиражи (~2000-3000 экз. на старте), больше крупных магазинов;

+ солидность: сотрудничество в классическом книжном флоу вызывает вау-эффект, добавляет веса и серьезности;

+ больше каналов продвижения, специальные маркетинговые отделы;

+ как следствие проще попасть на полки книжных, в т.ч. офлайн, получить классический книжный путь.

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

получаете, как правило, меньший процент роялти (авторских отчислений);

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

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

Площадки самиздата

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

Плюсы и минусы Ридеро

+ меньше багов, приятнее интерфейс; 

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

+ список магазинов релевантнее под IT-аудиторию (например, продажа печатной версии print-on-demand в Ozon, а не в My-shop как на ЛитРес);

+ оперативная поддержка;

зависимость от посредничества и подсаживание на платные услуги. Например, скачать 1 раз сверстанную книгу в pdf — 250 руб., fb2 — еще 250 руб., для безлимита — покупать подписку в 1000-1500 руб./мес. Поправить аннотацию в магазинах — 2 мес. ожидания или доплата;

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

Отступление про бестселлеры в Ozon:

Однажды, зайдя на страницу своей книги в Ozon, я увидела на ней метку «бестселлер». Предвкусила масштабы продаж Дарьи Донцовой. Дождавшись следующего месяца, получила статистику от Ridero — 2 продажи. На вопрос в Ozon, а по какой логике книги получают метку «бестселлер», получила политкорректный ответ — как товары, наиболее популярные и продаваемые среди остальных. Уточнения, сколько нужно продаж и как определяются товары сравнения, остались без ответа. Так что стать бестселлером не сложно и за 1-2 книги (правда, ненадолго — метка пропала через день-два), либо же, реальные продажи были чуть больше. Но к бестселлерам в Ozon стала относиться настороженнее.

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

Плюсы и минусы ЛитРес: Самиздата

+ оперативная статистика без задержек в 1-2 мес. по самому ЛитРес-у, а это все же основная площадка электронных книг;

+ больший контроль за отображение книги на ЛитРес;

сырость, на момент выбора не удалось загрузить, говорящих ошибок не было; 

свой дизайн обложки возможен только для лицевой стороны;

только дефолтная верстка книги. Сделать на свой вкус более стильную по шрифтам и отступам (боль фронтендера!) верстку для печатной книги нельзя;

медленнее поддержка;

меньше привлекательных целевой айтишной аудитории каналов продажи. Например, нет Amаzon, нет продаж печатной версии на Ozon, Aliexpress;

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

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

Мой путь на данном этапе

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

Изучив сайты МИФа, Эксмо/Бомборы, Альпины, Питера, ДМК-Пресса и прочих основных и не очень игроков книжного рынка, сроки рассмотрения, % одобрений, процессы, включая в том же МИФе требования дописывания глав «под заказ» редакторов по потребностям аудитории, прочие условия и веющее от них пренебрежительное и бесправное отношение к авторам — поняла, что пока хочу сохранить свободу.

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

В то же время хотелось попасть в крупные сетевые книжные офлайн магазины вроде Читай-Город и Буквоед. Просто был такой пунктик 🙂  Как метрика серьезности результата, качества контента и легкий вызов себе — не каждое написанное в сети оказывается на полках классических книжных. И главное, как еще один сложнодоступный «своим ходом» канал распространения: чтобы студенты и начинающие могли зайти, увидеть и купить. Ведь главное для автора, чтобы его книги читали и использовали себе во благо как можно большее число людей.

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

С другой стороны, заранее уточнила у интересующих «серьезных» издательств:

  • будут ли рассматривать рукопись, ранее открыто размещенную в сети?

  • ранее опубликованную на ресурсах ЛитРес и аналогах для продажи?

  • распечатанную за счет автора малым тиражом?

  • смогу ли сохранить за собой права (и какие) на распространение (т.е. исключительная или неисключительная лицензия предполагается сотрудничеством)?

Краткая сводка ответов:

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

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

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

Небольшая правовая ремарка

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

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

В ответ на присланную рукопись, руководитель отдела компьютерной литературы БХВ, отреагировал в ключе «зачем выложили, как нам на этом теперь заработать?» В целом резко и не очень дружелюбно (хотя местами по рыночному справедливо) прокомментировал присланные вводные, а затем сразу перешел к делу. Мол, хотят одну книгу выпустить, как бы им ее назвать, какие технологии вынести на обложку, что сейчас популярно во фронтенде. С лихой овцы хоть шерсти клок 🙂 Естественно, после получения бесплатной консультации, инициативы по связи о моей книге не последовало. Интереса ради, я поинтересовалась сама позже, получив ответ о готовности сотрудничества только за деньги автора. 

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

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

Продолжение в следующих частях:

Часть 2. Самиздат: сколько стоит свобода. Денежные и временные затраты на выпуск электронной и печатной книги.

Часть 3. Издательства: как попасть на полки книжных магазинов. Шансы на интерес к вам, опасные договоры и процесс выпуска книги.

Часть 4. Запись аудиоверсии книги.

Часть 5. Сколько получают авторы.

ссылка на оригинал статьи https://habr.com/ru/post/560232/

Duff’s device или loop unrolling в Си своими руками

Выглядит ли следующий код валидным С++ кодом? Если да, то какое значение будет выведено в результате его работы?

#include <iostream>  int main() {   int number = 11;        int count  = number / 4;   int i = 0;      switch (number % 4) {     case 0:        do {     	  ++i;     case 3: ++i;     case 2: ++i;     case 1: ++i;       } while (count-- > 0);   }    std::cout << i; }

С первого взгляда этот код может показаться какой-то кашей, в которой конструкция switch и цикл do-while просто наложены друг на друга. Однако с точки зрения стандарта языка , здесь все совершенно законно.

На самом деле, здесь описан цикл, в котором переменная i увеличивается на единицу number раз, и, как не сложно теперь догадаться, в данном случае программа выведет число 11. То есть код выше будет справедливо «упростить» следующим образом:

#include <iostream>  int main() {   int number = 11;   int i = 0;      while (number-- > 0) {     ++i;   }    std::cout << i; }

Но для чего же тогда извращаться и скрещивать do-while и switch?

Duff’s device (устройство Даффа)

Этот прием был изобретен ещё в 1983 году программистом по имени Tom Duff, и его применение позволяет реализовать на Си вручную такую оптимизацию, как loop unrolling.

Заключается эта оптимизация в следующем. При выполнении циклов в конце каждой итерации совершается проверка, не закончился ли цикл и не пора ли из него выйти. В нашем случае, после каждой итерации цикла происходит проверка, что number еще не достиг нуля. Выполняя loop unroling (или развертывание цикла), мы уменьшаем количество таких проверок тем, что за одну итерацию выполняем не одно тело цикла, а сразу несколько. В примере выше телом цикла является инкремент переменной i. В дальнейшем цикл, в котором используется данная оптимизация, я буду называть — развернутый цикл.

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

#include <iostream>  int main() {      int number = 11;      int i = 0;         while (number-- > 0) {     ++i;   }         std::cout << i;  }

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

int main() {   int number = 11; // общее число итераций = 11.      // в развернутом цикле выполняем по 4 тела цикла за раз.   int count  = number / 4; // количество итераций развернутого цикла.   int left   = number % 4; // количество итераций, которые в него "не влезли".   int i = 0;      // выполняем "невлезшие" в развернутый цикл итерации   // перед выполнением развернуто цикла.   while (left-- > 0) {     i++;   }    // выполняем развернутый цикл по 4 итерации за раз.   while (count-- > 0) {     i++;     i++;     i++;     i++;   }        std::cout << i; }

В данном случае мы выполнили не 11 проверок условия выхода из цикла, как в первом примере, а number / 4 + number % 4 проверок, то есть всего 5, при number равном 11.

Но у программистов на Assembly есть более изящное решение этой проблемы: в начале выполнения развернутого цикла производиться jump на его середину, чтобы сначала выполнить остаток итераций. И устройство Даффа позволяет сделать точно так же в Си. Рассмотрим еще раз код, который я приводил в самом начале:

#include <iostream>  int main() {   int number = 11; // общее число итераций все еще равно 11.   int count = number / 4; // число итераций развернутого цикла.   int i = 0;    // находим остаток от деления общего числа итераций на количество итераций   // в одной итерации развернутого цикла и совершаем переход к этой итерации   // внутрь тела развернутого цикла.   switch (number % 4) {     case 0:        do {           ++i;     case 3: ++i; // <- наш случай. Выполнение цикла начнется отсюда и при первой     case 2: ++i; // итерации развернутого цикла будет совершено не четыре, а      case 1: ++i; // только три итерации исходного цикла (11 % 4 итераций).       } while (count-- > 0);    }      std::cout << i; }

В данном случае мы выполнили только number / 4 проверок условия выхода из цикла, то есть всего две проверки, вместо первоначальных одиннадцати. Данный прием возможен благодаря тому, что в конструкции switch в Си выполнение кода будет «проваливаться» из блока одной метки case в блок следующей при отсутствии между ними оператора break. Но, как правило, никто не ожидает увидеть метку case посередине какой-то другой конструкции, как цикл do-while в нашем случае. Но в действительности, метка case может находится в любом месте внутри switch, и управление будет просто передано на следующую за ней инструкцию, что бы там не находилось.

В оригинале устройство Даффа выглядело следующим образом:

send(to, from, count) // to - регистр ввода/вывода, отображаемый в память. Он не увеличивается // при каждой итерации цкла. // from - массив в памяти, откуда происходит копирование в регистр 'to' register short *to, *from; register count; {     // в данном случае цикл разворачивался по 8 итераций за раз     register n = (count + 7) / 8;     switch (count % 8) {     case 0: do { *to = *from++;     case 7:      *to = *from++;     case 6:      *to = *from++;     case 5:      *to = *from++;     case 4:      *to = *from++;     case 3:      *to = *from++;     case 2:      *to = *from++;     case 1:      *to = *from++;             } while (--n > 0);     } }

Положительные и побочные эффекты

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

Предлагаю посмотреть, во что превращается код в обоих случаях после компиляции. Для этого воспользуемся Compiler Explorer‘ом с компилятором gcc 11.1 для x86-64 без дополнительных опций. Рассмотрим сгенерированный код, не сильно вдаваясь в подробности.

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

...  ; рассмотрим только код самого цикла while ; [rbp-4] - это переменная number = 11 на стеке ; [rbp-8] - это переменная i = 0 на стеке         jmp     .L2  .L3:         add     DWORD PTR [rbp-8], 1    ; увеличили i на единицу .L2:         mov     eax, DWORD PTR [rbp-4]  ; кладем в регистр eax значение number         lea     edx, [rax-1]            ; отнимаем от number единицу         mov     DWORD PTR [rbp-4], edx  ; сохраняем значение number-1 на стек         test    eax, eax                ; проверяем, что number еще не стал 0         jg      .L3                     ; если не стал, повторяем цикл еще раз ...

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

... ; рассматриваем только внутренности switch ; в [rbp-4] и [rbp-8] все как и было ; [rbp-12] - переменная count          mov     eax, DWORD PTR [rbp-4] ; весь этот код до метки .L8 нужен для          cdq                            ; вычисления остатка от деления общего         shr     edx, 30                ; количества итераций на количество         add     eax, edx               ; итераций в одной итерации развернутого         and     eax, 3                 ; цикла и перехода к нужной итерации.         sub     eax, edx                        cmp     eax, 3								 ; в зависимости от остатка мы прыгаем на:         je      .L2                    ; вторую итерацию         cmp     eax, 3         jg      .L3				         cmp     eax, 2         je      .L4										 ; третью итерацию         cmp     eax, 2         jg      .L3         test    eax, eax         je      .L5                    ; первую итерацию         cmp     eax, 1         je      .L6                    ; четвертую итерацию         jmp     .L3                    ; да, в худшем случае мы дойдем аж до сюда                                        ; проверив все предыдущее. .L8:         nop .L5:                                   ; а вот наши "развернутые итерации":         add     DWORD PTR [rbp-8], 1   ; перый i++ .L2:         add     DWORD PTR [rbp-8], 1   ; второй i++ .L4:         add     DWORD PTR [rbp-8], 1   ; третий i++ .L6:         add     DWORD PTR [rbp-8], 1   ; четвертый i++         mov     eax, DWORD PTR [rbp-4] ; дальше выполняется точно такая же, как         lea     edx, [rax-1]           ; и в первом случае, проверка условия         mov     DWORD PTR [rbp-4], edx ; выхода из цикла         test    eax, eax         jg      .L8 .L3:          ; тут идет завершение программы ...

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

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

Мой блог в Телеграме

ссылка на оригинал статьи https://habr.com/ru/post/560320/

Raspberry Pi Foundation: 5 устройств менее чем за год. Что дальше?

Менее чем за год Raspberry Pi Foundation выпустила целую серию новых продуктов. Четыре гаджета вышло в 2020 году и один — в 2021. Все это — невзирая на проблемы с логистикой из-за пандемии. Успех частично объясняется ростом спроса на ПК, включая миниатюрные системы, после массового перехода на удаленку.

Компания постаралась удовлетворить спрос: сначала она выпустила хорошую камеру, в апреле 2020, через месяц — Raspberry Pi 4 c 8 ГБ ОЗУ. Потом — Compute Module 4, Raspberry Pi 400 и, совсем недавно, Raspberry Pi Pico за $4. Даже пандемия COVID-19 не повлияла на эффективность работы и цепочку поставок. Будет ли компания отдыхать? Вряд ли.

«Мы вошли в пандемию с огромным списком задач. Их успешное решение, запуски новых продуктов, позволяют нам успешно двигаться вперед», — заявил создатель и соучредитель Raspberry Pi Эбен Аптон.

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

Год бюджетных ПК


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

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

Но в любом случае множество пользователей по всему миру оценили достоинства системы ценой менее $50, которую можно использовать в качестве десктопного ПК. Спрос резко увеличился, и уже в марте 2020 года компания продала 640 000 девайсов, что стало вторым по объему продаж месяцем в истории компании. По данным TechRepublic общий объем разных устройств за 2020 год превысил 7,1 млн штук.

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

Команда, кстати, думала, что пандемия быстро закончится, возможно, даже к тому моменту, как будет выпушен Pi 400. Но, как все мы знаем, это оказалось ошибкой. Согласно данным продаж, компания продала в марте от 300 до 400 тыс. этих устройств. У Raspberry Pi Foundation не было проблем с продажами — скорее, проблемы возникли с необходимостью удовлетворять постоянно растущий спрос.

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

Как Raspberry Pi помогла пациентам, студентам и их родителям


Компания участвовала и в большом количестве социальных и благотворительных инициатив. В частности, команда предоставляла свои устройства учащимся, кто не мог позволит приобрести технику. В феврале Raspberry Pi Foundation предоставила 5000-й комплект в рамках кампании Bloomfield Trust.

Также Raspberry Pi Foundation продавала свои наборы напрямую школам, без посредников и наценок. Более того, образовательным учреждениям устройства предлагались по оптовым ценам. По словам представителей компании, Raspberry Pi c 2 ГБ ОЗУ, мышью, клавиатурой и базовым дисплеем — лучший выбор, чем бюджетные ноутбуки, которые предлагаются школьникам в рамках государственных инициатив в Великобритании.

«За 40 фунтов стерлингов вы получаете нечто гораздо более крутое, чем самый дешевый Chromebook. Да и недорогие Хромбуки — это мусор», — рассказал представитель компании.

Бездонная бочка


Речь идет о еще одном устройстве компании — микроконтроллере Pico. Это первая такая система в продуктовой линейке Raspberry Pi, как и первое устройство с собственным процессором.

Pico — чрезвычайно востребованный продукт. В марте 2021 года компания отрузила 200 000 устройств при общем количестве заказов в 900 000. Затем было отгружено еще 100 000 устройств, но покупатели заказали дополнительно 200 000.

«Это попытка наполнить бездонную бочку. Некоторые покупатели, видя цену в $4, вероятно, заказывали десятки микроконтроллеров», — заявил Аптон.

Raspberry Pi Pico позиционируется как девайс для обучения, хотя, конечно, устройство массово покупают и любители DIY. В любом случае, микроконтроллеры — полностью новый рынок для компании. Этот девайс расширяет и без того объемную экосистему продуктов.

Программное обеспечение — новый фокус компании в 2021 году


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

Аптон считает, что удаленная разработка аппаратных систем — плохая идея. Поэтому в 2021 году Raspberry Pi снизила активность в разработке «железа» и направила дополнительные усилия на софт. Значит ли это, что Raspberry Pi 5 не появится в этом году? Вероятно, да, хотя компания пока не отказалась полностью от идеи выпуска нового девайса.

Raspberry Pi планирует к середине года перевести платформу на Debian 11, что позволит обновить многие элементы системы, включая оптимизацию стека Mesa 3D. Также Аптон надеется увидеть, как повлияет обновленный Chromium на Raspberry Pi. Одно из преимуществ Chromium — мультимедийные возможности, включая видеоконференции, что крайне актуально в нынешнюю эпоху удаленки.

«Сейчас мы используем Chromium 88, так что находимся на переднем краю современных технологий. Это очень важно, поскольку многие платформы видеоконференцсвязи задействуют самые новые возможности браузеров. И если мы говорим о том, что Raspberry Pi — отличная платформа для работы из дома, нужно поддерживать видеоконференцсвязь», — говорит Аптон.

Если 2020 год был годом нового «железа» Raspberry Pi, то 2021 может стать годом, где во главу угла ставится разработка ПО. Усилий для этого нужно не меньше, чем в ходе разработки аппаратного обеспечения, поскольку команда Raspberry Pi одновременно работает с ядром, с Debian и Chromium.

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

ссылка на оригинал статьи https://habr.com/ru/company/selectel/blog/559658/