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


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

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

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

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

Четверг, 07 Июля 2016 г. 14:39 (ссылка)
netfact.ru/computer/3019-10...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

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

[Перевод] Язык Go, микросервисы и DevOps – хорошая компания?

Пятница, 24 Июня 2016 г. 15:18 (ссылка)

Привет, Хабр!



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







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



Приятного чтения!







Сейчас все говорят о микросервисах и DevOps. Поставьте эти словечки себе в профиль – и вас сразу начнут осаждать рекрутеры. Я побывал в Мюнхене на нескольких интересных митапах по микросервисам, и меня наиболее удивило, что эта тема пользуется наибольшим интересом в сообществах Java и Scala. Удивило потому, что Java и Scala – очень насыщенные языки, в которых есть из чего выбирать.



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



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



Виртуальная машина Java оптимизирована для работы с долгоиграющими приложениями, в ней действует одна из наиболее выверенных и затейливых систем сборки мусора. Используется в боевых условиях уже более 10 лет. Тем не менее, когда мне доводится видеть современные высокодоступные архитектуры – сразу напрашивается вопрос: а нужны ли долгоиграющие приложения для реализации абсолютного большинства существующих сервисов?



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



За много лет в Java сложилось много нетривиальных решений – например, фреймворк log4j для логирования. На примере контейнерных решений вроде OpenShift можно убедиться, что теперь снова принято работать с stdout и stderr. Нет необходимости внедрять изощренные решения для логирования на уровне языка. Этот пример позволяет судить, как DevOps и новые среды времени выполнения меняют правила игры.



Типичный docker-образ на Go docker имеет размер около 15 MB; сравните его с образом для JVM на Java, размер которого — около 300 MB. Разница 1 к 10. Java JVM оптимизирована под экономный расход памяти, но все равно требует примерно в 10 раз больше памяти, чем Go.



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



Java и Scala – это языки для объектно-ориентированного программирования. Но при работе в сравнительно простых предметных областях такие решения кажутся мне довольно затратными. «Гибкий» аспект философии Go позволяет организовать разработку не только не хуже, но и гораздо понятнее.



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



В 1990-е был настоящий бум серверов приложений Java – считалось, что они обеспечат независимость разработки от операционной системы и аппаратного обеспечения. Читая спецификацию JEE, мы также рассчитывали на простоту удаленных взаимодействий и компонент-ориентированную разработку. Когда я вижу контейнер docker, на котором работают Java-приложения, всегда вспоминаю о новой версии EJB. В принципе, стек Java не упростился, но теперь он упакован в контейнер. Такая упаковка даром не дается, поскольку добавляется еще один уровень сложности; вы с ним познакомитесь, как только попробуете отладить сеть такого docker-контейнера.



Go docker – вариант для масштабирования сервисов, но сложную среду времени исполнения он не спасает. Если у вас всего один простой сервис, то простые бинарные файлы Go можно выполнять прямо на хосте. Если же речь идет о более сложном приложении, то сервисы можно положить, например, в контейнер и запускать их в PaaS-среде вроде OpenShift. Чтобы протестировать сервис на ноутбуке разработчика, контейнер не нужен, всяческая связанная с ним магия – тоже.



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



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



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



Думаю, просто никто не любит резких изменений. Мы попробовали изменить всего одно измерение в многомерном мире программирования. Это измерение – размер сервиса. По моему опыту, изменить одно измерение еще недостаточно, чтобы пошла эволюция. Поскольку все измерения взаимосвязаны, они влияют друг на друга. Решив упростить приложение, переделав его в виде микросервисов, мы должны также упростить и исполняющую среду, и язык программирования, с которым работаем. Иначе получим лишь новую головную боль – например, придется управлять сразу множеством JVM, которые занимают кучу места в памяти и запускаются довольно медленно. Либо получим множество мелких объектно-ориентированных решений, которые будут распределенными, а значит – более сложными. Наконец, мы просто запутаемся со множеством сервисов, образующих огромное дерево зависимостей.



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



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




