Нужен энергоэффективный дата-центр? Тогда стоит построить его под водой

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

Речь идет о проекте, который получил название Project Natick. О нем мы уже писали, а сейчас появилась более подробная информация с некоторыми техническими деталями. По словам авторов идеи, основные преимущества такого типа ЦОД-ов это снижение затрат энергии на охлаждение оборудование, а также возможность размещения дата-центров ближе к пользователям, что сократит время обработки запросов. В Microsoft подсчитали, что около 50% населения мира живут примерно в 100 км от побережья. Еще одно преимущество — модульность.

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

Как это выглядит?

По замыслу, подводный ЦОД от Project Natick будет представлять собой систему из отдельных стальных цилиндров, в каждом из которых располагается типовое серверное оборудование. Дата-центр будет состоять из большого количества таких цилиндров. Подводный ЦОД сможет вмещать несколько тысяч серверов. Дата-центры будут располагаться рядом с побережьем на глубине от 50 до 200 метров. Стальные цилиндры могут как плавать (для этого создается специальная система балансиров), так и быть прикрепленными к дну моря или океана.

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

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

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

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

Например, капсула, которая тестировалась ранее, уже в первые же сутки после погружения стала центром внимания для морской живности — крабов и других животных. В то же время, оказалось, что разработанная для подводных дата-центров система охлаждения работает вполне эффективно (при температуре воды в 10-18 градусов Цельсия).

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

Время эксплуатации системы составляет около 5 лет. После завершения этого срока на место прибудет специальная команда, которая либо заменит устаревшее оборудование, либо заберет ДЦ для переработки. В целом, предусмотрено 4 возможных цикла обновления оборудования, что позволит подводному ЦОД работать вплоть до 20 лет.


ссылка на оригинал статьи https://habrahabr.ru/post/324708/

В мире микробов процветает сотрудничество

image
В зависимости от внешних условий, микроб Myxococcus xanthus может принимать как конкурентную (жёлтый) форму, так и кооперативную (зелёный)

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

Это разнообразие поведения вызывает вопрос по поводу эволюции: как же сотрудничество может процветать в такой эгоистичной среде, управляемой естественным отбором? «Классическая проблема в том, что любая кооперация окажется затратной с точки зрения отдельных индивидов», – говорит Майкл Десай [Michael Desai], физик, переметнувшийся к биологам-эволюционистам из Гарвардского университета, изучающий микробов. «Загадка в том, как эта ситуация могла бы появиться в результате эволюции?».

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

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

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

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

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

Новая территория

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

image
При недостатке еды тысячи отдельных Myxococcus xanthus кооперируются и создают образование, производящее споры

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

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

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

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

В эксперименте от 2007 года влияние экспансии было продемонстрировано визуально. Оскар Халачек [Oskar Hallatschek], работающий биофизиком в Калифорнийском университете в Беркли, начал с капли двух хорошенько перемешанных штаммов микробов, покрашенных при помощи флуоресцентных красок в два цвета. Поскольку оба штамма растут с одной скоростью, модель статической популяции предсказывает, что их концентрация со временем не будет меняться; начальное соотношение 50:50 сохранится. Но результаты оказались совершенно иными. Микробы, начав делиться и распространяться по чашке Петри, быстро отделились друг от друга и организовали рисунок-«вертушку» с разными секциями определённых цветов. «Это очень сильный эффект, которого очень трудно избегать», – говорит Халачек.

Открытия стали потрясающей иллюстрацией такого явления, как генетический сёрфинг, теоретически предсказанный несколькими годами ранее. (Среди исследователей много физиков, которых привлекает, среди прочего, потенциал моделирования и проверки эволюционных теорий). В больших статичных популяциях вероятность фиксации новых нейтральных мутаций (не влияющих на пригодность к эволюции) крайне низка. Но согласно модели сёрфинга, вероятность распространения мутаций, случающихся на границе растущей популяции, гораздо выше – они как бы осёдлывают волну экспансии – и укрепляются, потому что в том месте проходит воспроизводство небольшого числа клеток. В работе от 2007 года Халачек с коллегами объяснили, как генетический дрейф может подпитывать и генетический сёрфинг и появление рисунка «вертушки». Зелёные бактерии делятся и создают больше зелёных колоний, из-за чего растёт зелёный клин. «В случае с расширяющейся колонией всё дело в расположении, – говорит Халачек. – Даже если вы идеально получившийся мутант, вам нужно быть именно на этой границе, чтобы процветать, или у вас не будет шансов».

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

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

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

