Случайны выбор дневника Раскрыть/свернуть полный список возможностей


Найдено 1235 сообщений
Cообщения с меткой

производительность - Самое интересное в блогах

Следующие 30  »
rss_rss_hh_new

[Перевод] Производительность без цикла событий

Пятница, 06 Мая 2016 г. 12:58 (ссылка)





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



Про Go часто говорят, что он хорош для серверов: здесь есть статические бинарники (static binaries), развитый concurrency, высокая производительность. В этой статье мы поговорим о двух последних пунктах: о том, как язык и среда выполнения (runtime) ненавязчиво позволяют Go-программистам создавать легко масштабируемые серверы и не беспокоиться из-за управления потоками (thread) или блокирующих операций ввода/вывода.



Аргумент в пользу производительности языка



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



Закон Мура







Согласно часто ошибочно цитируемому закону Мура, количество транзисторов на единице площади кристалла удваивается примерно каждые 18 месяцев. Однако рабочие частоты, зависящие от совершенно других свойств, перестали расти уже с десяток лет назад, с выходом Pentium 4, и с тех пор понемногу снижаются.



От пространственных ограничений к энергетическим





Sun Enterprise e450 — размером примерно с барный холодильник и потребляет примерно столько же электричества



Это Sun e450. Когда моя карьера только начиналась, эти компьютеры были рабочими лошадками индустрии. Они были массивны. Если поставить один на другой три штуки, то они займут целую 19-дюймовую стойку. При этом каждый потреблял всего лишь около 500 Вт.



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



Эффект от энергетических ограничений проявился:




  • на макроуровне — мало кто может обеспечить работу стойки с 1200-ваттными 1U-серверами;

  • на микроуровне — все эти сотни ватт рассеиваются на маленьком кремниевом кристалле в виде тепла.





С чем связан такой рост энергопотребления?





КМОП-инвертор



Это инвертор, один из простейших логических вентилей. Если на вход А подаётся высокий уровень, то на выход Q подаётся низкий, и наоборот. Вся современная потребительская электроника построена на КМОП-логике (КМОП — комплементарная структура металл-оксид-полупроводник). Ключевое слово здесь «комплементарная». Каждый логический элемент внутри процессора реализован с помощью пары транзисторов: когда один включается, другой выключается.



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



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



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



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



«Халява кончилась»



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



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



Аргумент в пользу параллельного языка программирования



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







Одновременная многопоточность, или Hyper-Threading, как это называет Intel, позволяет одному ядру параллельно выполнять несколько потоков инструкций благодаря добавлению небольшой аппаратной обвязки. Intel применяет технологию Hyper-Threading для искусственного сегментирования рынка процессоров, в то время как Oracle и Fujitsu активнее используют её в своей продукции, доводя количество аппаратных потоков выполнения до 8 или 16 на каждое ядро.



Двухпроцессорные материнские платы появились в конце 1990-х, когда вышел Pentium Pro. Сегодня это стандартное решение, большинство серверов поддерживают двух- или четырёхпроцессорные конфигурации. Увеличение плотности транзисторов позволило даже размещать несколько ядер на одном кристалле. Двухъядерные процессоры обосновались в мобильном сегменте, четырёхъядерные — в настольном, ещё больше ядер в серверном сегменте. По сути, сегодня количество ядер в сервере ограничено лишь вашим бюджетом.



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



Процессы, потоки выполнения и горутины



В основе параллелизма Go лежат так называемые горутины (goroutine). Давайте немного отвлечёмся и вспомним историю их возникновения.



Процессы



На заре времён, при пакетной модели обработки, компьютеры могли выполнять в один отрезок времени только одну задачу. Стремление к более интерактивным формам вычисления привело в 1960-х к разработке многопроцессных операционных систем, или систем, работающих в режиме разделения времени (time sharing). В 1970-х эта идея проникла в серверы, FTP, Telnet, rlogin, а позднее и в CERN httpd Тима Бернерса-Ли. Обработка всех входящих сетевых соединений сопровождалась порождением (forking) дочерних процессов.



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



Переключение контекста







У переключения контекста есть три основные статьи расходов:




  • Ядро должно сохранять содержимое всех регистров процессора сначала для одного процесса, потом восстанавливать значения для другого. Поскольку переключение между процессами может произойти в любой момент, ОС должна хранить содержимое всех регистров, потому что она не знает, какие из них сейчас используются. Конечно, это крайне упрощённое описание. В ряде случае ОС может избегать сохранения и восстановления часто используемых архитектурных регистров, запуская процесс в таком режиме, при котором доступ к floating-point или MMX/SSE-регистрам вызовет прерывание (fault). В таких ситуациях ядро понимает, что процесс будет использовать эти регистры и их нужно сохранять и восстанавливать.

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

  • Накладные расходы ОС на переключение контекста, а также накладные расходы функции-планировщика при выборе следующего процесса для обеспечения процессора работой.



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



Потоки



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



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



Горутины



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




  • отправка и приём из канала, если они приведут к блокировке;

  • вызов инструкции go func(...), хотя нет гарантии, что переключение на новую горутину произойдёт немедленно;

  • возникновение блокирующих системных вызовов, например операций с файлами или сетевых операций;

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





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



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



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



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



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



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



Адресное пространство процесса







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







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



Стеки потока







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



Недостаток этого подхода в том, что с увеличением количества потоков в программе уменьшается объём доступного адресного пространства.



Управление стеком горутин



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



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



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



Рост стека горутин







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



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



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



Интегрированный сетевой поллер (network poller)



В 2002 году Дэн Кегель (Dan Kegel) опубликовал статью «Проблема c10k». Говоря простым языком, она была посвящена написанию серверного ПО, способного обрабатывать не менее 10 000 ТСР-сессий на недорогом оборудовании, доступном в то время. После написания этой статьи возникло расхожее мнение, что высокопроизводительные серверы нуждаются в нативных потоках. Позднее их место заняли циклы событий (event loops).



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



Go взял всё самое лучшее из этих двух подходов.



Ответ Go на проблему c10k



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



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



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



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



Горутины, управление стеком и интегрированный сетевой поллер



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



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



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



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



И все эти возможности совершенно прозрачны для Go-программиста.

Original source: habrahabr.ru.

https://habrahabr.ru/post/283038/?utm_source=habrahabr&utm_medium=rss&utm_campaign=best

Метки:   Комментарии (0)КомментироватьВ цитатник или сообщество
rss_rss_hh_new

[Перевод] C/C++: как измерять процессорное время

Суббота, 24 Апреля 2016 г. 01:02 (ссылка)

image

КДПВ



От переводчика:

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



P.S. Когда в статье написано "сегодня" или "сейчас", имеется ввиду "на момент выхода статьи", то есть, если я не ошибаюсь, март 2012. Ни я, ни автор не гарантируем, что это до сих пор так.

P.P.S. На момент публикации оригинал недоступен, но хранится в кэше Яндекса



Функции API, позволяющие получить процессорное время, использованное процессом, отличаются в разных операционных системах: Windows, Linux, OSX, BSD, Solaris, а также прочих UNIX-подобных ОС. Эта статья предоставляет кросс-платформенную функцию, получающую процессорное время процесса и объясняет, какие функции поддерживает каждая ОС.



Как получить процессорное время



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



Разные инструменты, такие как ps в POSIX, Activity Monitor в OSX и Task Manager в Windows показывают процессорное время, используемое процессами, но часто бывает полезным отслеживать его прямо из самого процесса. Это особенно полезно во время бенчмаркинга алгоритмов или маленькой части сложной программы. Несмотря на то, что все ОС предоставляют API для получения процессорного времени, в каждой из них есть свои тонкости.



Код



Функция getCPUTime( ), представленная ниже, работает на большинстве ОС (просто скопируйте код или скачайте файл getCPUTime.c). Там, где это нужно, слинкуйтесь с librt, чтобы получить POSIX-таймеры (например, AIX, BSD, Cygwin, HP-UX, Linux и Solaris, но не OSX). В противном случае, достаточно стандартных библиотек.



Далее мы подробно обсудим все функции, тонкости и причины, по которым в коде столько #ifdef'ов.



getCPUTime.c
/*
* Author: David Robert Nadeau
* Site: http://NadeauSoftware.com/
* License: Creative Commons Attribution 3.0 Unported License
* http://creativecommons.org/licenses/by/3.0/deed.en_US
*/
#if defined(_WIN32)
#include

#elif defined(__unix__) || defined(__unix) || defined(unix) || (defined(__APPLE__) && defined(__MACH__))
#include
#include resource.h>
#include times.h>
#include

#else
#error "Unable to define getCPUTime( ) for an unknown OS."
#endif