Актуальность книги






































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





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


Original source: habrahabr.ru.

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

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

Сравнение прозводительности D и Go для веб

Суббота, 19 Июня 2016 г. 02:29 (ссылка)

Доброго времени суток, хабр!



Так как мне скоро предстоит разрабатывать веб-приложение, а писать на интерпретирумых языках как-то нет желания, тем более, что есть такие ЯП как D и Go, возникло желание сравнить их производительность при работе с веб (в сети не нашёл тестов, которые были бы свежими). Для D это vibe.d, а для Go, как я понял, не используются фреймворки. Так как Go я знаю менее чем «никак» решил не выпендриваться: тестовые приложения просто отдают страничку с некоторым текстом (ни баз данных, ни сложного роутинга, ни изображений).



Нагрузка давалась с помощью Apache Benchmark.







Приложение на D это стандартное vibe приложение, нас будут интересовать только

source/app.d
import vibe.d;

shared static this()
{
auto settings = new HTTPServerSettings;
settings.options |= HTTPServerOption.distribute; // без этой настройки vibe однопоточен
settings.port = 8101;
settings.bindAddresses = ["::1", "127.0.0.1"];

listenHTTP(settings, &index);
}

void index(HTTPServerRequest req, HTTPServerResponse res)
{
auto var = "hello habr";
res.render!("index.dt",var);
}
и
views/index.dt
html
head
body
h1 Привет, Хабр!

p D is a systems programming language with C-like syntax.

= var
сборка: dub build --build=release



В Go приложении нас интересуют соответствующие файлы

site_test_go.go
package main

import (
"html/template"
"net/http"
)

type Page struct {
Var string
}

func indexViewer(w http.ResponseWriter, r *http.Request) {
p := Page{Var:"hello habr"}
t, _ := template.ParseFiles("index.html")
t.Execute(w, p)
}

func main() {
http.HandleFunc("/", indexViewer);
http.ListenAndServe(":8100", nil);
}


и
index.html

Привет, Хабр!



Go is an open source programming language that makes it easy to build simple, reliable, and efficient software.




{{.Var}}



сборка: go build site_test_go.go



Нагрузка: ab -c200 -n50000 http://localhost:8101/ (8100 для site_test_go)



Начнём


Apache Benchmark


Немного различный код index файлов был сделан для соответствия Document Length (видимо vibe передаёт что-то в заголовке ответа)










dlang golang
Server Software: vibe.d/0.7.28

Server Hostname: localhost

Server Port: 8101



Document Path: /

Document Length: 182 bytes



Concurrency Level: 200

Time taken for tests: 4.481 seconds

Complete requests: 50000

Failed requests: 0

Total transferred: 16450000 bytes

HTML transferred: 9100000 bytes