Маленькая командная работа

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

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

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

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

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

Микробы Десая могли расти в двух измерениях, но некоторые случаи экспансии одномерны – к примеру, птицы, передвигающиеся по цепочке островов. Физик из MIT Джеф Гоур [Jeff Gore] с коллегами проанализировал одномерный случай, выращивая смесь кооперирующих и жульничающих микробов в тонких заполненных жидкостью трубках. Они вручную перемещали микробов, ежедневно перенося некоторую часть жидкости в новую трубку. В отличие от микробов-обманщиков Десаи, способных выживать без кооперации, обманщикам Гоура кооператоры нужны были для получения пищи и выживания, и они проникали в растущую популяцию кооператоров.

image
Джеф Гоур

Исследователи сравнили скорость экспансии кооператоров на границе со скоростью размножения обманщиков, бывших позади. Чтобы кооператорам сопутствовал успех, им нужно было расширяться быстрее, чем их атаковали обманщики. В работе, опубликованной в Proceedings of the National Academy of Sciences, показан, что в суровых условиях в смешанной популяции кооператоры распространяются, а обманщики – вымирают. Однако, если как кооператоры, так и обманщики, развиваются в пустом пространстве, кооператоры перегоняют обманщиков только в мягких, но не в суровых условиях. (Скорость миграции подсчитывается через измерение плотности популяции каждой из трубок, растущей со временем). «Удивительно, как пространственная экспансия предпочитает кооперацию – они захватывают новые территории быстрее, чем обманщики могут захватывать их», – говорит Гоур.

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

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

Кроме микробов

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

«Остаётся разобраться, насколько такой эффект распространён в природе», – говорит Десаи. Многие виды расширяют свои территории, либо по сезонам, либо в долгосрочной перспективе. Привела ли миграция людей из Африки десятки тысяч лет назад к появлению предпочтений в пользу кооперации?

* * *

Экспансия людей

Расширяющаяся популяция несёт характерный генетический признак, и такой признак был найден у людей. Но проблема в том, что этот признак похож на тот, что остаётся в результате естественного отбора. Люди могли мигрировать из Африки «без всякой видимой причины, может просто потому, что это было возможно, но необязательно из-за какого-то давления со стороны селекции», – говорит Лорен Экскофье, генетик-популяционист из Бернского университета. Эти открытия говорят о том, что только из-за увеличения частоты мутации нельзя делать вывод, что это происходит под влиянием естественного отбора. «В одном наборе экспериментов пытаются разработать методы разделения этих чисто нейтральных эффектов случая и реального отбора», – говорит Оскар Халачек.

Экскофье с коллегами пытались изучить эффекты увеличения области обитания людей, анализируя схемы миграции популяции франкоговорящих канадцев в XIX и XX веках. Благодаря подробным генеалогическим записям исследователи могли определить, кто и когда менял место жительства. Согласно результатам, опубликованным в 2011 году в журнале Science, у женщин на границе области проживания было на 15% больше детей, чем у других. «Люди, находившиеся на гребне волны распространения, оставляли в популяции больше генов, чем те, кто находился в её середине», – говорит Экскофье. «Поэтому люди и бактерии в чём-то схожи – индивиды на границе обитания больше влияют на генофонд будущих поколений».

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

* * *

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

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

Понимание микробной кооперации может стать важным по другим причинам, говорит Джоа Ксавье [Joao Xavier], вычислительный биолог в Центре изучения арка им. Слоана-Кеттеринга в Нью-Йорке. К примеру, динамика пространственной экспансии могла бы объяснить, как плотные опухоли приобретают способность распространяться при помощи метастаз.

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

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

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