/**
* Returns the amount of CPU time used by the current process,
* in seconds, or -1.0 if an error occurred.
*/
double getCPUTime( )
{
#if defined(_WIN32)
/* Windows -------------------------------------------------- */
FILETIME createTime;
FILETIME exitTime;
FILETIME kernelTime;
FILETIME userTime;
if ( GetProcessTimes( GetCurrentProcess( ),
&createTime, &exitTime, &kernelTime, &userTime ) != -1 )
{
SYSTEMTIME userSystemTime;
if ( FileTimeToSystemTime( &userTime, &userSystemTime ) != -1 )
return (double)userSystemTime.wHour * 3600.0 +
(double)userSystemTime.wMinute * 60.0 +
(double)userSystemTime.wSecond +
(double)userSystemTime.wMilliseconds / 1000.0;
}

#elif defined(__unix__) || defined(__unix) || defined(unix) || (defined(__APPLE__) && defined(__MACH__))
/* AIX, BSD, Cygwin, HP-UX, Linux, OSX, and Solaris --------- */

#if defined(_POSIX_TIMERS) && (_POSIX_TIMERS > 0)
/* Prefer high-res POSIX timers, when available. */
{
clockid_t id;
struct timespec ts;
#if _POSIX_CPUTIME > 0
/* Clock ids vary by OS. Query the id, if possible. */
if ( clock_getcpuclockid( 0, &id ) == -1 )
#endif
#if defined(CLOCK_PROCESS_CPUTIME_ID)
/* Use known clock id for AIX, Linux, or Solaris. */
id = CLOCK_PROCESS_CPUTIME_ID;
#elif defined(CLOCK_VIRTUAL)
/* Use known clock id for BSD or HP-UX. */
id = CLOCK_VIRTUAL;
#else
id = (clockid_t)-1;
#endif
if ( id != (clockid_t)-1 && clock_gettime( id, &ts ) != -1 )
return (double)ts.tv_sec +
(double)ts.tv_nsec / 1000000000.0;
}
#endif

#if defined(RUSAGE_SELF)
{
struct rusage rusage;
if ( getrusage( RUSAGE_SELF, &rusage ) != -1 )
return (double)rusage.ru_utime.tv_sec +
(double)rusage.ru_utime.tv_usec / 1000000.0;
}
#endif

#if defined(_SC_CLK_TCK)
{
const double ticks = (double)sysconf( _SC_CLK_TCK );
struct tms tms;
if ( times( &tms ) != (clock_t)-1 )
return (double)tms.tms_utime / ticks;
}
#endif

#if defined(CLOCKS_PER_SEC)
{
clock_t cl = clock( );
if ( cl != (clock_t)-1 )
return (double)cl / (double)CLOCKS_PER_SEC;
}
#endif

#endif

return -1; /* Failed. */
}


Использование



Чтобы замерить процессорное время алгоритма, вызовите getCPUTime( ) до и после запуска алгоритма, и выведите разницу. Не стоит предполагать, что значение, возвращенное при единичном вызове функции, несет какой-то смысл.



double startTime, endTime;

startTime = getCPUTime( );
...
endTime = getCPUTime( );

fprintf( stderr, "CPU time used = %lf\n", (endTime - startTime) );


Обсуждение



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








































































OS clock clock_gettime GetProcessTimes getrusage times
AIX yes yes yes yes
BSD yes yes yes yes
HP-UX yes yes yes yes
Linux yes yes yes yes
OSX yes yes yes
Solaris yes yes yes yes
Windows yes


Каждый из этих способов подробно освещен ниже.



GetProcessTimes( )



На Windows и Cygwin (UNIX-подобная среда и интерфейс командной строки для Windows), функция GetProcessTimes( ) заполняет структуру FILETIME процессорным временем, использованным процессом, а функция FileTimeToSystemTime( ) конвертирует структуру FILETIME в структуру SYSTEMTIME, содержащую пригодное для использования значение времени.



typedef struct _SYSTEMTIME
{
WORD wYear;
WORD wMonth;
WORD wDayOfWeek;
WORD wDay;
WORD wHour;
WORD wMinute;
WORD wSecond;
WORD wMilliseconds;
} SYSTEMTIME, *PSYSTEMTIME;


Доступность GetProcessTimes( ): Cygwin, Windows XP и более поздние версии.



Получение процессорного времени:



#include 
...

FILETIME createTime;
FILETIME exitTime;
FILETIME kernelTime;
FILETIME userTime;
if ( GetProcessTimes( GetCurrentProcess( ),
&createTime, &exitTime, &kernelTime, &userTime ) != -1 )
{
SYSTEMTIME userSystemTime;
if ( FileTimeToSystemTime( &userTime, &userSystemTime ) != -1 )
return (double)userSystemTime.wHour * 3600.0 +
(double)userSystemTime.wMinute * 60.0 +
(double)userSystemTime.wSecond +
(double)userSystemTime.wMilliseconds / 1000.0;
}


clock_gettme( )



На большинстве POSIX-совместимых ОС, clock_gettime( ) (смотри мануалы к AIX, BSD, HP-UX, Linux и Solaris) предоставляет самое точное значение процессорного времени. Первый аргумент функции выбирает "clock id", а второй это структура timespec, заполняемая использованным процессорным временем в секундах и наносекундах. Для большинства ОС, программа должна быть слинкована с librt.



Однако, есть несколько тонкостей, затрудняющих использование этой функции в кросс-платформенном коде:




  • Функция является опциональной частью стандарта POSIX и доступна только если _POSIX_TIMERS определен в значением больше 0. На сегодняшний день, AIX, BSD, HP-UX, Linux и Solaris поддерживают эту функцию, но OSX не поддерживает.

  • Структура timespec, заполняемая функцией clock_gettime( ) может хранить время в наносекундах, но точность часов отличается в разных ОС и на разных системах. Функция clock_getres( ) возвращает точность часов, если она вам нужна. Эта функция, опять-таки, является опциональной частью стандарта POSIX, доступной только если _POSIX_TIMERS больше нуля. На данный момент, AIX, BSD, HP-UX, Linux и Solaris предоставляют эту функцию, но в Solaris она не работает.

  • стандарт POSIX определяет имена нескольких стандартных значений "clock id", включая CLOCK_PROCESS_CPUTIME_ID, чтобы получить процессорное время процесса. Тем не менее, сегодня BSD и HP-UX не имеют этого id, и взамен определяют собственный id CLOCK_VIRTUAL для процессорного времени. Чтобы запутать все ещё больше, Solaris определяет оба этих, но использует CLOCK_VIRTUAL для процессорного времени потока, а не процесса.
































ОС Какой id использовать
AIX CLOCK_PROCESS_CPUTIME_ID
BSD CLOCK_VIRTUAL
HP-UX CLOCK_VIRTUAL
Linux CLOCK_PROCESS_CPUTIME_ID
Solaris CLOCK_PROCESS_CPUTIME_ID



  • Вместо того, чтобы использовать одну из констант, объявленных выше, функция clock_getcpuclockid( ) возвращает таймер для выбранного процесса. Использование процесса 0 позволяет получить процессорное время текущего процесса. Однако, это ещё одна опциональная часть стандарта POSIX и доступна только если _POSIX_CPUTIME больше 0. На сегодняшний день, только AIX и Linux предоставляют эту функцию, но линуксовские include-файлы не определяют _POSIX_CPUTIME и функция возвращает ненадёжные и несовместимые с POSIX результаты.

  • Функция clock_gettime( ) может быть реализована с помощью регистра времени процессора. На многопроцессорных системах, у отдельных процессоров может быть несколько разное восприятие времени, из-за чего функция может возвращать неверные значения, если процесс передавался от процессора процессору. На Linux, и только на Linux, это может быть обнаружено, если clock_getcpuclockid( ) возвращает не-POSIX ошибку и устанавливает errno в ENOENT. Однако, как замечено выше, на Linux clock_getcpuclockid( ) ненадежен.



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



Доступность clock_gettime( ): AIX, BSD, Cygwin, HP-UX, Linux и Solaris. Но clock id на BSD и HP-UX нестандартные.



Доступность clock_getres( ): AIX, BSD, Cygwin, HP-UX и Linux, но не работает Solaris.



Доступность clock_getcpuclockid( ): AIX и Cygwin, не недостоверна на Linux.



Получение процессорного времени:



#include 
#include
...

#if defined(_POSIX_TIMERS) && (_POSIX_TIMERS > 0)
clockid_t id;
struct timespec ts;
#if _POSIX_CPUTIME > 0
/* Clock ids vary by OS. Query the id, if possible. */
if ( clock_getcpuclockid( 0, &id ) == -1 )
#endif

#if defined(CLOCK_PROCESS_CPUTIME_ID)
/* Use known clock id for AIX, Linux, or Solaris. */
id = CLOCK_PROCESS_CPUTIME_ID;
#elif defined(CLOCK_VIRTUAL)
/* Use known clock id for BSD or HP-UX. */
id = CLOCK_VIRTUAL;
#else
id = (clockid_t)-1;
#endif
if ( id != (clockid_t)-1 && clock_gettime( id, &ts ) != -1 )
return (double)ts.tv_sec +
(double)ts.tv_nsec / 1000000000.0;
#endif


getrusage( )



На всех UNIX-подобных ОС, функция getrusage( ) это самый надежный способ получить процессорное время, использованное текущим процессом. Функция заполняет структуру rusage временем в секундах и микросекундах. Поле ru_utime содержит время проведенное в user mode, а поле ru_stime — в system mode от имени процесса.



Внимание: Некоторые ОС, до широкого распространения поддержки 64-бит, определяли функцию getrusage( ), возвращающую 32-битное значение, и функцию getrusage64( ), возвращающую 64-битное значение. Сегодня, getrusage( ) возвращает 64-битное значение, аgetrusage64( ) устарело.



Доступность getrusage( ): AIX, BSD, Cygwin, HP-UX, Linux, OSX, and Solaris.



Получение процессорного времени:



#include resource.h>
#include times.h>
...

struct rusage rusage;
if ( getrusage( RUSAGE_SELF, &rusage ) != -1 )
return (double)rusage.ru_utime.tv_sec +
(double)rusage.ru_utime.tv_usec / 1000000.0;


times( )



На всех UNIX-подобных ОС, устаревшая функция times( ) заполняет структуру tms с процессорным временем в тиках, а функция sysconf( ) возвращает количество тиков в секунду. Поле tms_utime содержит время, проведенное в user mode, а поле tms_stime — в system mode от имени процесса.



Внимание: Более старый аргумент функции sysconf( ) CLK_TCK устарел и может не поддерживаться в некоторых ОС. Если он доступен, функция sysconf( ) обычно не работает при его использовании. Используйте _SC_CLK_TCK вместо него.



Доступность times( ): AIX, BSD, Cygwin, HP-UX, Linux, OSX и Solaris.



Получение процессорного времени:



#include 
#include times.h>
...

const double ticks = (double)sysconf( _SC_CLK_TCK );
struct tms tms;
if ( times( &tms ) != (clock_t)-1 )
return (double)tms.tms_utime / ticks;