Requests per second: 11157.90 [#/sec] (mean)

Time per request: 17.925 [ms] (mean)

Time per request: 0.090 [ms] (mean, across all concurrent requests)

Transfer rate: 3584.91 [Kbytes/sec] received



Connection Times (ms)

min mean[+/-sd] median max

Connect: 0 8 33.2 7 1009

Processing: 2 10 3.7 9 207

Waiting: 1 8 3.5 7 205

Total: 3 18 33.4 17 1020



Percentage of the requests served within a certain time (ms)

50% 17

66% 18

75% 18

80% 18

90% 19

95% 23

98% 29

99% 30

100% 1020 (longest request)



Server Software:

Server Hostname: localhost

Server Port: 8100



Document Path: /

Document Length: 182 bytes



Concurrency Level: 200

Time taken for tests: 4.263 seconds

Complete requests: 50000

Failed requests: 0

Total transferred: 14950000 bytes

HTML transferred: 9100000 bytes

Requests per second: 11728.36 [#/sec] (mean)

Time per request: 17.053 [ms] (mean)

Time per request: 0.085 [ms] (mean, across all concurrent requests)

Transfer rate: 3424.59 [Kbytes/sec] received



Connection Times (ms)

min mean[+/-sd] median max

Connect: 0 8 14.9 7 1011

Processing: 2 9 2.3 9 211

Waiting: 1 7 2.4 7 210

Total: 10 17 15.1 16 1020



Percentage of the requests served within a certain time (ms)

50% 16

66% 17

75% 18

80% 18

90% 19

95% 20

98% 21

99% 22

100% 1020 (longest request)







Память


Замер производился с помощью gnome-system-monitor

















dlang golang
до первого запроса memory: 680.0KiB

virtual: 907.5MiB

resident: 10.2MiB

writable: 343.3MiB

shared: 9.5MiB

memory: 888.0KiB

virtual: 110.8MiB

resident: 5.2MiB

writable: 35.7MiB

shared: 4.4MiB

после запуска ab memory: 19.2MiB

virtual: 9.5GiB

resident: 28.7MiB

writable: 9.0GiB

shared: 9.6MiB

memory: 6.5MiB

virtual: 1.3GiB

resident: 12.5MiB

writable: 1.0GiB

shared: 5.9MiB





Загрузка процессора


Замер производился с помощью gnome-system-monitor










dlang golang




Версии ПО


apr-util-1.5.4-1.fc22.x86_64



DMD64 D Compiler v2.071.0

DUB version 0.9.25, built on May 22 2016

vibe 0.7.28



go version go1.5.4 linux/amd64



Выводы


Производительность инструментов практически не различается (чему я удивлён, на самом деле).

Огорчило потребление памяти у D: практически в 3 раза больше чем у Go.

Исходя из графиков загруженности процессора, можно сделать вывод: планировщик заданий в Go устроен лучше — сразу распределяет нагрузку на ядра поровну, но в среднем загрузка ЦП у D ниже.

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



PS: это мой первый эксперимент с производительностью веб (вообще пока не хорошо с веб знаком), так что буду очень рад, если вы укажите на ошибки в способе измерения и/или начальных условиях =)




Original source: habrahabr.ru.

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

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

Сравнение прозводительности D и Go для веб

Суббота, 19 Июня 2016 г. 02:29 (ссылка)

Доброго времени суток, хабр!



Так как мне скоро предстоит разрабатывать веб-приложение, а писать на интерпретирумых языках как-то нет желания, тем более, что есть такие ЯП как D и Go, возникло желание сравнить их производительность при работе с веб (в сети не нашёл тестов, которые были бы свежими). Для D это vibe.d, а для Go, как я понял, не используются фреймворки. Так как Go я знаю менее чем «никак» решил не выпендриваться: тестовые приложения просто отдают страничку с некоторым текстом (ни баз данных, ни сложного роутинга, ни изображений).



Нагрузка давалась с помощью Apache Benchmark.







Приложение на D это стандартное vibe приложение, нас будут интересовать только

source/app.d
import vibe.d;

shared static this()
{
auto settings = new HTTPServerSettings;
settings.options |= HTTPServerOption.distribute; // без этой настройки vibe однопоточен
settings.port = 8101;
settings.bindAddresses = ["::1", "127.0.0.1"];

listenHTTP(settings, &index);
}

void index(HTTPServerRequest req, HTTPServerResponse res)
{
auto var = "hello habr";
res.render!("index.dt",var);
}
и
views/index.dt
html
head
body
h1 Привет, Хабр!

p D is a systems programming language with C-like syntax.

= var
сборка: dub build --build=release



В Go приложении нас интересуют соответствующие файлы

site_test_go.go
package main

import (
"html/template"
"net/http"
)

type Page struct {
Var string
}

func indexViewer(w http.ResponseWriter, r *http.Request) {
p := Page{Var:"hello habr"}
t, _ := template.ParseFiles("index.html")
t.Execute(w, p)
}

func main() {
http.HandleFunc("/", indexViewer);
http.ListenAndServe(":8100", nil);
}


и
index.html

Привет, Хабр!



Go is an open source programming language that makes it easy to build simple, reliable, and efficient software.




{{.Var}}



сборка: go build site_test_go.go



Нагрузка: ab -c200 -n50000 http://localhost:8101/ (8100 для site_test_go)



Начнём


Apache Benchmark


Немного различный код index файлов был сделан для соответствия Document Length (видимо vibe передаёт что-то в заголовке ответа)










dlang golang
Server Software: vibe.d/0.7.28

Server Hostname: localhost

Server Port: 8101



Document Path: /

Document Length: 182 bytes



Concurrency Level: 200

Time taken for tests: 4.481 seconds

Complete requests: 50000

Failed requests: 0

Total transferred: 16450000 bytes

HTML transferred: 9100000 bytes

Requests per second: 11157.90 [#/sec] (mean)

Time per request: 17.925 [ms] (mean)

Time per request: 0.090 [ms] (mean, across all concurrent requests)

Transfer rate: 3584.91 [Kbytes/sec] received



Connection Times (ms)

min mean[+/-sd] median max

Connect: 0 8 33.2 7 1009

Processing: 2 10 3.7 9 207

Waiting: 1 8 3.5 7 205

Total: 3 18 33.4 17 1020



Percentage of the requests served within a certain time (ms)

50% 17

66% 18

75% 18

80% 18

90% 19

95% 23

98% 29

99% 30

100% 1020 (longest request)



Server Software:

Server Hostname: localhost

Server Port: 8100



Document Path: /

Document Length: 182 bytes



Concurrency Level: 200

Time taken for tests: 4.263 seconds

Complete requests: 50000

Failed requests: 0

Total transferred: 14950000 bytes

HTML transferred: 9100000 bytes

Requests per second: 11728.36 [#/sec] (mean)

Time per request: 17.053 [ms] (mean)

Time per request: 0.085 [ms] (mean, across all concurrent requests)

Transfer rate: 3424.59 [Kbytes/sec] received



Connection Times (ms)

min mean[+/-sd] median max

Connect: 0 8 14.9 7 1011

Processing: 2 9 2.3 9 211

Waiting: 1 7 2.4 7 210

Total: 10 17 15.1 16 1020



Percentage of the requests served within a certain time (ms)

50% 16

66% 17

75% 18

80% 18

90% 19

95% 20

98% 21

99% 22

100% 1020 (longest request)







Память


Замер производился с помощью gnome-system-monitor

















dlang golang
до первого запроса memory: 680.0KiB

virtual: 907.5MiB

resident: 10.2MiB

writable: 343.3MiB

shared: 9.5MiB

memory: 888.0KiB

virtual: 110.8MiB

resident: 5.2MiB

writable: 35.7MiB

shared: 4.4MiB

после запуска ab memory: 19.2MiB

virtual: 9.5GiB

resident: 28.7MiB

writable: 9.0GiB

shared: 9.6MiB

memory: 6.5MiB

virtual: 1.3GiB

resident: 12.5MiB

writable: 1.0GiB

shared: 5.9MiB





Загрузка процессора


Замер производился с помощью gnome-system-monitor










dlang golang




Версии ПО


apr-util-1.5.4-1.fc22.x86_64



DMD64 D Compiler v2.071.0

DUB version 0.9.25, built on May 22 2016

vibe 0.7.28



go version go1.5.4 linux/amd64



Выводы


Производительность инструментов практически не различается (чему я удивлён, на самом деле).

Огорчило потребление памяти у D: практически в 3 раза больше чем у Go.

Исходя из графиков загруженности процессора, можно сделать вывод: планировщик заданий в Go устроен лучше — сразу распределяет нагрузку на ядра поровну, но в среднем загрузка ЦП у D ниже.

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



PS: это мой первый эксперимент с производительностью веб (вообще пока не хорошо с веб знаком), так что буду очень рад, если вы укажите на ошибки в способе измерения и/или начальных условиях =)
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/303590/

Метки:   Комментарии (0)КомментироватьВ цитатник или сообщество
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)КомментироватьВ цитатник или сообщество

Следующие 30  »

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

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

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