С другой стороны, популяции редко стабилизируются полностью. «В естественных популяциях кооперация поддерживается из-за частых случаев экспансии, – говорит Кирилл Королёв, физик из Бостонского университета, коллега Гоура. – Возможно, периодически происходят большие встряски, типа лесных пожаров, после которых популяции медленно восстанавливаются».
ссылка на оригинал статьи https://geektimes.ru/post/287236/

Обновился Yii2 плагин для PhpStorm

В новой верии yii2support для phpStorm появилось автодополнение иницилизарующих массивов, которые используются в Yii повсеместно. С помощью массивов производится иницилизация объектов. Массивы принимает как конструктор базового класса Object, так как менеджер зависимостей Yii::createObject. Это очень удобный функционал, удобство которого было ограничено отсуствием помощи IDE. Теперь это проблема решена, и работает не только автодополнение, но и всплывающая помощь, переходы на декларацию и обратно, переименование свойств, исправление опечаток.

Автодополнение работает в следующих случаях:

  • В массиве передаваемом в конструктор объектов и Yii::creareObject.
  • В массивах которые имеют ключ со значением указывающем на класс: FQN строка, ::className() или ::class()
  • Стандартные ключи в папке config
  • Виджеты
  • Ключ «columns» у GridView

Ранее уже было сделана работа с view и i18n.

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

Надеемся что ваша работа станет удобнее, а Yii популярнее.
ссылка на оригинал статьи https://habrahabr.ru/post/324706/

Профилирование и оптимизация веб-приложений на Go

enter image description here

Привет, меня зовут Павел Мурзаков, я – разработчик в команде Features в Badoo. Нам важно, чтобы наши сервисы потребляли как можно меньше ресурсов, поскольку каждый дополнительный сервер стоит денег. Поэтому мы часто профилируем и оптимизируем код. Часть наших демонов написана на Go, с оптимизацией кода на котором мне пришлось работать в последнее время. Благо в стандартной библиотеке Go есть множество готовых инструментов для этого.

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

Go имеет мощный встроенный профайлер, который поддерживает профилирование CPU, памяти, горутин и блокировок.

Подключение профайлера

Go предоставляет низкоуровневый API для профилирования runtime/pprof, но если вы разрабатываете демон, то удобнее работать с высокоуровневым пакетом net/http/pprof.

Всё, что вам нужно для подключения профайлера, – импортировать net/http/pprof; необходимые HTTP-обработчики будут зарегистрированы автоматически:

package main  import (     "net/http"     _ "net/http/pprof" )  func hiHandler(w http.ResponseWriter, r *http.Request) {     w.Write([]byte("hi")) }  func main() {     http.HandleFunc("/", hiHandler)     http.ListenAndServe(":8080", nil) }

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