clock( )



На всех UNIX-подобных ОС, очень старая функция clock( ) возвращает процессорное время процесса в тиках, а макрос CLOCKS_PER_SEC количество тиков в секунду.



Заметка: Возвращенное процессорное время включает в себя время проведенное в user mode И в system mode от имени процесса.



Внимание: Хотя изначально CLOCKS_PER_SEC должен был возвращать значение, зависящее от процессора, стандарты C ISO C89 и C99, Single UNIX Specification и стандарт POSIX требуют, чтобы CLOCKS_PER_SEC имел фиксированное значение 1,000,000, что ограничивает точность функции микросекундами. Большинство ОС соответствует этим стандартам, но FreeBSD, Cygwin и старые версии OSX используют нестандартные значения.



Внимание: На AIX и Solaris, функция clock( ) включает процессорное время текущего процесса И и любого завершенного дочернего процесса для которого родитель выполнил одну из функций wait( ), system( ) или pclose( ).



Внимание: В Windows, функция clock( ) поддерживается, но возвращает не процессорное, а реальное время.



Доступность clock( ): AIX, BSD, Cygwin, HP-UX, Linux, OSX и Solaris.



Получение процессорного времени:



#include 
...

clock_t cl = clock( );
if ( cl != (clock_t)-1 )
return (double)cl / (double)CLOCKS_PER_SEC;


Другие подходы



Существуют и другие ОС-специфичные способы получить процессорное время. На Linux, Solarisи некоторых BSD, можно парсить /proc/[pid]/stat, чтобы получить статистику процесса. На OSX, приватная функция API proc_pidtaskinfo( ) в libproc возвращает информацию о процессе. Также существуют открытые библиотеки, такие как libproc, procps и Sigar.



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



На Windows, можно использовать диспетчер задач, чтобы мониторить использование CPU.



На OSX, можно использовать Activity Monitor, чтобы мониторить использование CPU. Утилита для профайлинга Instruments поставляемая в комплекте с Xcode может мониторить использование CPU, а также много других вещей.



Downloads




  • getCPUTime.c реализует выше описанную функцию на C. Скомпилируйте её любым компилятором C и слинкуйте с librt, на системах где она доступна. Код лицензирован под Creative Commons Attribution 3.0 Unported License.



Смотри также



Связанные статьи на NadeauSoftware.com