package main  import (     "net/http"     "net/http/pprof" )  func hiHandler(w http.ResponseWriter, r *http.Request) {     w.Write([]byte("hi")) }  func main() {     r := http.NewServeMux()     r.HandleFunc("/", hiHandler)      // Регистрация pprof-обработчиков     r.HandleFunc("/debug/pprof/", pprof.Index)     r.HandleFunc("/debug/pprof/cmdline", pprof.Cmdline)     r.HandleFunc("/debug/pprof/profile", pprof.Profile)     r.HandleFunc("/debug/pprof/symbol", pprof.Symbol)     r.HandleFunc("/debug/pprof/trace", pprof.Trace)      http.ListenAndServe(":8080", r) }

Вот и всё. Запустите приложение, а затем используйте pprof tool:

go tool pprof [binary] http://127.0.0.1:8080/debug/pprof/profile

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

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

Пример: микросервис left-pad

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

$ curl "http://127.0.0.1:8080/v1/leftpad/?str=test&len=10&chr=*" {"str":"******test"}

Сервис должен собирать статистику: количество входящих запросов и продолжительность каждого запроса. Предполагается, что все собранные данные отправляются в агрегатор метрик (например, StatsD). Кроме того, сервису необходимо логировать параметры запроса: URL, IP-адрес и User Agent.

Начальный вариант реализации нашего примера можно найти на GitHub.
Компилируем и запускаем приложение:

go build && ./goprofex

Измерение производительности

Нам нужно определить, сколько запросов в секунду может обслуживать наш микросервис. Это можно сделать с помощью ab – Apache benchmarking tool:

ab -k -c 8 -n 100000 "http://127.0.0.1:8080/v1/leftpad/?str=test&len=50&chr=*" # -k   Включить постоянное HTTP-соединение (KeepAlive) # -c   Количество одновременных запросов # -n   Количество запросов, которое будет делать ab

Неплохо, но может быть быстрее:

Requests per second:    22810.15 [#/sec] (mean) Time per request:       0.042 [ms] (mean, across all concurrent requests)

Примечание: измерение проводилось на MacBook Pro Late 2013 (2,6 ГГц Intel Core i5, 8 Гб, 1600 МГц DDR3, macOS 10.12.3) с использованием Go 1.8.

Профилирование CPU

Снова запускаем Apache benchmarking tool, но уже с большим количеством запросов (1 млн должно быть достаточно). И одновременно запускаем pprof:

go tool pprof goprofex http://127.0.0.1:8080/debug/pprof/profile

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

Когда pprof перейдёт в интерактивный режим, введите top, чтобы увидеть список функций, которые в процентном соотношении больше всего присутствовали в полученной выборке. В нашем случае все эти функции из стандартной библиотеки и библиотеки времени выполнения (runtime), что для нас неинформативно:

(pprof) top 63.77s of 69.02s total (92.39%) Dropped 331 nodes (cum <= 0.35s) Showing top 10 nodes out of 78 (cum >= 0.64s)       flat  flat%   sum%        cum   cum%     50.79s 73.59% 73.59%     50.92s 73.78%  syscall.Syscall      4.66s  6.75% 80.34%      4.66s  6.75%  runtime.kevent      2.65s  3.84% 84.18%      2.65s  3.84%  runtime.usleep      1.88s  2.72% 86.90%      1.88s  2.72%  runtime.freedefer      1.31s  1.90% 88.80%      1.31s  1.90%  runtime.mach_semaphore_signal      1.10s  1.59% 90.39%      1.10s  1.59%  runtime.mach_semaphore_wait      0.51s  0.74% 91.13%      0.61s  0.88%  log.(*Logger).formatHeader      0.49s  0.71% 91.84%      1.06s  1.54%  runtime.mallocgc      0.21s   0.3% 92.15%      0.56s  0.81%  runtime.concatstrings      0.17s  0.25% 92.39%      0.64s  0.93%  fmt.(*pp).doPrintf

Есть более наглядный способ, который позволяет решить эту проблему – команда web. Она генерирует граф вызовов в формате SVG и открывает его в веб-браузере:

enter image description here

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

С помощью команды list можно подробно исследовать каждую функцию, например, list leftpad:

(pprof) list leftpad ROUTINE ================= main.leftpad in /Users/artem/go/src/github.com/akrylysov/goprofex/leftpad.go       20ms      490ms (flat, cum)  0.71% of Total          .          .      3:func leftpad(s string, length int, char rune) string {          .          .      4:   for len(s) < length {       20ms      490ms      5:       s = string(char) + s          .          .      6:   }          .          .      7:   return s          .          .      8:}

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

(pprof) disasm leftpad ROUTINE ======================== main.leftpad       20ms      490ms (flat, cum)  0.71% of Total          .          .    1312ab0: GS MOVQ GS:0x8a0, CX          .          .    1312ab9: CMPQ 0x10(CX), SP          .          .    1312abd: JBE 0x1312b5e          .          .    1312ac3: SUBQ $0x48, SP          .          .    1312ac7: MOVQ BP, 0x40(SP)          .          .    1312acc: LEAQ 0x40(SP), BP          .          .    1312ad1: MOVQ 0x50(SP), AX          .          .    1312ad6: MOVQ 0x58(SP), CX ...

Профилирование кучи

Запустите профайлер кучи:

go tool pprof goprofex http://127.0.0.1:8080/debug/pprof/heap

По умолчанию он показывает объём используемой памяти:

(pprof) top 512.17kB of 512.17kB total (  100%) Dropped 85 nodes (cum <= 2.56kB) Showing top 10 nodes out of 13 (cum >= 512.17kB)       flat  flat%   sum%        cum   cum%   512.17kB   100%   100%   512.17kB   100%  runtime.mapassign          0     0%   100%   512.17kB   100%  main.leftpadHandler          0     0%   100%   512.17kB   100%  main.timedHandler.func1          0     0%   100%   512.17kB   100%  net/http.(*Request).FormValue          0     0%   100%   512.17kB   100%  net/http.(*Request).ParseForm          0     0%   100%   512.17kB   100%  net/http.(*Request).ParseMultipartForm          0     0%   100%   512.17kB   100%  net/http.(*ServeMux).ServeHTTP          0     0%   100%   512.17kB   100%  net/http.(*conn).serve          0     0%   100%   512.17kB   100%  net/http.HandlerFunc.ServeHTTP          0     0%   100%   512.17kB   100%  net/http.serverHandler.ServeHTTP

Но нас больше интересует количество размещённых в куче объектов. Запустим pprof с опцией -alloc_objects:

go tool pprof -alloc_objects goprofex http://127.0.0.1:8080/debug/pprof/heap

Почти 70% всех объектов были созданы двумя функциям – leftpad и StatsD.Send. Изучим их подробнее:

(pprof) top 559346486 of 633887751 total (88.24%) Dropped 32 nodes (cum <= 3169438) Showing top 10 nodes out of 46 (cum >= 14866706)       flat  flat%   sum%        cum   cum%  218124937 34.41% 34.41%  218124937 34.41%  main.leftpad  116692715 18.41% 52.82%  218702222 34.50%  main.(*StatsD).Send   52326692  8.25% 61.07%   57278218  9.04%  fmt.Sprintf   39437390  6.22% 67.30%   39437390  6.22%  strconv.FormatFloat   30689052  4.84% 72.14%   30689052  4.84%  strings.NewReplacer   29869965  4.71% 76.85%   29968270  4.73%  net/textproto.(*Reader).ReadMIMEHeader   20441700  3.22% 80.07%   20441700  3.22%  net/url.parseQuery   19071266  3.01% 83.08%  374683692 59.11%  main.leftpadHandler   17826063  2.81% 85.90%  558753994 88.15%  main.timedHandler.func1   14866706  2.35% 88.24%   14866706  2.35%  net/http.Header.clone

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

-inuse_objects, показывающий количество объектов в памяти;
-alloc_space, показывающий, сколько памяти было выделено с момента запуска программы.

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

Escape-анализ

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

func foo(a []string) {       fmt.Println(len(a)) }  func main() {       foo(make([]string, 8)) }

В приведённом выше примере make([]string, 8) выделяет память в стеке. Go использует escape-анализ, чтобы определить, можно ли безопасно выделить память в стеке вместо кучи. Вы можете добавить опцию -gcflags=-m, чтобы увидеть результаты escape-анализа:

5  type X struct {v int} 6 7  func foo(x *X) { 8       fmt.Println(x.v) 9  } 10 11 func main() { 12      x := &X{1} 13      foo(x) 14 }  go build -gcflags=-m ./main.go:7: foo x does not escape ./main.go:12: main &X literal does not escape

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

// Пример 1 type Fooer interface {       foo(a []string) }  type FooerX struct{}  func (FooerX) foo(a []string) {       fmt.Println(len(a)) }  func main() {       a := make([]string, 8) // make([]string, 8) escapes to heap       var fooer Fooer       fooer = FooerX{}       fooer.foo(a) }  // Пример 2 func foo(a interface{}) string {       return a.(fmt.Stringer).String() }  func main() {       foo(make([]string, 8)) // make([]string, 8) escapes to heap }

В статье Дмитрия Вьюкова Go Escape Analysis Flaws описаны и другие случаи, когда escape-анализ недостаточно хорош, чтобы понять, безопасно ли выделять память в стеке.
Вообще для небольших структур, которые вам не нужно изменять, предпочтительно использовать передачу по значению, а не по ссылке.
Примечание: для больших структур дешевле передать указатель, чем скопировать всю структуру и передать её по значению.

Профилирование горутин

При запуске профайлера горутин получаем их стек вызова и количество работающих горутин:

go tool pprof goprofex http://127.0.0.1:8080/debug/pprof/goroutine

enter image description here

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

Профилирование блокировок

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

Перед запуском профайлера блокировок необходимо с помощью функции runtime.SetBlockProfileRate установить уровень профилирования. Вы можете добавить её вызов в свою функцию main или init.

go tool pprof goprofex http://127.0.0.1:8080/debug/pprof/block

enter image description here

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

Бенчмаркинг

Как отмечалось выше, самыми большими нарушителями с точки зрения производительности являются функции пакетов log, leftpad и StatsD.Send. Мы нашли узкое место. Но прежде чем приступать к оптимизации, необходимо разработать воспроизводимый способ измерения производительности интересующего нас кода. Такой механизм включён в пакет testing. Нужно создать функцию вида func BenchmarkXxx(*testing.B) в тестовом файле:

func BenchmarkStatsD(b *testing.B) {     statsd := StatsD{         Namespace:  "namespace",         SampleRate: 0.5,     }     for i := 0; i < b.N; i++ {         statsd.Incr("test")     } }

Также можно с использованием пакета net/http/httptest провести бенчмаркинг всего HTTP-обработчика:

func BenchmarkLeftpadHandler(b *testing.B) {     r := httptest.NewRequest("GET", "/v1/leftpad/?str=test&len=50&chr=*", nil)     for i := 0; i < b.N; i++ {         w := httptest.NewRecorder()         leftpadHandler(w, r)     } }

Запускаем бенчмарк:

go test -bench=. -benchmem

Он показывает время, занимаемое каждой итерацией, а также объём и количество выделений памяти:

BenchmarkTimedHandler-4           200000          6511 ns/op        1621 B/op         41 allocs/op BenchmarkLeftpadHandler-4         200000         10546 ns/op        3297 B/op         75 allocs/op BenchmarkLeftpad10-4             5000000           339 ns/op          64 B/op          6 allocs/op BenchmarkLeftpad50-4              500000          3079 ns/op        1568 B/op         46 allocs/op BenchmarkStatsD-4                1000000          1516 ns/op         560 B/op         15 allocs/op

Повышение производительности

Логирование

Хороший, но не всегда очевидный способ сделать приложение быстрее – заставить его меньше работать. За исключением случаев отладки, строка log.Printf("%s request took %v", name, elapsed) не обязательно должна присутствовать в нашем сервисе. Перед развёртыванием приложения в продакшне все ненужные логи должны быть удалены из кода или отключены. Эта проблема может быть решена с помощью одной из многочисленных библиотек для логирования.

Ещё одна важная вещь, связанная с логированием (и вообще со всеми операциями ввода-вывода), – использование по возможности буферизованного ввода-вывода, что позволяет сократить количество системных вызовов. Обычно нет необходимости записывать в файл каждый вызов логгера – для реализации буферизованного ввода-вывода используйте пакет bufio. Мы можем просто обернуть передаваемый логгеру объект io.Writer в bufio.NewWriter или bufio.NewWriterSize:

log.SetOutput(bufio.NewWriterSize(f, 1024*16))

leftpad

Снова обратимся к функции leftpad:

func leftpad(s string, length int, char rune) string {     for len(s) < length {         s = string(char) + s     }     return s }

Конкатенация строк в цикле – не самая умная вещь, потому что каждая итерация цикла приводит к размещению в памяти новой строки. Лучшим способом построения строки является использование bytes.Buffer:

func leftpad(s string, length int, char rune) string {     buf := bytes.Buffer{}     for i := 0; i < length-len(s); i++ {         buf.WriteRune(char)     }     buf.WriteString(s)     return buf.String() }

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

func leftpad(s string, length int, char rune) string {     if len(s) < length {         return strings.Repeat(string(char), length-len(s)) + s     }     return s }

StatsD

Следующий фрагмент кода, который нам нужно изменить, – функция StatsD.Send:

func (s *StatsD) Send(stat string, kind string, delta float64) {     buf := fmt.Sprintf("%s.", s.Namespace)     trimmedStat := strings.NewReplacer(":", "_", "|", "_", "@", "_").Replace(stat)     buf += fmt.Sprintf("%s:%s|%s", trimmedStat, delta, kind)     if s.SampleRate != 0 && s.SampleRate < 1 {         buf += fmt.Sprintf("|@%s", strconv.FormatFloat(s.SampleRate, 'f', -1, 64))     }     ioutil.Discard.Write([]byte(buf)) // TODO: Write to a socket }

Вот несколько возможных улучшений:

  1. Функция sprintf удобна для форматирования строк. И это прекрасно, если вы не вызываете её тысячи раз в секунду. Она тратит процессорное время на разбор входящей форматированной строки и размещает в памяти новую строку при каждом вызове. Мы можем заменить её на bytes.Buffer + Buffer.WriteString/Buffer.WriteByte.

  2. Функция не должна каждый раз создавать новый экземпляр Replacer, он может быть объявлен ​​как глобальная переменная или как часть структуры StatsD.

  3. Замените strconv.FormatFloat на strconv.AppendFloat и передайте ему буфер, выделенный в стеке. Это предотвратит дополнительное выделение памяти в куче.

    func (s *StatsD) Send(stat string, kind string, delta float64) {         buf := bytes.Buffer{}         buf.WriteString(s.Namespace)         buf.WriteByte('.')         buf.WriteString(reservedReplacer.Replace(stat))         buf.WriteByte(':')         buf.Write(strconv.AppendFloat(make([]byte, 0, 24), delta, 'f', -1, 64))         buf.WriteByte('|')         buf.WriteString(kind)         if s.SampleRate != 0 && s.SampleRate < 1 {             buf.WriteString("|@")             buf.Write(strconv.AppendFloat(make([]byte, 0, 24), s.SampleRate, 'f', -1, 64))         }         buf.WriteTo(ioutil.Discard) // TODO: Write to a socket     } 

Это уменьшает количество выделений памяти с 14 до одного и примерно в четыре раза ускоряет вызов Send:

BenchmarkStatsD-4                5000000           381 ns/op         112 B/op          1 allocs/op

Измерение результата

После всех оптимизаций бенчмарки показывают очень хороший прирост производительности:

benchmark                     old ns/op     new ns/op     delta BenchmarkTimedHandler-4       6511          1181          -81.86% BenchmarkLeftpadHandler-4     10546         3337          -68.36% BenchmarkLeftpad10-4          339           136           -59.88% BenchmarkLeftpad50-4          3079          201           -93.47% BenchmarkStatsD-4             1516          381           -74.87%  benchmark                     old allocs     new allocs     delta BenchmarkTimedHandler-4       41             5              -87.80% BenchmarkLeftpadHandler-4     75             18             -76.00% BenchmarkLeftpad10-4          6              3              -50.00% BenchmarkLeftpad50-4          46             3              -93.48% BenchmarkStatsD-4             15             1              -93.33%  benchmark                     old bytes     new bytes     delta BenchmarkTimedHandler-4       1621          448           -72.36% BenchmarkLeftpadHandler-4     3297          1416          -57.05% BenchmarkLeftpad10-4          64            24            -62.50% BenchmarkLeftpad50-4          1568          160           -89.80% BenchmarkStatsD-4             560           112           -80.00%

Примечание: для сравнения результатов я использовал benchcmp.

Запускаем ab ещё раз:

Requests per second:    32619.54 [#/sec] (mean) Time per request:       0.030 [ms] (mean, across all concurrent requests)

Теперь веб-сервис может обрабатывать около 10 000 дополнительных запросов в секунду!

Советы по оптимизации

  • Избегайте ненужных выделений памяти в куче.
  • Для небольших структур используйте передачу параметров по значению, а не по ссылке.
  • Заранее выделяйте память под maps и slices, если вам известен размер.
  • Не логируйте без необходимости.
  • Используйте буферизованный ввод-вывод, если выполняете много последовательных операций чтения или записи.
  • Если ваше приложение широко использует JSON, то подумайте об использовании парсеров/ сериализаторов (лично я предпочитаю easyjson).
  • В горячих местах любая операция может привести к значительному снижению производительности.

Вывод

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

Вы можете найти полные исходники нашего примера на GitHub. Первоначальная версия помечена как v1, а оптимизированная – как v2. Вот ссылка для сравнения двух версий.

ссылка на оригинал статьи https://habrahabr.ru/post/324682/

Баг месяца: эстафета от PC-Lint к PVS-Studio

Недавно мы опубликовали 2 статьи про ТОП 10 ошибок в открытых проектах C++ и C# за предыдущий 2016 год. Статьи понравились читателям и это натолкнуло на мысль: почему бы не сделать такой ТОП для каждого месяца? Ведь на нашем сайте огромная база ошибок, которая хорошо структурирована и из которой можно выбрать самые интересные баги, найденные в определенный промежуток времени. В этой заметке будет рассказано о новой рубрике «Баг месяца» и о том, из чего мы исходили, когда готовили список ошибок для нее.

Picture 1


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

Начнем с того, что хотя номер диагностики бага месяца Gimpel Software реально существует, код, который представлен под ним, искусственно создан в виде задачки с забавным описанием, например: «Санта и его эльфы автоматизируют свою систему доставки Рождества, но один из эльфов сделал критическую ошибку, которая вызвала то, что в некоторых фрагментах кода действие происходит в непредсказуемом порядке», или: «Шон О’Флаэрти, возможно, был немного подвыпившим, когда писал программу ежегодного празднования Дня Святого Патрика в его пабе. Хотя он ее и не закончил, тут все-же есть небольшая проблема». Тут же предлагается найти этот баг самостоятельно, а ниже можно проверить себя и посмотреть в чем была проблема.

Мы же брали реальные ошибки, которые содержат проверенные нами Open Source проекты за несколько лет. Причем старались выбирать явные баги, которые будут понятны не только самому подготовленному читателю. Например, всеми любимый copy-paste, который встречается почти в каждом проекте, или различного рода опечатки. Помимо куска кода с ошибкой также приведена диагностика, с помощью которой PVS-Studio ее нашел и разъяснение, в чем собственно проблема. Ниже добавлена ссылка на статью о проверке данного проекта, где можно посмотреть, какие еще баги в нем содержались.

Как написано на официальном сайте Gimpel Software, их первый «баг месяца» был найден в марте 1991 года и список продолжал пополняться в течение 21 года. В свое время они были очень популярны и хорошо рекламировали PC-Lint. С 2012 года обновление этого раздела прекратилось, но разработчики надеются, что «баг месяца» может быть воскрешен в будущем.

Ну а пока этого не произошло, мы решили, так сказать, взять эстафету у PC-Lint и создать свой «баг месяца» с блэкдж.. Open Source проектами и найденными ошибками в них.

Picture 3

Итак, было просмотрено большое количество проектов, которые мы проверяли с января 2014 по март 2017 года и выбрано 54 интересных ошибки: 39 для C/C++ языка и 15 для C#, так как поддержка C# началась только с 2016 года. С обновляемой таблицей багов можно ознакомиться по этой ссылке https://hownot2code.com/the-bug-of-the-month/.

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

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

Для того, чтобы таких ошибок становилось меньше, а качество кода лучше, предлагаем скачать и воспользоваться PVS-Studio на своем проекте. Возможно вам удастся найти свой интересный баг месяца, о котором вы впоследствии сможете рассказать сообществу разработчиков и уберечь их от подобной ошибки. А пока можно потренироваться и попробовать самостоятельно найти ошибки в известных Open Source проектах — http://q.viva64.com/.

Примечание. В недавно выпущенной версии PVS-Studio 6.14 появилась поддержка Visual Studio 2017, Roslyn 2.0 / C# 7.0 для C# анализатора (см. историю версий).

Итак, добро пожаловать: The bug of the month!

Если хотите поделиться этой статьей с англоязычной аудиторией, то прошу использовать ссылку на перевод: Ekaterina Milovidova. Bug of the month: taking the baton from PC-Lint to PVS-Studio
ссылка на оригинал статьи https://habrahabr.ru/post/324704/