Статьи в интернете




  • Процессорное время на википедии объясняет, что такое процессорное время.

  • CPU Time Inquiry на GNU.org объясняет как использовать древнюю функцию clock( ).

  • Determine CPU usage of current process (C++ and C#) предоставляет код и объяснения для получения процессорного времени и другой статистики на Windows.

  • Posix Options на Kernel.org объясняет опциональные фичи и константы POSIX, включая _POSIX_TIMERS и _POSIX_CPUTIME.







Как вы замеряете время в своих бенчмарках?


























































Проголосовало 13 человек. Воздержалось 12 человек.




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





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

https://habrahabr.ru/post/282301/

Метки:   Комментарии (0)КомментироватьВ цитатник или сообщество
rss_rss_hh_new

[Из песочницы] Увеличение производительности Magento

Среда, 20 Апреля 2016 г. 13:03 (ссылка)

…или правильная работа с коллекциями.



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



В этой статье рассказано о Magento 1.*, но описанное так же подходит и для Magento 2.*.



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



$temp = array();
$collection = Mage::getModel('catalog/product')->getCollection()->addAttributeToSelect('*');
foreach ($collection as $product) {
$product = $product->load($product->getId());
$temp[] = $product->getSku();
}
Неправильно



вместо



$temp = array();
$collection = Mage::getModel('catalog/product')->getCollection()->addAttributeToSelect('sku');
foreach ($collection as $product) {
$temp[] = $product->getSku();
}
Правильно



Причины такого очень просты:




  1. После загрузки нет необходимых атрибутов

  2. Так делают «программисты» в интернете

  3. Загрузка лишних атрибутов по принципу «хуже не будет»



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




  1. Eav/Flat таблицы

  2. Cache

  3. Правильная работа с коллекциями



И конечно же выводы.






EAV/Flat таблицы



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



В Magento к EAV сущностям относятся: продукты, категории, кастомеры и кастомер адреса. Сами же атрибуты хранятся в eav_attribute таблице.



Всего типов значений атрибутов в Magento 5: text, varchar, int, decimal и datetime. Есть еще 1 тип – static, он отличается от остальных 5ти тем, что находится в таблице с сущностью.



В таблице атрибутов указано в какой таблице или какого типа является тот или иной атрибут и Magento уже знает куда его писать и откуда читать.



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



Как это хранится в БД
Entity:

Product – catalog_product_entity,

Category – catalog_category_entity,

Customer – customer_entity,

Customer address – customer_address_entity



Attribute:

eav_attribute

catalog_eav_attribute

customer_eav_attribute



Value:

*_text

*_varchar

*_int

*_decimal

*_datetime



Flat — это привычный нам всем подход, где все лежит в 1 месте и никакие дополнительные таблицы нам не нужны, чтобы получить товар и все его атрибуты без лишней работы – SELECT * FROM табличка WHERE id = какой то ид и все.



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



Как это хранится в БД
Product:

catalog_product_flat_1 // *_N store_view

Category:

catalog_category_flat_1 // *_N store_view



Для того, чтобы включить атрибут во Flat таблицу и вообще включить использование Flat таблиц необходимо проделать следующее
В админке Catalog > Attributes > Manage attributes



Magento добавит атрибут во Flat таблицу если у атрибута выставлено 1 из ниже указанных значений.







В админке System > Configuration > Catalog



Magento будет использовать Flat таблицы для сущностей указанных ниже.







Обратите внимание на следующие факты:




  1. Flat таблицы используются ТОЛЬКО на страницах категорий, списке продуктов в составе Group product, да и вообще везде, где используется коллекция. Они не используются на странице товаров, в админке, при использовании метода load у модели.

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

  3. После включения Flat таблиц Magento все равно продолжает использовать EAV, но так же начинает копировать изменения во Flat таблицу при сохранении изменений



Зачем же все вот это надо и почему бы не использовать везде Flat подход? Посмотрите на сводную таблицу плюсов и минусов
EAV:

+ Более гибкая система чем Flat

+ При добавлении нового атрибута нет необходимости реиндексировать данные

+ Практически не ограниченное количество атрибутов

+ Всегда доступны все атрибуты

+ Статик атрибуты (sku, created_at, updated_at) всегда присутствуют в выборке, даже если их не указывать специально

— Fatal error: Call to a member function getBackend() при выборке/фильтрации по не существующему атрибуту

— Производительность



Flat:

+ Производительность

+ В выборку/фильтрацию могут быть применены только существующие атрибуты, которые добавлены во Flat таблицу

— Ограничение на размер строки (до 65,535 байт, т.е. 85 varchar 255) и количеству столбцов (InnoDB до 1000, некоторые до 4096)

— Используется только при работе с коллекциям (при загрузке всегда используется EAV)

— Результат отличается от выдачи запроса при EAV (отсутствуют статик атрибуты)

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

— При добавлении нового атрибута необходимо реиндексировать Flat таблицы






Cache



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



Типы кэшей в Magento 1.*:








  • Configuration – кэширует конфигурационные файлы

  • Layout – кэширует layout файлы

  • Block HTML output – кэширует phtml шаблоны. По умолчанию используется на фронтенде только в top menu и footer.

  • Translations – кэширует csv транслейт файлы

  • Collections data – кэширует коллекции, которые используют ->initCache(…) метод. По умолчанию кэширует только core_store, core_store_group, core_website коллекции при инициализации.

  • EAV types and attributes – должен кэшировать eav атрибуты, НО не кэширует. Используется в 1 методе, который никогда не вызывается начиная с Magneto CE 1.4

  • Web services cache – кэширует api.xml файлы

  • Page Cache (FPC) – кэширует весь HTML, кэширует только CMS, Category, Product страницы. Игнорируется если протокол https, гет параметр ?no_cache=1, куки NO_CACHE

  • DDL Cache (Скрытый) – кэширует DESCRIBE вызовы к БД, используется в операциях записи



…и ни 1 не кэширует коллекции автоматически.




Правильная работа с коллекциями



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



Тестовый стенд:

OS X 10.10

3.1 GHz Intel Core i5 (4 cores)

8GB



Magento конфигурация:

Magento EE 1.14.0

MySQL 5.5.38

PHP 5.6.2



Контент:

3 Categories

2000 Products

2000 CMS pages



Процесс:

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



class Test_Test_IndexController extends Mage_Core_Controller_Front_Action
{
public function indexAction()
{
$temp = array();
$start = microtime(true);
Init values

Loop start
$temp[] = $product->getSku();
Loop end
Or
Some code snippet

$stop = microtime(true);
echo $stop - $start;
}
}


Псевдо код

Тесты




  1. EAV/Flat с перезагрузкой моделей и без

  2. Кэширование коллекций

  3. Правильное использование count() и getSize()

  4. Правильное использование getFirstItem и setPage(1,1)



EAV/Flat с перезагрузкой моделей и без



Цикл по коллекции. С load (перезагрузка) моделей внутри цикла:



$temp = array();
$collection = Mage::getModel('catalog/product')->getCollection()->addAttributeToSelect(...);
foreach ($collection as $product) {
$product = $product->load($product->getId());
$temp[] = $product->getSku();
}


Цикл по коллекции. Без load моделей внутри:



$temp = array();
$collection = Mage::getModel('catalog/product')->getCollection()->addAttributeToSelect(...);
foreach ($collection as $product) {
$temp[] = $product->getSku();
}


3 вида выборки данных:




  1. addAttributeToSelect(‘*’); // все атрибуты

  2. addAttributeToSelect(‘sku’); // 1 статик атрибут

  3. addAttributeToSelect(‘name’); // 1 стандартный атрибут



Результаты




Как вы видимо заметили, время без перезагрузки моделей В РАЗЫ меньше, чем когда вы перезагружаете модельки. Так же время еще меньше, когда Flat таблицы включены (т.е. нет лишних джойнов и юнионов) и мы выбираем только необходимые атрибуты.



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



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



3ий раз Magento нужно приджойнить еще табличку где хранится этот атрибут.



С Flat таблицами все аналогично, а в 2ух случаях вcе идентично – это потому что оба атрибута находятся в 1 таблице, отсюда и время идентичное.



Думаю цифры говорят сами за себя.





Кэширование коллекций



Без кэша:



$collection = Mage::getModel('catalog/product')->getCollection()
->addAttributeToSelect('*');


Используя метод initCache:



$collection = Mage::getModel('catalog/product')->getCollection()
->addAttributeToSelect('*')
->initCache(Mage::app()->getCache(),'our_data',array('SOME_TAGS'));


Кастомная реализация кэширования:



$cache = Mage::app()->getCache();
$collection = $cache->load('our_data');
if(!collection) {
$collection = Mage::getModel('collection/product')->getCollection()->addAttributeToSelect('*')->getItems();
$cache->save(serialize($collection),'our_data',array(Mage_Core_Model_Resource_Db_Collection_Abstract::CACHE_TAG));
} else {
$collection = unserialize($collection);
}


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



Результаты




Без кэша собственно ничего удивительного…все как обычно.



А вот используя маджентовский кэш я лично удивился, когда увидел, что время стало больше. А про EAV кэширование вообще глупой затеей, потому что EAV коллекция грузит сначала сущности из таблицы продуктов (именно вот это и кэшируется), а потом отдельным запросом выбирает значения атрибутов и заполняет объекты. Во Flat там все из 1 таблицы гонится. Но тем не менее время больше уходится на работу с кэшем чем с БД (тестировал я причем как с файловой системой, так и с redis – отличия 4ая цифра после запятой…т.е. на 2к сущностях ее нет). Суть InitCache метода заключается в том, что он сначала соберет все данные в коллекцию сам (пагинация, фильтры, events и так далее), создаст хеш из sql запроса и его будет искать в кэше, а если там что-то есть, то он это ансерелизует, а потом происходит запуск всех events и последующих методов. Это самая медленная процедура во всем процессе, именно вот тут выходит что кэш медленнее чем простой запрос в БД. Но зато не шлет запрос в БД… что не так и страшно уже.



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



Правильное использование count() и getSize()



getSize()



$size = Mage::getModel('catalog/product')->getCollection()
->addAttributeToSelect('*')
->getSize();


count()



$size = Mage::getModel('catalog/product')->getCollection()
->addAttributeToSelect('*')
->count();


Результаты




Разница методов заключается в том, что count() производит загрузку всех объектов коллекции, а потом обычным пхпшным count’ом подсчитывает количество объектов и возвращает нам число. getSize же не производит загрузку коллекции, а генерирует еще 1 запрос в БД, где нет лимитов, ордеров и списка выбираемых атрибутов, есть только COUNT(*).



Пример использования обоих методов такой:



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



Правильное использование getFirstItem и setPage(1,1)



getFirstItem()



$product = Mage::getModel('catalog/product')->getCollection()
->getFirstItem();


setPage(1,1)



$product = Mage::getModel('catalog/product')->getCollection()
->setPage(1,1)
->getFirstItem();


load()



$product = Mage::getModel('catalog/product')->load(22);


Результаты




Проблема getFirstItem в том, что он загружает всю коллекцию целиком, а потом просто в foreach возвращает первый элемент, а если его нет то возвращает пустой объект.



setPage (он же $this->setCurPage($pageNum)->setPageSize($pageSize)) же ограничивает выборку ровно 1 записью, что как вы видите значительно ускоряет загрузку результата.



Даже load быстрее getFirstItem, но обратите внимание, что load медленнее оказался чем выборка из коллекции 1 элемента. Это связано с тем, что load всегда работает с EAV таблицами.






Выводы



Подводя итог всему выше написанному, хочу посоветовать всем людям, работающим с Magento:




  • Никогда не вызывайте повторно load метод у объектов, полученных из коллекции

  • Загружайте только необходимые атрибуты

  • Если применимо к проекту, используйте Flat таблицы

  • Используйте count для подсчета результатов загруженной коллекции и getSize для получения числа всех записей

  • Не используйте getFirstItem метод без setPage(1,1) или аналогичных методов



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

https://habrahabr.ru/post/282025/

Метки:   Комментарии (0)КомментироватьВ цитатник или сообщество
rss_rss_hh_new

[Из песочницы] От любви до ненависти — один шаг, или как я разлюбил ActiveRecord

Среда, 13 Апреля 2016 г. 11:30 (ссылка)

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



В админке выводилась информация из базы, по 20 записей на страницу + подтягивались связи. На это уходило 50 (!!!) секунд. Грех было не посмотреть, что происходит с базой. Я не верил, что при 50к записях, порядка 6-7 джойнов для фильтрации, и затем 6-7 запросов eager loading могут быть такие тормоза.



Так и было — на все запросы уходило порядка 0.18с, что вполне приемлимо.



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



class OrderController
{
public function index(Request $request, OrderFilter $filter)
{
// применяем фильтры
$query = $filter->applyFilters($request);

// Отдаем постранично
return $query->paginate($request->input('count', 20));
}
}


Dispatcher начинает преобразовывать результат работы контроллера в зависимости от того, что хочет клиент. Разумеется, он видел заголовок Accept: application/json, и начинал свою грязную работу. И тут начиналась жара.



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



Тот самый зловещий код
/**
* Convert the model's attributes to an array.
*
* @return array
*/
public function attributesToArray()
{
$attributes = $this->getArrayableAttributes();
// If an attribute is a date, we will cast it to a string after converting it
// to a DateTime / Carbon instance. This is so we will get some consistent
// formatting while accessing attributes vs. arraying / JSONing a model.
foreach ($this->getDates() as $key) {
if (! isset($attributes[$key])) {
continue;
}
$attributes[$key] = $this->serializeDate(
$this->asDateTime($attributes[$key])
);
}
$mutatedAttributes = $this->getMutatedAttributes();
// We want to spin through all the mutated attributes for this model and call
// the mutator for the attribute. We cache off every mutated attributes so
// we don't have to constantly check on attributes that actually change.
foreach ($mutatedAttributes as $key) {
if (! array_key_exists($key, $attributes)) {
continue;
}
$attributes[$key] = $this->mutateAttributeForArray(
$key, $attributes[$key]
);
}
// Next we will handle any casts that have been setup for this model and cast
// the values to their appropriate type. If the attribute has a mutator we
// will not perform the cast on those attributes to avoid any confusion.
foreach ($this->getCasts() as $key => $value) {
if (! array_key_exists($key, $attributes) ||
in_array($key, $mutatedAttributes)) {
continue;
}
$attributes[$key] = $this->castAttribute(
$key, $attributes[$key]
);
if ($attributes[$key] && ($value === 'date' || $value === 'datetime')) {
$attributes[$key] = $this->serializeDate($attributes[$key]);
}
}
// Here we will grab all of the appended, calculated attributes to this model
// as these attributes are not really in the attributes array, but are run
// when we need to array or JSON the model for convenience to the coder.
foreach ($this->getArrayableAppends() as $key) {
$attributes[$key] = $this->mutateAttributeForArray($key, null);
}
return $attributes;
}




Ну конечно, мутаторы — это очень удобная штука. Классно и красиво можно получать доступ к различным данным в моделях/связях, но на странице документации разработчики поленились написать, что их использование оказывает существенное (так и хочется написать huge impact) влияние на производительность.



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



Чтобы не ломать ничего, пришлось звать на помощь Redis, в котором теперь лежать все данные, и регулярно обновляются после обновления моделей. Но не тут то было! Объем данных настолько велик, что Redis падал (грех на мне, возможно надо было его подтюнить). Пришлось пропускать данные через gzcompress, ибо стандартные 64мб никуда не годятся. И даже отдельный инстанс завел, чтобы была уверенность, что Redis больше не упадет под нагрузкой.



Теперь все работает, все хорошо. Данные отдаются меньше, чем за 0.5с, все счастливы. Но я сижу и думаю «подкупает Laravel скоростью и простотой, но следующий проект однозначно будет без этих ваших ActiveRecord».



Вот и сказке конец, а кто слушал — молодец, боттлнеков избежит.

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

https://habrahabr.ru/post/281493/

Метки:   Комментарии (0)КомментироватьВ цитатник или сообщество
stertenorva

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

Пятница, 02 Апреля 2016 г. 00:43 (ссылка)

Корейские ученые Pohang University of Science and Technology (POSTECH) разработали миниатюрный высокопроизводительный топливный элемент, который увеличит время работы беспилотных летательных аппаратов. В будущем новинка может стать достойной альтернативой литий-ионным аккумуляторам

Читать далее...
Метки:   Комментарии (0)КомментироватьВ цитатник или сообщество
rss_rss_hh_new

[Перевод] Монолитные репозитории в Git

Понедельник, 28 Марта 2016 г. 17:41 (ссылка)

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



Скала Улуру

Скала Улуру в Австралии как пример монолита — КДПВ, не более



Что такое монорепозиторий?



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


  • Репозиторий содержит более одного логического проекта (например, iOS-клиент и веб-приложение)

  • Эти проекты могут быть не связаны, слабо связаны или связаны сторонними средствами (например, через систему управления зависимостями)

  • Репозиторий большой во многих смыслах:


    • По количеству коммитов

    • По количеству веток и/или тегов

    • По количеству файлов

    • По размеру содержимого (то есть размеру папки .git)




В каких случаях монорепозитории удобны?



Мне видится пара возможных сценариев:


  • Репозиторий содержит набор сервисов, фреймворков и библиотек, составляющих единое логическое приложение. Например, множество микросервисов и совместно используемых библиотек, которые все вместе обеспечивают работу приложения foo.example.com.

  • Семантическое версионирование артефактов не требуется или не приносит особой пользы из-за того, что репозиторий используется в самых разных окружениях (например, staging и production). Если нет необходимости отправлять артефакты пользователям, то исторические версии, вроде 1.10.34, могут стать ненужными.



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

У Facebook есть пример такого монорепозитория:

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

чем даже ядро Linux, в котором, по состоянию на 2013 год, находилось 17 миллионов строк кода в 44 тысячах файлов.


При проведении тестов производительности в Facebook использовали тестовый репозиторий со следующими параметрами:


  • 4 миллиона коммитов

  • Линейная история

  • Около 1.3 миллиона файлов

  • Размер папки .git около 15 Гб

  • Файл индекса размером до 191 Мб



Концептуальные проблемы



С хранением несвязанных проектов в монорепозитории Git возникает много концептуальных проблем.



Во-первых, Git учитывает состояние всего дерева в каждом сделанном коммите. Это нормально для одного или нескольких связанных проектов, но становится неуклюжим для репозитория со многими несвязанными проектами. Проще говоря, на поддерево, существенное для разработчика, влияют коммиты в несвязанных частях дерева. Эта проблема ярко проявляется с большим числом коммитов в истории дерева. Поскольку верхушка ветки всё время меняется, для отправки изменений требуется частый merge или rebase.



Тег в Git — это именованный указатель на определённый коммит, который, в свою очередь, ссылается на целое дерево. Однако польза тегов уменьшается в контексте монорепозитория. Посудите сами: если вы работаете над веб-приложением, которое постоянно развёртывается из монорепозитория (Continuous Deployment), какое отношение релизный тег будет иметь к версионированному клиенту под iOS?



Проблемы с производительностью



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



Количество коммитов



Хранение несвязанных проектов в едином большом репозитории может оказаться хлопотным на уровне коммитов. С течением времени такая стратегия может привести к большому числу коммитов и значительному темпу роста (из описания Facebook — "тысячи коммитов в неделю"). Это становится особенно накладно, поскольку Git использует направленный ациклический граф (directed acyclic grap — DAG) для хранения истории проекта. При большом числе коммитов любая команда, обходящая граф, становится медленнее с ростом истории.



Примерами таких команд являются git log (изучение истории репозитория) и git blame (аннотирование изменений файла). При выполнении последней команды Git придётся обойти кучу коммитов, не имеющих отношение к исследуемому файлу, чтобы вычислить информацию о его изменениях. Кроме того, усложняется разрешение любых вопросов достижимости: например, достижим ли коммит A из коммита B. Добавьте сюда множество несвязанных модулей, находящихся в репозитории, и проблемы производительности усугубятся.



Количество указателей (refs)



Большое число указателей — веток и тегов — в вашем монорепозитории влияют на производительность несколькми путями.

Анонсирование указателей содержит каждый указатель вашего монорепозитория. Поскольку анонсирование указателей — это первая фаза любой удалённой Git операции, под удар попадают такие команды как git clone, git fetch или git push. При большом количестве указателей их производительность будет проседать. Увидеть анонсирование указателей можно с помощью команды git ls-remote, передав ей в качестве аргумента URL репозитория. Например, такая команда выведет список всех указателей в репозитории ядра Linux:

git ls-remote git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git


Если указатели хранятся не в сжатом виде, перечисление веток будет работать медленно. После выполнения команды git gc указатели будут упакованы в единый файл, и тогда перечисление даже 20.000 указателей станет быстрым (около 0.06 секунды).



Любая операция, которая требует обхода истории коммитов репозитория и учитывает каждый указатель (например, git branch --contains SHA1) в монорепозитории будет работать медленно. К примеру, при 21.708 указателях поиск указателя, содержащего старый коммит (который достижим из почти всех указателей), занял на моём компьютере 146.44 секунды (время может отличаться в зависимости от настроек кеширования и параметров носителя информации, на котором хранится репозиторий).



Количество учитываемых файлов



Индекс (.git/index) учитывает каждый файл в вашем репозитории. Git использует индекс для определения, изменился ли файл, выполняя stat(1) для каждого файла и сравнивая информацию об изменении файла с информацией, содержащейся в индексе.



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


  • git status может работать медленно, т.к. эта команда проверяет каджый файл, а индекс-файл будет большим

  • git commit также может работать медленно, поскольку проверяет каждый файл



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



Большие файлы



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



Комбинированные эффекты



Количество и размер файлов в сочетании с частотой их изменений наносят ещё больший удар по производительности:


  • Переключение между ветками или тегами, которое актуально в контексте поддерева (например, поддерево, с которым я работаю), по-прежнему обновляет дерево целиком. Этот процесс может быть медленным из-за большого числа затрагиваемых файлов, однако существует обходное решение. К примеру, следующая команда обновит папку ./templates так, чтобы она соответстовала указанной ветке, но при этом не изменит HEAD, что приведёт к побочному эффекту: обновлённые файлы будут отмечены в индексе как изменённые:

    `git checkout ref-28642-31335 -- templates`

  • Клонирование и загрузка (fetching) замедляются и становятся ресурсоёмкими для сервера, поскольку вся информация упаковывается в pack-файл перед отправкой.

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

  • Любая команда, включающая создание pack-файла, например git upload-pack, git gc, требует значительных ресурсов.



Что насчёт Bitbucket?



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




































Параметр Влияние на сервер Влияние на пользователя
Большие репозитории (много файлов, большие файлы или и то, и другое) Память, CPU, IO, git clone нагружает на сеть, git gc медленный и ресурсоёмкий Клонирование занимает значительное время, — как у разработчиков, так и на CI
Большое количество коммитов git log и git blame работают медленно
Большое количество указателей Просмотр списка веток, анонсирование указателей занимают значительное время (git fetch, git clone, git push работают медленно) Страдает доступность
Большое количество файлов Коммиты на стороне сервера становятся долгими git status и git commit работают медленно
Большие файлы См. "Большие репозитории" git add для больших файлов, git push и git gc работают медленно


Стратегии смягчения последствий



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



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



Удалите указатели



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



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



Обращение с большим количеством файлов



Если в вашем репозитории много файлов (их число достигает десятков и сотен тысяч штук), поможет быстрый локальный диск и достаточный объём памяти, которая может быть использована для кеширования. Эта область потребует более значительных изменений на клиентской стороне, подобных тем, которые Facebook реализовал для Mercurial.



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



Используйте Git LFS (Large File Storage — хранилище для больших файлов)



Для проектов, которые содержат большие файлы, например, видео или графику, Git LFS является одним из способов уменьшения их влияния на размер и общую производительность репозитория. Вместо того, чтобы хранить большие объекты в самом репозитории, Git LFS под тем же имененм хранит маленький файл-указатель на этот объект. Сам объект хранится в специальном хранилище больших файлов. Git LFS встраивается в операции push, pull, checkout и fetch, чтобы прозрачно обеспечить передачу и подстановку этих объектов в рабочую копию. Это означает, что вы можете работать с большими файлами так же, как обычно, при этом не раздувая ваш репозиторий.



Bitbucket Server 4.3 полностью поддерживает Git LFS v1.0+, а кроме того, позволяет просматривать и сравнивать большие графические файлы, хранящиеся в LFS.



Git LFS в Bitbucket Server



Мой коллега Стив Стритинг активно участвует в разработке проекта LFS и не так давно написал о нём статью.



Определите границы и разделите ваш репозиторий



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



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




Штефан Заазен — архитектор Atlassian Bitbucket. Страсть к DVCS привела его к миграции команды Confluence с Subversion на Git и, в конечном итоге, к главной роли в разработке того, что сейчас известно под названием Bitbucket Server. Штефана можно найти в Twitter под псевдонимом @stefansaasen.



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

https://habrahabr.ru/post/280358/

Метки:   Комментарии (0)КомментироватьВ цитатник или сообщество
anob

Саботаж или манипуляция?

Пятница, 18 Марта 2016 г. 04:47 (ссылка)
infopolk.ru/1/U/articles/72...5c6ab90332

Саботаж или манипуляция?


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

Комментарии (0)КомментироватьВ цитатник или сообщество
SoftLabirint

100 способов ускорить работу компьютера, о которых должен знать каждый / Д. Макарский (2016) PDF » SoftLabirint.Ru: Скачать бесплатно и без регистрации - Самые Популярные Новости Интернета

Четверг, 11 Марта 2016 г. 03:22 (ссылка)
softlabirint.ru/book/23042-...6-pdf.html


100 способов ускорить работу компьютера, о которых должен знать каждый / Д. Макарский (2016) PDF

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



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



Название: 100 способов ускорить работу компьютера, о которых должен знать каждый

Автор: Дмитрий Макарский

Издательство: Эксмо

Год: 2016

Язык: русский

Формат: PDF

Страниц: 224

Размер: 10,59 Мб



Скачать: 100 способов ускорить работу компьютера, о которых должен знать каждый / Д. Макарский (2016) PDF



Скачать | Download | TurboBit.net

http://turbobit.net/81qz6yedhtks/100_sposobov_uskorit_computer.rar.html



Скачать | Download | HitFile.net

http://www.hitfile.net/iH37eJh/100_sposobov_uskorit_computer.rar.html



Скачать | Download | Файлообменник.рф

http://файлообменник.рф/yssmbeaqebl1/100_sposobov_uskorit_computer.rar.html



Скачать | Download | BornCash.org

http://borncash.org/load/1734001089&name=100_sposobov_uskorit_computer.rar



Скачать | Download | StartFiles.org

http://startfiles.org/load/1734001089&name=100_sposobov_uskorit_computer.rar



Скачать | Download | GoldFiles.org

http://goldfiles.org/load/1734001089&name=100_sposobov_uskorit_computer.rar



Скачать | Download | File-Space.org

http://file-space.org/files/get/GFrBhN1IlJ/100-sposobov-uskorit-computer.rar.html



 



Подписка на новости сайта…

http://feeds.feedburner.com/Soft-Labirint

http://feeds.feedburner.com/Soft-Labirint?format=xml

https://feedburner.google.com/fb/a/mailverify?uri=Soft-Labirint

Метки:   Комментарии (0)КомментироватьВ цитатник или сообщество
Максимыч-

Настройки для максимальной производительности Windows 10

Воскресенье, 06 Марта 2016 г. 16:56 (ссылка)




Настройки для максимальной производительности Windows 10




Рамка Makсимыча

Метки:   Комментарии (4)КомментироватьВ цитатник или сообщество
rss_rss_hh_new

Маршрутизаторы AudioCodes

Суббота, 06 Марта 2016 г. 03:01 (ссылка)

Добрый день.



Сегодня новый обзор – маршрутизаторы AudioCodes. Да, Вы не ошиблись и прочитали правильно, именно маршрутизаторы. Жизнь не стоит на месте, и AudioCodes уже давно, будучи пионером VoIP и всемирно известным брендом голосовых шлюзов любых масштабов, от одного порта FXS до нескольких STM-1, превратился в производителя сложных и комплексных решений не только для операторского, но и для корпоративного рынка. Сейчас в профиле продуктов компании больше решений, не относящихся к категории «голосовой шлюз», чем традиционных шлюзов.



Что такое бизнес-маршрутизатор АудиоКодес?

После визуального осмотра интерфейсов многие скажут, что это типичный и многим знакомый шлюз Mediant 500, Mediant 800 или Mediant 1000. Однако давайте присмотримся подробнее ко всем сериям устройств.





Не у всех моделей из этих серий есть аналоговые или цифровые TDM интерфейсы. Значит, уже не шлюз. Хотя такое утверждение далеко не означает, что на устройствах без TDM интерфейсов нет поддержки VoIP. Но обо всем по порядку. Маршрутизаторы AudioCodes – это Mediant 500/500L MSBR, Mediant 800 MSBR и Mediant 1000 MSBR. Каждое название – это серия устройств с различными конфигурациями, которые отличаются наличием и количеством конкретных WAN/LAN интерфесов и/или TDM интерфейсов.



К названию обычных шлюзов добавляется аббревиатура MSBR (Multi Service Business Router), что говорит о первичном предназначении этого типа устройств именно как о маршрутизаторе. С точки зрения программного обеспечения (прошивки), Mediant и Mediant MSBR отличаются. Однако, заменой прошивки Mediant MSBR может превратиться и в обычный голосовой шлюз или SBC, o котором мы писали ранее тут и тут.



Будучи маршрутизатором, модели с TDM портами одновременно дополнительно получают функционал голосового шлюза (от нескольких FXS до 1-2 потоков Е1). Модели с LAN портами (до 12 на Mediant 800 MSBR) одновременно являются LAN коммутатором, а при использовании лицензии SBC, устройство получает дополнительно и функционал SBC. Есть модели со встроенной точкой доступа WiFi. Этого мало, существуют модели со встроенными модулями OSN, представляющими собой Intel-сервер. И эту возможность можно использовать для установки любых операционных систем и сторонних приложений или IP-PBX, реализуя на устройстве комплесные коробочные (одно устройство) решения «все в одном».



Рассмотрим пример. В типичном случае при подключении офиса к провадеру или разных офисов между собой приходится использовать несколько устройств. Каждое из таких устройств выполняет свою функцию (каналообразующее устройство, маршрутизатор, Firewall, коммутатор, SBC, шлюз, QoS/QoE агент и т.д.). В случае использования маршрутизаторов AudioCodes необходимость в использовании нескольких устройств отпадает. Одно наше устройство одновременно может выполнять несколько или все из следующих ролей:


  • Каналообразующее оборудование — различные типы WAN интерфейсов (оптика, медь, SHDSL 2/4 проводный, E1). Можно использовать до 3х WAN интерфейсов одновременно

  • Маршрутизатор с поддержкой статической и динамической маршрутизации (RIP1, RIP2, OSPFv2, BGPv4)

  • Коммутатор с GE/FE

  • Firewall

  • Голосовой шлюз

  • SIP сервер или IP-PBX

  • Пограничный контроллер сессий (SBC)

  • QoS/QoE агент для контроля качества передачи голоса и данных

  • Методы обеспечения отказоустройчивости для различных голосовых решений (SAS, CRP, SBC). В зависимости от применяемого решения и схемы включения есть возможность строить отказоустойчивые схемы по-разному (IP-PBX, виртуальная АТС, TDM backup).



Схематически озвученный подход выглядит так:







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







Все вышесказанное порождает множество вопросов о стабильности и производительности такой схемы включения. Мультисервисность накладывает массу ограничений на производительность. Насколько живучая такая схема применения? Как поведет себя устройство, будучи нагруженным несколькими функциями? Разные типы трафика усугубляют ситуацию: HTTP трафик характеризуется небольшим количеством больших пакетов, голосовой трафик – большим количеством относительно небольших пакетов. Какими данными оперировать при оценке производительности? Использовать ли шифрование (шифрование + большое количество пакетов приводит к загрузке CPU до 50% и выше)? Выполнять ли транскодинг (как известно, производительность от этого тоже страдает)? А что если включить NAT или QoS (сами по себе эти функции могут снизить пакетные скорости более чем на 80%)?



AudioCodes имеет ответы на такие вопросы. Архитектура Mediant содержит несколько вычислительных ресурсов. Мы используем разные CPU для обработки голоса и данных и собственные DSP. Это означает наличие отдельных ресурсов для шифрования, транскодинга, обработки NAT и QoS. В итоге это дает стабильную производительность. Обработка данных и голоса не влияет друг на друга. А стабильная производительность – ВАЖНОЕ преимущество. И суммарные цифры при оценке производительности в разрезе вышесказанного – это не только bits per second, но и packets per second (PPS).



Ниже представлены данные тестирования производительности в PPS для некоторых других производителей по сравнению с Mediant 800 для режимов работы с NAT и QoS.







Еще один важный вопрос во время разговора о маршрутизаторах – это собственно его возможности по маршрутизации трафика и другим традиционным функциям маршрутизаторов. Не будем вдаваться в длительные обсуждения, для обзора ограничимся основной информацией, а за деталями направим читателя к описаниям на сайте (http://www.audiocodes.com/multi-service-business-router-msbr).



Data Routing

• PPP, MLPPP, PPPoE, PPPoA, L2TP, IPoE, IPoA

• ATM: Up to 8 PVCs

• OAM-F5 (send/receive): loopback, continuity check

• Shaping: UBR, VBR-NRT, VBR-RT, CBR

• DHCP Client, Relay, server

• VLAN, VRF

• Layer 3 routing and Layer 2 bridging, Jumbo frames

• Internal layer 2 switching

• Static and dynamic routing (RIP1, RIP2, OSPFv2, BGPv4), Policy-Based Routing

• Multicast routing: IGMPv2/3

• IPv6, IPv6/IPv4 Dual Stack, ICMPv6, DHCPv6, SLAAC



Data Security


  • IPSec, GRE, L2TP

  • ESP – Tunnel mode

  • Encryption (AES, DES, 3DES)

  • Authentication Header

  • IKE mode – IPsec VPN

  • IDS/IPS:

— Fragmented traffiс

— Malformed Request

— Ping of Death

— Properly formed request from unauthenticated source

— DDoS attack

— SYN flood


  • Stateful packet inspection firewall

  • DMZ

  • Port Triggering

  • Packet Filtering

  • Application Layer Gateway



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


  • Графический WEB интерфейс для конфигурирования и базового мониторинга – активные аварии и графики загрузки

  • Командная строка для технического персонала, скрипты для конфигурации и глубокого технического анализа с помощью Telnet/SSH/serial

  • Система управления EMS для контроля и управления любыми продуктами AudioCodes

  • Поддержка автоматических апдейтов – возможность автоматически менять конфигурационные файлы устройства и версии ПО с помощью HTTP/HTTPS

  • Поддержка TR-069



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





*В зависимости от конфигурации конкретного устройства из серии.



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







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



Удачных подключений!



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

https://habrahabr.ru/post/278677/

Комментарии (0)КомментироватьВ цитатник или сообщество
akry

На чём может расти экономика России

Четверг, 04 Марта 2016 г. 00:02 (ссылка)

Многие не понимают, почему Россия не делает сейчас промышленно-технологического скачка. Кажется: влей денег, сделай правильную госпрограмму, и вскоре заколосится транзисторами наша собственная Кремниевая долина. Но почему-то денег и сил тратится вроде бы много, а долина всё дальше. Почему? Должно быть, враги мешают? Отнюдь.

Читать далее...
Метки:   Комментарии (0)КомментироватьВ цитатник или сообщество
rss_rss_hh_new

[Перевод] Архитектура Stack Overflow

Среда, 02 Марта 2016 г. 17:54 (ссылка)

image



Чтобы понять, как все это работает, давайте начнем с показателей Stack Overflow. Итак, ниже приводится статистика за 12 ноября 2013 и 9 февраля 2016 года:



статистика

  • 209,420,973 (+61,336,090) HTTP-запросов к нашему балансировщику нагрузки;

  • 66,294,789 (+30,199,477) страниц было загружено;

  • 1,240,266,346,053 (+406,273,363,426) битов (1.24 TБ) отосланного HTTP-трафика;

  • 569,449,470,023 (+282,874,825,991) битов (569 ГБ) всего получено;

  • 3,084,303,599,266 (+1,958,311,041,954) битов (3.08 ТБ) всего отослано;

  • 504,816,843 (+170,244,740) SQL-запросов (только из HTTP-запросов);

  • 5,831,683,114 (+5,418,818,063) обращений к Redis;

  • 17,158,874 (not tracked in 2013) поисков в Elastic;

  • 3,661,134 (+57,716) запросов Tag Engine;

  • 607,073,066 (+48,848,481) мс (168 часов) выполнения SQL-запросов;

  • 10,396,073 (-88,950,843) мс (2.8 часов) затрачено на обращение к Redis;

  • 147,018,571 (+14,634,512) мс (40.8 часов) затрачено на запросы к Tag Engine;

  • 1,609,944,301 (-1,118,232,744) мс (447 часов) затрачено на обработку в ASP.Net;

  • 22.71 (-5.29) мс в среднем (19.12 мс в ASP.Net) на формирование каждой из 49,180,275 запрошенных страниц;

  • 11.80 (-53.2) мс в среднем (8.81 мс в ASP.Net) на формирование каждой из 6,370,076 домашних страниц.





Вы можете спросить, почему существенно сократилась продолжительность обработки в ASP.Net по сравнению с 2013 годом (когда было 757 часов) несмотря на прибавление 61 миллиона запросов в день. Это произошло как и из-за модернизации оборудования в начале 2015 года, так и из-за некоторого изменения параметров в самих приложениях. Пожалуйста, не забывайте, что производительность – это наша отличительная особенность. Если Вы хотите, чтобы я более подробно рассказал о характеристиках оборудования – без проблем. В следующем посте будут подробные спецификации железа всех серверов, которые обеспечивают работу сайта.



Итак, что изменилось за прошедшие 2 года? Кроме замены некоторых серверов и сетевого оборудования, не очень многое. Вот укрупненный список хардварной части, которая обеспечивает работу ресурса (выделены различия по сравнению с 2013 годом):




  • 4 Microsoft SQL Servers (новое железо для 2-х из них);

  • 11 Web-серверов IIS (новое оборудование);

  • 2 сервера Redis (новое оборудование);

  • 3 сервера Tag Engine (новое оборудование для 2-х из 3-х);

  • 3 сервера Elasticsearch (те же, старые);

  • 4 балансировщика нагрузки HAProxy (добавлено 2 для поддержки CloudFlare);

  • 2 брандмауэра Fortinet 800C (вместо Cisco 5525-X ASAs);

  • 2 маршрутизатора Cisco ASR-1001 (вместо маршрутизаторов Cisco 3945);

  • 2 маршрутизатора Cisco ASR-1001-x (новые!).



Что нам необходимо, чтобы запустить Stack Overflow? Этот процесс не сильно изменился с 2013 года, но из-за оптимизации и нового железа, нам необходим только один web-сервер. Мы этого не хотели, но несколько раз успешно проверили. Вношу ясность: я заявляю, что это работает. Я не утверждаю, что это (запуск SO на единственном web-сервере) — хорошая затея, хотя каждый раз выглядит весьма забавно.



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



Для того, чтобы Вы поняли, как сегодня выглядит наше оборудование, привожу несколько своих фото рэковой стойки А (в сравнении с ее «сестрой» стойкой B), сделанных во время нашего переоборудования в феврале 2015 года:



image



И, как не парадоксально, с той недели у меня в альбоме есть еще 255 фотографий (в сумме 256, и да — это не случайное число). Теперь, давайте рассмотрим оборудование. Вот логическая схема взаимодействия главных систем:



image



Основные правила



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




  • Все резервировано.

  • Все сервера и сетевые устройства связаны между собой, по крайней мере, 2 x 10 ГБит/с каналами.

  • Все сервера имеют 2 входа питания и 2 подвода питания от 2-х ИБП, подключенных к двум генераторам и двум сетевым линиям.

  • Все сервера между рэками A и B имеют резервированного партнера (redundant partner).

  • Все сервера и сервисы дважды резервированы через другой дата-центр (Колорадо), хотя здесь я буду больше говорить о Нью-Йорке.

  • Все резервировано.



В сети Интернет



Сначала Вы должны найти нас – это DNS. Процесс нахождения нас должен быть быстрым, поэтому мы поручаем это CloudFlare (в настоящее время), так как их серверы DNS ближе почти всех остальных DNS мира. Мы обновляем наши записи DNS через API, а они делают «хостинг» DNS. Но поскольку мы «тормозы» с глубоко укоренившимися проблемами доверия (к другим), мы также все еще имеем наши собственные DNS-сервера. Если произойдет апокалипсис (вероятно, вызванный GPL, Punyon или кэшированием), а люди все еще будут хотеть программировать, чтобы не думать о нем, мы переключимся на них.



После того, как Вы найдете наше «секретное убежище», пойдет HTTP-трафик через одного из наших четырех Интернет провайдеров (Level 3, Zayo, Cogent, и Lightower в Нью-Йорке), и через один из наших четырех локальных маршрутизаторов. Для достижения максимальной эффективности, мы вместе с нашими провайдерами используем BGP (довольно стандартный) для управления трафиком и обеспечения нескольких путей его передачи. Маршрутизаторы ASR-1001 и ASR-1001-X объединены в 2 пары, каждая из которых обслуживает 2 провайдера в режиме активный/активный. Таким образом, мы обеспечиваем резервирование. Хотя они подключены все к той же физической сети 10 Гбит/с, внешний трафик проходит по отдельным изолированным внешним VLAN, которые также подключены к балансировщикам нагрузки. После прохождения через маршрутизаторы, трафик направляется к балансировщикам нагрузки.



Я думаю, что самое время упомянуть, что между нашими двумя дата-центрами мы используем линию MPLS на 10 Гбит/с, но это напрямую не связано с обслуживанием сайта. Она служит для дублирования данных и их быстрого восстановления в случаях, когда нам нужна пакетная передача. «Но Ник, это не резервирование!» Да, технически Вы правы (абсолютно правы): это – единственное «пятно» на нашей репутации. Но постойте! Через наших провайдеров мы имеем еще две более отказоустойчивые линии OSPF (по стоимости MPLS — № 1, а это № 2 и 3). Каждое из упомянутых устройств быстрее подключается к соответствующему устройству в Колорадо, и при отказе они распределяют между собой сбалансированный трафик. Мы смогли заставить оба устройства соединяться с обоими устройствами 4-мя способами, но все они и так одинаково хороши.



Идем дальше.



Балансировщики нагрузки (HAProxy)



Балансировщики нагрузки работают на HAProxy 1.5.15 под CentOS 7, предпочтительной у нас разновидности Linux. HAProxy также ограничивает и трафик TLS (SSL). Для поддержки HTTP/2 мы скоро начнем внимательно изучать HAProxy 1.7.



В отличие от всех других серверов с двойным сетевым подключением по LACP 10 Гбит/с, каждый балансировщик нагрузки имеет по 2 пары каналов 10 Гбит/с: одну для внешней сети и одну для DMZ. Для более эффективного управляемого согласования SSL эти «коробки» имеют память 64 ГБ или больше. Когда мы можем кэшировать в памяти больше сессий TLS для повторного использования, тратится меньше времени на образование нового соединения с тем же самым клиентом. Это означает, что мы можем возобновлять сессии и быстрее, и с меньшими затратами. Учитывая, что RAM в переводе на доллары довольно дешевая, это – легкий выбор.



Сами балансировщики нагрузки – довольно простые устройства. Мы создаем иллюзию, что разные сайты «сидят» на различных IP (в основном по вопросам сертификации и управления DNS), и маршрутизируем на различные выходные буфера основываясь, главным образом, на заголовках хоста. Единственными «знаменитыми» вещами, которые мы делаем, является ограничение скорости и некоторые захваты заголовков (отсылаемых с нашего уровня веб-узлов) в сообщение системного журнала HAProxy. Поэтому мы можем делать запись метрик производительности для каждого запроса. Мы также расскажем об этом позднее.



Уровень веб-узлов (IIS 8.5, ASP.Net MVC 5.2.3 и .Net 4.6.1)



Балансировщики нагрузки регулируют трафик 9-ти серверов, которые мы называем «Primary» (01-09), и 2-х web-серверов «Dev/meta» (10-11, среда нашей площадки). Серверы Primary управляют такими вещами, как Stack Overflow, Careers и всеми сайтами Stack Exchange, кроме сайтов meta.stackoverflow.com и meta.stackexchange.com, которые размещены на 2-х последних серверах. Основное приложение Q&A само по себе многопользовательское. Это означает, что одно приложение обслуживает запросы для всех Q&A сайтов. Скажем по-другому – мы можем управлять всей сетью Q&A одним пулом приложений на одном сервере. Другие приложения, такие как Careers, API v2, Mobile API и т.д. размещены отдельно. Вот так выглядят основной и dev-уровни в IIS:







Вот так выглядит распределение Stack Overflow по уровню веб-узлов, напоминая Opserver (наша внутренняя dashboard):



image



… а вот так выглядят web-серверы с точки зрения нагрузки:



image



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



Сервисное звено (IIS, ASP.Net MVC 5.2.3, .Net 4.6.1 и HTTP.SYS)



В основе этих web-серверов лежит очень похожее «сервисное звено». Оно также работает на IIS 8.5 под Windows 2012R2 и отвечает за внутренние службы, поддерживая вычислительный уровень веб-узлов и другие внутренние системы. Здесь два крупных «игрока»: «Stack Server», который управляет Tag Engine и основан на http.sys (не под IIS), и Providence API (работает на IIS). Забавный факт: я должен установить подобие на каждом из этих 2 процессов, прописываясь на разные сокеты, потому что Stack Server просто «пробивает» кэши L2 и L3, когда с интервалом в 2 минуты приходят обновленные опросные списки.



Эти сервисные «коробки» выполняют ответственную работу по поднятию Tag Engine и внутренних API, где нам нужна избыточность, но не 9-ти кратная. Например, загрузка из базы данных (в настоящее время 2-х) всех постов и их тэгов, которые изменяются каждую n-ную минуту, не очень «дешевая». Мы не хотим загружать все это 9 раз на уровень веб-узлов; достаточно 3 раза и это обеспечивает нам достаточную «безопасность». Кроме того, на уровне оборудования мы конфигурируем эти «коробки» по-разному, чтобы они были более оптимизированы под различные характеристики вычислительной нагрузки Tag Engine и гибкого индексирования (которое также здесь работает). Сам по себе «Tag Engine» является относительно сложной темой и ему будет посвящен отдельный пост. Основной момент: когда Вы заходите на /questions/tagged/java, вы нагружаете Tag Engine, чтобы определить соответствующие запросы. Он делает все наше сопоставление тэгов вне процесса поиска, поэтому новая навигация и все прочее используют эти сервисы для обработки данных.



Кэш и Pub/Sub (Redis)



Здесь мы используем Redis для нескольких вещей и они вряд ли будут сильно изменяться. Несмотря на выполнение примерно 160 миллиардов операций в месяц, в каждый момент загрузка центрального процессора менее 2%. Обычно намного ниже:



image



С Redis мы имеем систему кэшей L1/L2. «L1» – это кэш HTTP на web-серверах или каком-либо работающем приложении. «L2» снижает скорость Redis и делает оценки. Наши оценки сохраняются в формате Protobuf через protobuf-dot-net от Марка Грэвелла (Marc Gravell). Для клиента мы используем StackExchange.Redis – собственно разработанный и открытый источник. Когда один web-сервер получает «промах кэша» (cache miss) L1, либо L2, он берет оценку из источника (запрос к базе данных, вызов API и т.д.) и помещает результат и в местный кэш, и в Redis. Следующий сервер, нуждающийся в оценке, может получить «промах кэша» L1, но найдет оценку в L2/Redis, экономя на запросе к базе данных или вызове API.



Также у нас работает много Q&A сайтов, поэтому у каждого сайта есть свое собственное кэширование L1/L2: по ключевому префиксу в L1 и по ID базы данных в L2/Redis. Более подробно этот вопрос мы рассмотрим в следующих постах.



Вместе с 2-мя основными серверами Redis (мастер/ведомый), которые управляют всеми запросами к сайтам, у нас также есть система машинного обучения, работающая на 2-х более специализированных серверах (из-за памяти). Она используется для отображения рекомендованных запросов на домашней странице, улучшения выдачи и т.д. Эта платформа, называемая Providence, у нас обслуживается Кевином Монтроузом (Kevin Montrose).



Основные серверы Redis имеют по 256 ГБ RAM (используется около 90 ГБ), а в серверах Providence установлено по 384 ГБ RAM (используется около 125 ГБ).



Redis используется не только для работы с кэшем – он также имеет алгоритм «publish & subscriber» (публикация и подписка), работающий так, что один сервер может разослать сообщение, а все другие «подписчики» получат его – включая нижерасположенных клиентов на ведомых серверах Redis. Мы используем этот алгоритм для очистки кэша L1 на других серверах, когда один web-сервер делает удаление для сохранения согласованности. Но есть и другое важное применение: websockets.



Websocket’ы (NetGain)



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



Сами сокет-серверы используют необработанные сокеты, выполняемые на уровне веб-узлов. Это — очень маленькое приложение на вершине нашей открытой библиотеки: StackExchange.NetGain. Во время пиковой нагрузки у нас одновременно открыто около 500,000 параллельных каналов websocket. Это — множество браузеров. Забавный факт: некоторые из этих страниц были открыты более 18 месяцев назад. Мы не знаем почему. Кто-то должен проверить, живы ли еще эти разработчики.



Вот так изменялось на этой неделе число одновременно открытых websocket:



image



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



Поиск (Elasticsearch)



Спойлер: это не то, от чего можно прийти в восторг.



Уровень веб-узлов выполняет солидную поисковую работу по сравнению с Elasticsearch 1.4, который использует очень тонкий высокоэффективный клиент StackExchange.Elastic. В отличие от большинства других средств, мы не планируем выкладывать его в открытый доступ просто потому, что он отражает только очень небольшое подмножество API, которые мы используем. Я убежден, что его выпуск «в свет» принесет больше вреда, чем пользы из-за путаницы среди разработчиков. Мы используем Elastic для поиска, вычисления связанных запросов и советов, как формировать вопрос.



Каждый кластер Elastic (по одному такому есть в каждом дата-центре) имеет 3 узла, а каждый сайт имеет свой собственный индекс. Careers имеет несколько дополнительных индексов. Это делает нашу систему немного нестандартной: наши 3 серверных кластера немного больше «накачаны» всеми этими хранилищами SSD, памятью в 192 ГБ и двойной сетью по 10 Гбит/с на каждый канал.



Те же самые прикладные домены (да, мы здесь тронуты на .Net Core …) в Stack Server, на котором установлен Tag Engine, также непрерывно индексируют элементы в Elasticsearch. Здесь мы применяем некоторые уловки, такие как ROWVERSION в SQL Server (источник данных), сравниваемый с документом «последней позиции» в Elastic. Так как он ведет себя как последовательность, мы можем просто использовать и индексировать любые элементы, которые изменились с момента последнего прохода.



Главной причиной того, что мы остановились на Elasticsearch вместо чего-то подобного полнотекстовому поиску SQL, является масштабируемость и лучшее распределение денег. SQL CPU достаточно дорогие, Elastic же дешевый и сегодня имеет намного лучшие характеристики. Почему не Solr? Мы хотим искать по всей сети (сразу много индексов), но это не поддерживалось Solr во время принятия решения. Причиной того, что мы еще не перешли на 2.x, является солидная доля «types», из-за которых нам надо будет все переиндексировать для обновления. У меня просто нет достаточно времени, чтобы когда-нибудь составить план и сделать необходимые для перехода изменения.



Базы данных (SQL-сервер)



Мы используем SQL Server как наш единственный источник достоверной информации. Все данные Elastic и Redis получают от SQL-сервера. У нас работают 2 кластера SQL-серверов под AlwaysOn Availability Groups. У каждого из этих кластеров есть один мастер (выполняющий почти всю нагрузку) и одна реплика в Нью-Йорке. Кроме этого, еще есть одна реплика в Колорадо (наш дата-центр с динамическим копированием). Все реплики асинхронные.



Первый кластер – набор серверов Dell R720xd, у каждого 384 ГБ RAM, 4 TB PCIe SSD и 2 x 12 ядер. На нем установлены Stack Overflow, Sites (имеет дурную славу, я объясню позже), PRIZM и базы данных Mobile.



Второй кластер – это набор серверов Dell R730xd, у каждого 768 ГБ RAM, 6 TB PCIe SSD и 2 x 8 ядер. На этом кластере стоит все остальное. Список «всего остального» включает Careers, Open ID, Chat, наш Exception log и некоторые Q&A сайты (например, Super User, Server Fault и т.д.).



Использование CPU на уровне баз данных – это то, чего мы предпочитаем иметь в минимальном количестве, но, фактически, в данный момент оно немного повысилось из-за некоторых проблем с кэшем, которые мы сейчас решаем. Сейчас NY-SQL02 и 04 назначены мастерами, 01 и 03 – репликами, которые мы сегодня перезагрузили после обновления SSD. Вот как выглядят прошедшие 24 часа:



image



Мы используем SQL довольно просто. Ведь просто – это быстро. Хотя некоторые запросы могут быть безумными, наше взаимодействие с SQL само по себе – просто классика. У нас есть одна «древняя» Linq2Sql, но все новые разработки используют Dapper – наш выложенный в открытый доступ Micro-ORM, использующий POCOs. Позвольте сказать по-другому: Stack Overflow имеет только 1 хранимую в базе данных процедуру, и я намереваюсь перевести тот последний кусок в код.



Библиотеки



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




  • Dapper (.Net Core) — высокопроизводительный Micro-ORM для ADO.Net;

  • StackExchange.Redis – высокопроизводительный клиент Redis;

  • MiniProfiler – малообъемный профайлер, который мы используем на каждой странице (поддерживает только Ruby, Go и Node);

  • Exceptional – регистратор ошибок для SQL, JSON, MySQL, и т.п;

  • Jil — высокопроизводительный (де) сериализатор JSON ;

  • Sigil – помощник генерации .Net CIL (когда C# не достаточно быстрый);

  • NetGain — высокопроизводительный сервер websocket ;

  • Opserver — контрольная приборная панель, опрашивающая напрямую большинство систем и также получающая данные от Orion, Bosun или WMI;

  • Bosun – система мониторинга серверных СУБД, написанная на Go.



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

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

https://habrahabr.ru/post/278391/

Комментарии (0)КомментироватьВ цитатник или сообщество
lenov_ru

Top Farm » Русский Google Play - игры Android без вирусов и регистрации

Пятница, 12 Февраля 2016 г. 08:50 (ссылка)
mod-hak.ru/game/simulators/...-farm.html


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

Комментарии (0)КомментироватьВ цитатник или сообщество
lenov_ru

GameTime Football 2 v 1.0.2 Мод (много денег) » Клуб пользователей планшетов на ANDROID / Lenovo IdeaTab A2109 8GB / Samsung Galaxy Tab 2 7.0 / Asus Transformer TF700T / NVIDIA Tegra 3

Воскресенье, 31 Января 2016 г. 09:58 (ссылка)
lenov.ru/games/sports/24183...deneg.html


GameTime Football 2 - Тут мы должны команду собрать свою и обучить игроков своих всем приемам игры. Создавай команду свою при помощи приобретений свободных агентств и своих обучай игроков су

Комментарии (0)КомментироватьВ цитатник или сообщество

Следующие 30  »

<производительность - Самое интересное в блогах

Страницы: [1] 2 3 ..
.. 10

LiveInternet.Ru Ссылки: на главную|почта|знакомства|одноклассники|фото|открытки|тесты|чат
О проекте: помощь|контакты|разместить рекламу|версия для pda